📄 详细分析三.txt
字号:
经过前面的分析,我们已经理清楚了业务层,接下来的部分将是web层部分.首先我们从web.xml开始,我们知道任何一个java web应用系统都是从WEB-INF/web.xml启动的,根据servlet2.4规范filter执行是按照web.xml配置的filter-mapping先后顺序进行执行,这里用了UrlRewriteFilter和字符编码过滤器CharacterEncodingFilter(UTF-8),还有配置延迟加载时使用OpenSessionInView,可参考资料http://www.javaeye.com/topic/32001;另外,还有struts-clearup,以及Strut2的org.apache.struts2.dispatcher.FilterDispatcher,注意它有两个dipatcher参数, 在这种情况下,如果请求是以/*开头的,并且是通过request dispatcher的forward方法传递过来或者直接从客户端传递过来的,则必须经过这个过滤器, Servlet 2.3 版新增了Filter的功能,不过它只能由客户端发出请求来调用Filter,但若使用 RequestDispatcher.forward( )或RequestDispatcher.include( )的方法调用Filter 时,Filter 却不会执行.这个功能应该都是主要用于UrlRewrite用的吧.而<context-param>元素定义了一些应用级的参数,如:urlrewrite,cluster都为false,servletmapping为*.bbscs,poststoragemode为1;接下来是listener有两个,一是com.loaer.bbscs.web.servlet.SysListener就是对上面的变量进行操作的一个监听器,而另一个则是spring的ContextLoaderListener,这里我们不讨论,接下来是几个servlet,提供一些常用的功能:authimg生成验证码,rss.另外还有welcome-file及struts-tag.tld的taglib和一些error-page,如:error-code:401--->location:-->/401.htm.对于具体的加载过程可见启动resin3等服务器后的控制台显示,我个人觉得是加载了应用级参数,再是2个Linstener..,到spring-->加载所有bean到时空器-->各个bean的加载(包括hibernate的配置等)--->ContextLoader启动完成-->接下来,对Filter按相反的顺序加载(struts->struts-clean-->characterEncoding)先是struts2的配置文件的加载,还有global messages...
注意这段时间内也会启动一些TimerTask任务...,这点可从日志中看到,最后是ObjectTypeDeterminerFactory应该是用于struts-clean吧.还有OpenSessionInViewFilter,CharacterEncodingFilter,UrlRewriteFilter...
这样,应用和resin服务才开始发挥作用,才能访问!
好的,我们先看在com.laoer.bbscs.web.servlet包中的几个源文件:
SysListener:它继承自HttpServlet,实现了ServletContextLinster接口.
String rootpath = sce.getServletContext().getRealPath("/");
if (rootpath != null) {
rootpath = rootpath.replaceAll("\\\\", "/");
} else {
rootpath = "/";
}
if (!rootpath.endsWith("/")) {
rootpath = rootpath + "/";
}
Constant.ROOTPATH = rootpath; 记录在常量java文件中.public static String ROOTPATH = "";
我们看一个代表性的示例源码:
String poststoragemodes = sce.getServletContext().getInitParameter("poststoragemode");
if (poststoragemodes == null) {
poststoragemodes = "0";
}
Constant.POST_STORAGE_MODE = NumberUtils.toInt(poststoragemodes, 0);//文章存储格式(这里是文件)
-->
<context-param>
<param-name>poststoragemode</param-name>
<param-value>1</param-value>
</context-param>
接下来,我们分析AuthImg,主要用它的doGet方法:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("image/jpeg");
ServletOutputStream out = response.getOutputStream();
int width = 60, height = 20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//这段代码创建了一个 BufferedImage 对象,它代表一个 60像素宽、20像素高的图像。为了应用这个图像,我们需要有图形上下文,而 BufferedImage 对象的 createGraphics() 方法就返回一个与该图像相关的 Graphics2D 对象:
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height); //背景色
g.setFont(mFont); //字体private Font mFont = new Font("Times New Roman", Font.PLAIN, 18);
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);//画线155条
}
String rand = RandomStringUtils.randomNumeric(4);//产生四个随机数
char c;
for (int i = 0; i < 4; i++) {
c = rand.charAt(i);
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); //各个数的着色不一样
g.drawString(String.valueOf(c), 13 * i + 6, 16);
}
WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());//
webapplicationcontextutils.getwebapplicationcontext(servletcontext); 如果没有直接返回null
SysConfig sysConfig = (SysConfig) wc.getBean("sysConfig");//web层得到sysConfig
UserCookie uc = new UserCookie(request, response, sysConfig);//写入Cookie中
uc.addAuthCode(rand);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);//也可以用ImageIO.write(bi,"jpg",out);
encoder.encode(image);
out.close();
}
接下来,看RSS:它也是用在doGet,有两种,一种是首页,不带bid参数,一种是带bid.用于各个版区的...
long bid;
try {
bid = Long.parseLong(request.getParameter("bid"));
} catch (NumberFormatException e) {
bid = 0L;
}
首先为了使用服务,这里用了spring的WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());这样我们就可以得到ForumService和SysConfig,BoardService..这里用了下SyndFeed feed=new SynFeedImpl();
com.sun.syndication这个包,接着便是feed.setFeedType("rss_2.0");先设置好feed源的标题/链接(有两种,分有bid和没bid)/描述,注意这些参数大多来自于sysConfig,接下来,我们设置entry(条目)及其description
List<SyndEntry> entries = new ArrayList<SyndEntry>();
SyndEntry entry;
SyndContent description;
我们看后半部分的代码:
Board board = boardService.getBoardByID(bid);//得到board
if (board != null) {
if (board.getBoardType() == 2 || board.getBoardType() == 3) {
String forumLink = "";
try {
forumLink = BBSCSUtil.absoluteActionURL(request, "/forum.bbscs?action=index&bid=" + bid)
.toString();//RSS源链接
} catch (MalformedURLException ex2) {
forumLink = "";
}
feed.setTitle(sysConfig.getForumName() + " - " + board.getBoardName());
feed.setLink(forumLink);
feed.setDescription(sysConfig.getWebName() + " - " + sysConfig.getForumName() + " - "
+ board.getBoardName());
List<SyndEntry> entries = new ArrayList<SyndEntry>();//所有条目
SyndEntry entry;
SyndContent description;
Pages pages = new Pages();//构造一个Pages对象
pages.setPage(1);
pages.setPerPageNum(40);
pages.setFileName("");
PageList pl = forumService.findForumsMainWWW(bid, pages);//重点的地方
List flist = pl.getObjectList();
for (int i = 0; i < flist.size(); i++) {
Forum f = (Forum) flist.get(i);
try {
postLink = BBSCSUtil.absoluteActionURL(request,
"/main.bbscs?action=read&bid=" + f.getBoardID() + "&postID=" + f.getMainID())
.toString();
} catch (MalformedURLException ex) {
postLink = "";
}
entry = new SyndEntryImpl();
entry.setTitle(f.getTitle());
entry.setLink(postLink);
entry.setPublishedDate(new Date(f.getPostTime()));
description = new SyndContentImpl();
if (f.getEditType() == 0) {
description.setType("text/plain");//文本类型
} else {
description.setType("text/html");
}
description.setValue(BBSCSUtil
.getSpeShortString(forumService.getForumDetail(f, false), 400, ""));//格式化
entry.setDescription(description);
entries.add(entry);
}
feed.setEntries(entries);
try {
SyndFeedOutput output = new SyndFeedOutput();
Document messagesDocument = output.outputJDom(feed);
Format format = Format.getPrettyFormat();
format.setOmitDeclaration(true);
XMLOutputter xmlo = new XMLOutputter(format);
xmlo.output(messagesDocument, out);
} catch (Exception ex) {
logger.error(ex);
}
}
}
其中:
public static URL absoluteActionURL(HttpServletRequest request, String action) throws MalformedURLException {
return new URL(RequestUtils.serverURL(request) + getActionMappingURL(action, request));
}
--->
public static String getActionMappingURL(String action, HttpServletRequest request) {
StringBuffer value = new StringBuffer(request.getContextPath());//获得的当前目录路径
// Use our servlet mapping, if one is specified
String servletMapping = Constant.SERVLET_MAPPING;//*.bbscs
if (servletMapping != null) {
String queryString = null;
int question = action.indexOf("?");//action="/main.bbscs?action=read&bid=" + f.getBoardID() + "&postID=" + f.getMainID()).toString();
if (question >= 0) {
queryString = action.substring(question);//?action=read&bid=12&postID=123123
}
String actionMapping = getActionMappingName(action);// /main
if (servletMapping.startsWith("*.")) {
value.append(actionMapping);
value.append(servletMapping.substring(1)); //value=/main.bbcs
} else if (servletMapping.endsWith("/*")) {
value.append(servletMapping.substring(0, servletMapping.length() - 2));
value.append(actionMapping);
} else if (servletMapping.equals("/")) {
value.append(actionMapping);
}
if (queryString != null) {
value.append(queryString);
}
}
--->
public static String getActionMappingName(String action) {
String value = action;
int question = action.indexOf("?");
if (question >= 0) {
value = value.substring(0, question);// /main.bbscs
}
int slash = value.lastIndexOf("/");
int period = value.lastIndexOf(".");
if ((period >= 0) && (period > slash)) {
value = value.substring(0, period);// /main
}
if (value.startsWith("/")) {
return (value);
} else {
return ("/" + value);
}
}
OK!接下来,我们看看登录流程先开始分析吧!
在浏览器在输入http://localhost:8080/bbscs8启动:
<welcome-file-list>
<welcome-file>index.jsp</welcome-file> //相对当前目录哦!!!
</welcome-file-list>
index.jsp触发了action login.bbscs?action=check:
<script language="javascript" type="text/javascript">
window.location.href="login.bbscs?action=check";
</script>
</head>
-->struts2发生作用...
我们看下struts.properties:
struts.devMode=false
struts.action.extension=bbscs //后缀
struts.enable.DynamicMethodInvocation=true
struts.i18n.reload=true
struts.ui.theme=simple
struts.locale=zh_CN
struts.i18n.encoding=UTF-8
struts.objectFactory=spring //由spring来代理bean
struts.objectFactory.spring.autoWire=name
struts.serve.static.browserCache=false
struts.url.includeParams=none
struts.custom.i18n.resources=com.laoer.bbscs.web.action.BaseAction //资源文件!
看后台的输出日志[com.opensymphony.xwork2.validator.ActionValidatorManagerFactory]-[INFO] Detected AnnotationActionValidatorManager, initializing it... (注意:并不一定是这里触发哦!
接着我们看struts.xml,其中name为bbscs-default的package继承之struts-default,并引入了许多自定义的interceptor和interceptor-stack.也设置了global-results...这些是为它们package使用的.
<package name="loginout" extends="bbscs-default" namespace="/">
<action name="login" class="loginAction">
<result name="success" type="redirect">${tourl}</result>
<result name="input">/WEB-INF/jsp/login.jsp</result>
<result name="loginPass">/WEB-INF/jsp/passLogin.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>//一旦继承了struts-default包(package),所有Action都会调用拦截器栈 ——defaultStack。当然,在Action配置中加入“<interceptor-ref name="xx" />”可以覆盖defaultStack。
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<interceptor-ref name="userCookieInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
</action>
</package>
注意这里的login对应于spring配置文件action-servlet.xml中的loginAction:
<bean id="loginAction" class="com.laoer.bbscs.web.action.Login" //所管理的action bean
scope="prototype" autowire="byName"> //用了autowire就不用下面这段了
<!--
<property name="sysConfig">
<ref bean="sysConfig" />
</property>
-->
</bean>
好的,我们先看拦截器:<interceptor-ref name="defaultStack"></interceptor-ref>使用默认的拦截器栈(在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用),我们可以打开struts2.0core包找到struts-default.xml打开找到<interceptors>元素内容..请看资料:http://www.blogjava.net/max/archive/2006/12/06/85925.html
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -