⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 详细分析三.txt

📁 开源论坛实现
💻 TXT
📖 第 1 页 / 共 4 页
字号:
经过前面的分析,我们已经理清楚了业务层,接下来的部分将是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 + -