springmvc
在SpringMVC中,将一个普通的java类标注上Controller注解之后,再将类中的方法使用RequestMapping注解标注,那么这个普通的java类就够处理Web请求,示例代码如下:
复制代码
1/**
2*使用Controller注解标注LoginUI类
3*/
4@Controller
5publicclassLoginUI{
6
7//使用RequestMapping注解指明forward1方法的访问路径
8@RequestMapping("LoginUI/Login2")
9publicViewforward1(){
10//执行完forward1方法之后返回的视图
11returnnewView("/login2.jsp");
12}
13
14//使用RequestMapping注解指明forward2方法的访问路径
15@RequestMapping("LoginUI/Login3")
16publicViewforward2(){
17//执行完forward2方法之后返回的视图
18returnnewView("/login3.jsp");
19}
20}
复制代码
spring通过javaannotation就可以注释一个类为action,在方法上添加上一个javaannotation就可以配置请求的路径了,那么这种机制是如何实现的呢,今天我们使用"自定义注解+Servlet"来简单模拟一下SpringMVC中的这种注解配置方式。
一、编写注解
1.1、Controller注解
开发Controller注解,这个注解只有一个value属性,默认值为空字符串,代码如下:
复制代码
1packageme.gacl.annotation;
2
3importjava.lang.annotation.ElementType;
4importjava.lang.annotation.Retention;
5importjava.lang.annotation.RetentionPolicy;
6importjava.lang.annotation.Target;
7
8/**
9*@ClassName:Controller
10*@Description:自定义Controller注解
11*@author:孤傲苍狼
12*@date:2014-11-16下午6:16:40
13*
14*/
15@Retention(RetentionPolicy.RUNTIME)
16@Target(ElementType.TYPE)
17public@interfaceController{
18
19publicStringvalue()default"";
20}
复制代码
1.2、RequestMapping注解
开发RequestMapping注解,用于定义请求路径,这个注解只有一个value属性,默认值为空字符串,代码如下:
复制代码
1packageme.gacl.annotation;
2
3importjava.lang.annotation.ElementType;
4importjava.lang.annotation.Retention;
5importjava.lang.annotation.RetentionPolicy;
6importjava.lang.annotation.Target;
7
8/**
9*定义请求路径的javaannotation
10*/
11@Target(ElementType.METHOD)
12@Retention(RetentionPolicy.RUNTIME)
13public@interfaceRequestMapping{
14
15publicStringvalue()default"";
16}
复制代码
以上就是我们自定义的两个注解,注解的开发工作就算是完成了,有了注解之后,那么就必须针对注解来编写处理器,否则我们开发的注解配置到类或者方法上面是不起作用的,这里我们使用Servlet来作为注解的处理器。
二、编写核心的注解处理器
2.1、开发AnnotationHandleServlet
这里使用一个Servlet来作为注解处理器,编写一个AnnotationHandleServlet,代码如下:
复制代码
1packageme.gacl.web.controller;
2
3importjava.io.IOException;
4importjava.lang.reflect.InvocationTargetException;
5importjava.lang.reflect.Method;
6importjava.util.Set;
7importjavax.servlet.ServletConfig;
8importjavax.servlet.ServletException;
9importjavax.servlet.http.HttpServlet;
10importjavax.servlet.http.HttpServletRequest;
11importjavax.servlet.http.HttpServletResponse;
12importme.gacl.annotation.Controller;
13importme.gacl.annotation.RequestMapping;
14importme.gacl.util.BeanUtils;
15importme.gacl.util.RequestMapingMap;
16importme.gacl.util.ScanClassUtil;
17importme.gacl.web.context.WebContext;
18importme.gacl.web.view.DispatchActionConstant;
19importme.gacl.web.view.View;
20
21/**
22*<p>ClassName:AnnotationHandleServlet<p>
23*<p>Description:AnnotationHandleServlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求<p>
24*@authorxudp
25*@version1.0V
26*/
27publicclassAnnotationHandleServletextendsHttpServlet{
28
29privateStringpareRequestURI(HttpServletRequestrequest){
30Stringpath=request.getContextPath()+"/";
31StringrequestUri=request.getRequestURI();
32StringmidUrl=requestUri.replaceFirst(path,"");
33Stringlasturl=midUrl.substring(0,midUrl.lastIndexOf("."));
34returnlasturl;
35}
36
37publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
38throwsServletException,IOException{
39this.excute(request,response);
40}
41
42publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
43throwsServletException,IOException{
44this.excute(request,response);
45}
46
47privatevoidexcute(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
48//将当前线程中HttpServletRequest对象存储到ThreadLocal中,以便在Controller类中使用
49WebContext.requestHodler.set(request);
50//将当前线程中HttpServletResponse对象存储到ThreadLocal中,以便在Controller类中使用
51WebContext.responseHodler.set(response);
52//解析url
53Stringlasturl=pareRequestURI(request);
54//获取要使用的类
55Class<?>clazz=RequestMapingMap.getRequesetMap().get(lasturl);
56//创建类的实例
57ObjectclassInstance=BeanUtils.instanceClass(clazz);
58//获取类中定义的方法
59Method[]methods=BeanUtils.findDeclaredMethods(clazz);
60Methodmethod=null;
61for(Methodm:methods){//循环方法,找匹配的方法进行执行
62if(m.isAnnotationPresent(RequestMapping.class)){
63StringanoPath=m.getAnnotation(RequestMapping.class).value();
64if(anoPath!=null&&!"".equals(anoPath.trim())&&lasturl.equals(anoPath.trim())){
65//找到要执行的目标方法
66method=m;
67break;
68}
69}
70}
71try{
72if(method!=null){
73//执行目标方法处理用户请求
74ObjectretObject=method.invoke(classInstance);
75//如果方法有返回值,那么就表示用户需要返回视图
76if(retObject!=null){
77Viewview=(View)retObject;
78//判断要使用的跳转方式
79if(view.getDispathAction().equals(DispatchActionConstant.FORWARD)){
80//使用服务器端跳转方式
81request.getRequestDispatcher(view.getUrl()).forward(request,response);
82}elseif(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
83//使用客户端跳转方式
84response.sendRedirect(request.getContextPath()+view.getUrl());
85}else{
86request.getRequestDispatcher(view.getUrl()).forward(request,response);
87}
88}
89}
90}catch(IllegalArgumentExceptione){
91e.printStackTrace();
92}catch(IllegalAccessExceptione){
93e.printStackTrace();
94}catch(InvocationTargetExceptione){
95e.printStackTrace();
96}
97}
98
99@Override
100publicvoidinit(ServletConfigconfig)throwsServletException{
101/**
102*重写了Servlet的init方法后一定要记得调用父类的init方法,
103*否则在service/doGet/doPost方法中使用getServletContext()方法获取ServletContext对象时
104*就会出现java.lang.NullPointerException异常
105*/
106super.init(config);
107System.out.println("---初始化开始---");
108//获取web.xml中配置的要扫描的包
109StringbasePackage=config.getInitParameter("basePackage");
110//如果配置了多个包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
111if(basePackage.indexOf(",")>0){
112//按逗号进行分隔
113String[]packageNameArr=basePackage.split(",");
114for(StringpackageName:packageNameArr){
115initRequestMapingMap(packageName);
116}
117}else{
118initRequestMapingMap(basePackage);
119}
120System.out.println("----初始化结束---");
121}
122
123/**
124*@Method:initRequestMapingMap
125*@Description:添加使用了Controller注解的Class到RequestMapingMap中
126*@Anthor:孤傲苍狼
127*@parampackageName
128*/
129privatevoidinitRequestMapingMap(StringpackageName){
130Set<Class<?>>setClasses=ScanClassUtil.getClasses(packageName);
131for(Class<?>clazz:setClasses){
132if(clazz.isAnnotationPresent(Controller.class)){
133Method[]methods=BeanUtils.findDeclaredMethods(clazz);
134for(Methodm:methods){//循环方法,找匹配的方法进行执行
135if(m.isAnnotationPresent(RequestMapping.class)){
136StringanoPath=m.getAnnotation(RequestMapping.class).value();
137if(anoPath!=null&&!"".equals(anoPath.trim())){
138if(RequestMapingMap.getRequesetMap().containsKey(anoPath)){
139thrownewRuntimeException("RequestMapping映射的地址不允许重复!");
140}
141RequestMapingMap.put(anoPath,clazz);
142}
143}
144}
145}
146}
147}
148}
复制代码
这里说一下AnnotationHandleServlet的实现思路
1、AnnotationHandleServlet初始化(init)时扫描指定的包下面使用了Controller注解的类,如下图所示:
2、遍历类中的方法,找到类中使用了RequestMapping注解标注的那些方法,获取RequestMapping注解的value属性值,value属性值指明了该方法的访问路径,以RequestMapping注解的value属性值作为key,Class类作为value将存储到一个静态Map集合中。如下图所示:
当用户请求时(无论是get还是post请求),会调用封装好的execute方法,execute会先获取请求的url,然后解析该URL,根据解析好的URL从Map集合中取出要调用的目标类,再遍历目标类中定义的所有方法,找到类中使用了RequestMapping注解的那些方法,判断方法上面的RequestMapping注解的value属性值是否和解析出来的URL路径一致,如果一致,说明了这个就是要调用的目标方法,此时就可以利用java反射机制先实例化目标类对象,然后再通过实例化对象调用要执行的方法处理用户请求。服务器将以下图的方式与客户端进行交互
另外,方法处理完成之后需要给客户端发送响应信息,比如告诉客户端要跳转到哪一个页面,采用的是服务器端跳转还是客户端方式跳转,或者发送一些数据到客户端显示,那么该如何发送响应信息给客户端呢,在此,我们可以设计一个View(视图)类,对这些操作属性进行封装,其中包括跳转的路径、展现到页面的数据、跳转方式。这就是AnnotationHandleServlet的实现思路。
2.2、在Web.xml文件中注册AnnotationHandleServlet
在web.xml文件中配置AnnotationHandleServlet和需要扫描的包
复制代码
1<servlet>
2<servlet-name>AnnotationHandleServlet</servlet-name>
3<servlet-class>me.gacl.web.controller.AnnotationHandleServlet</servlet-class>
4<init-param>
5<description>配置要扫描包及其子包,如果有多个包,以逗号分隔</description>
6<param-name>basePackage</param-name>
7<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
8<!--<param-value>me.gacl.web.controller</param-value>-->
9</init-param>
10<load-on-startup>1</load-on-startup>
11</servlet>
12
13<servlet-mapping>
14<servlet-name>AnnotationHandleServlet</servlet-name>
15<!--拦截所有以.do后缀结尾的请求-->
16<url-pattern>*.do</url-pattern>
17</servlet-mapping>
复制代码
三、相关代码讲解
3.1、BeanUtils
BeanUtils工具类主要是用来处理一些反射的操作
复制代码
1packageme.gacl.util;
2
3importjava.lang.reflect.Constructor;
4importjava.lang.reflect.Field;
5importjava.lang.reflect.InvocationTargetException;
6importjava.lang.reflect.Method;
7importjava.lang.reflect.Modifier;
8
9/**
10*对java反射中操作的一些封装
11*/
12publicclassBeanUtils{
13
14/**
15*实例化一个class
16*@param<T>
17*@paramclazzPerson.class
18*@return
19*/
20publicstatic<T>TinstanceClass(Class<T>clazz){
21if(!clazz.isInterface()){
22try{
23returnclazz.newInstance();
24}catch(InstantiationExceptione){
25e.printStackTrace();
26}catch(IllegalAccessExceptione){
27e.printStackTrace();
28}
29}
30returnnull;
31}
32
33/**
34*通过构造函数实例化
35*@param<T>
36*@paramctor
37*@paramargs
38*@return
39*@throwsIllegalArgumentException
40*@throwsInstantiationException
41*@throwsIllegalAccessException
42*@throwsInvocationTargetException
43*/
44publicstatic<T>TinstanceClass(Constructor<T>ctor,Object...args)
45throwsIllegalArgumentException,InstantiationException,
46IllegalAccessException,InvocationTargetException{
47makeAccessible(ctor);
48returnctor.newInstance(args);//调用构造方法实例化
49}
50
51/**
52*查找某个class的方法
53*@paramclazz
54*@parammethodName
55*@paramparamTypes
56*@return
57*@throwsSecurityException
58*@throwsNoSuchMethodException
59*/
60publicstaticMethodfindMethod(Class<?>clazz,StringmethodName,Class<?>...paramTypes){
61try{
62returnclazz.getMethod(methodName,paramTypes);
63}catch(NoSuchMethodExceptione){
64returnfindDeclaredMethod(clazz,methodName,paramTypes);//返回共有的方法
65}
66}
67
68publicstaticMethodfindDeclaredMethod(Class<?>clazz,StringmethodName,Class<?>[]paramTypes){
69try{
70returnclazz.getDeclaredMethod(methodName,paramTypes);
71}
72catch(NoSuchMethodExceptionex){
73if(clazz.getSuperclass()!=null){
74returnfindDeclaredMethod(clazz.getSuperclass(),methodName,paramTypes);
75}
76returnnull;
77}
78}
79
80publicstaticMethod[]findDeclaredMethods(Class<?>clazz){
81returnclazz.getDeclaredMethods();
82}
83
84publicstaticvoidmakeAccessible(Constructor<?>ctor){
85if((!Modifier.isPublic(ctor.getModifiers())
86||!Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
87&&!ctor.isAccessible()){
88ctor.setAccessible(true);//如果是私有的设置为true使其可以访问
89}
90}
91
92publicstaticField[]findDeclaredFields(Class<?>clazz){
93returnclazz.getDeclaredFields();
94}
95}
复制代码
3.2、RequestMapingMap
该类是用于存储方法的访问路径,AnnotationHandleServlet初始化时会将类(使用Controller注解标注的那些类)中使用了RequestMapping注解标注的那些方法的访问路径存储到RequestMapingMap中。
复制代码
1packageme.gacl.util;
2
3importjava.util.HashMap;
4importjava.util.Map;
5
6/**
7*@ClassName:RequestMapingMap
8*@Description:存储方法的访问路径
9*@author:孤傲苍狼
10*@date:2014-11-16下午6:31:43
11*
12*/
13publicclassRequestMapingMap{
14
15/**
16*@Field:requesetMap
17*用于存储方法的访问路径
18*/
19privatestaticMap<String,Class<?>>requesetMap=newHashMap<String,Class<?>>();
20
21publicstaticClass<?>getClassName(Stringpath){
22returnrequesetMap.get(path);
23}
24
25publicstaticvoidput(Stringpath,Class<?>className){
26requesetMap.put(path,className);
27}
28
29publicstaticMap<String,Class<?>>getRequesetMap(){
30returnrequesetMap;
31}
32}
复制代码
3.3、ScanClassUtil
扫描某个包下面的类的工具类
复制代码
1packageme.gacl.util;
2
3importjava.io.File;
4importjava.io.FileFilter;
5importjava.io.IOException;
6importjava.net.JarURLConnection;
7importjava.net.URL;
8importjava.net.URLDecoder;
9importjava.util.Enumeration;
10importjava.util.LinkedHashSet;
11importjava.util.Set;
12importjava.util.jar.JarEntry;
13importjava.util.jar.JarFile;
14
15/**
16*@ClassName:ScanClassUtil
17*@Description:扫描指定包或者jar包下面的class
18*@author:孤傲苍狼
19*@date:2014-11-16下午6:34:10
20*
21*/
22publicclassScanClassUtil{
23
24/**
25*从包package中获取所有的Class
26*
27*@parampack
28*@return
29*/
30publicstaticSet<Class<?>>getClasses(Stringpack){
31
32//第一个class类的集合
33Set<Class<?>>classes=newLinkedHashSet<Class<?>>();
34//是否循环迭代
35booleanrecursive=true;
36//获取包的名字并进行替换
37StringpackageName=pack;
38StringpackageDirName=packageName.replace('.','/');
39//定义一个枚举的集合并进行循环来处理这个目录下的things
40Enumeration<URL>dirs;
41try{
42dirs=Thread.currentThread().getContextClassLoader().getResources(
43packageDirName);
44//循环迭代下去
45while(dirs.hasMoreElements()){
46//获取下一个元素
47URLurl=dirs.nextElement();
48//得到协议的名称
49Stringprotocol=url.getProtocol();
50//如果是以文件的形式保存在服务器上
51if("file".equals(protocol)){
52System.err.println("file类型的扫描");
53//获取包的物理路径
54StringfilePath=URLDecoder.decode(url.getFile(),"UTF-8");
55//以文件的方式扫描整个包下的文件并添加到集合中
56findAndAddClassesInPackageByFile(packageName,filePath,
57recursive,classes);
58}elseif("jar".equals(protocol)){
59//如果是jar包文件
60//定义一个JarFile
61System.err.println("jar类型的扫描");
62JarFilejar;
63try{
64//获取jar
65jar=((JarURLConnection)url.openConnection())
66.getJarFile();
67//从此jar包得到一个枚举类
68Enumeration<JarEntry>entries=jar.entries();
69//同样的进行循环迭代
70while(entries.hasMoreElements()){
71//获取jar里的一个实体可以是目录和一些jar包里的其他文件如META-INF等文件
72JarEntryentry=entries.nextElement();
73Stringname=entry.getName();
74//如果是以/开头的
75if(name.charAt(0)=='/'){
76//获取后面的字符串
77name=name.substring(1);
78}
79//如果前半部分和定义的包名相同
80if(name.startsWith(packageDirName)){
81intidx=name.lastIndexOf('/');
82//如果以"/"结尾是一个包
83if(idx!=-1){
84//获取包名把"/"替换成"."
85packageName=name.substring(0,idx)
86.replace('/','.');
87}
88//如果可以迭代下去并且是一个包
89if((idx!=-1)||recursive){
90//如果是一个.class文件而且不是目录
91if(name.endsWith(".class")
92&&!entry.isDirectory()){
93//去掉后面的".class"获取真正的类名
94StringclassName=name.substring(
95packageName.length()+1,name
96.length()-6);
97try{
98//添加到classes
99classes.add(Class
100.forName(packageName+'.'
101+className));
102}catch(ClassNotFoundExceptione){
103//log
104//.error("添加用户自定义视图类错误找不到此类的.class文件");
105e.printStackTrace();
106}
107}
108}
109}
110}
111}catch(IOExceptione){
112//log.error("在扫描用户定义视图时从jar包获取文件出错");
113e.printStackTrace();
114}
115}
116}
117}catch(IOExceptione){
118e.printStackTrace();
119}
120
121returnclasses;
122}
123
124/**
125*以文件的形式来获取包下的所有Class
126*
127*@parampackageName
128*@parampackagePath
129*@paramrecursive
130*@paramclasses
131*/
132publicstaticvoidfindAndAddClassesInPackageByFile(StringpackageName,
133StringpackagePath,finalbooleanrecursive,Set<Class<?>>classes){
134//获取此包的目录建立一个File
135Filedir=newFile(packagePath);
136//如果不存在或者也不是目录就直接返回
137if(!dir.exists()||!dir.isDirectory()){
138//log.warn("用户定义包名"+packageName+"下没有任何文件");
139return;
140}
141//如果存在就获取包下的所有文件包括目录
142File[]dirfiles=dir.listFiles(newFileFilter(){
143//自定义过滤规则如果可以循环(包含子目录)或则是以.class结尾的文件(编译好的java类文件)
144publicbooleanaccept(Filefile){
145return(recursive&&file.isDirectory())
146||(file.getName().endsWith(".class"));
147}
148});
149//循环所有文件
150for(Filefile:dirfiles){
151//如果是目录则继续扫描
152if(file.isDirectory()){
153findAndAddClassesInPackageByFile(packageName+"."
154+file.getName(),file.getAbsolutePath(),recursive,
155classes);
156}else{
157//如果是java类文件去掉后面的.class只留下类名
158StringclassName=file.getName().substring(0,
159file.getName().length()-6);
160try{
161//添加到集合中去
162//classes.add(Class.forName(packageName+'.'+className));
163//经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
164classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName+'.'+className));
165}catch(ClassNotFoundExceptione){
166//log.error("添加用户自定义视图类错误找不到此类的.class文件");
167e.printStackTrace();
168}
169}
170}
171}
172}
复制代码
3.4、WebContext
WebContext主要是用来存储当前线程中的HttpServletRequest和HttpServletResponse,当别的地方需要使用HttpServletRequest和HttpServletResponse,就可以通过requestHodler和responseHodler获取,通过WebContext.java这个类,我们可以在作为Controller的普通java类中获取当前请求的request、response或者session相关请求类的实例变量,并且线程间互不干扰的,因为用到了ThreadLocal这个类。
复制代码
1packageme.gacl.web.context;
2
3importjavax.servlet.ServletContext;
4importjavax.servlet.http.HttpServletRequest;
5importjavax.servlet.http.HttpServletResponse;
6importjavax.servlet.http.HttpSession;
7
8/**
9*WebContext主要是用来存储当前线程中的HttpServletRequest和HttpServletResponse
10*当别的地方需要使用HttpServletRequest和HttpServletResponse,就可以通过requestHodler和responseHodler获取
11**/
12publicclassWebContext{
13
14publicstaticThreadLocal<HttpServletRequest>requestHodler=newThreadLocal<HttpServletRequest>();
15publicstaticThreadLocal<HttpServletResponse>responseHodler=newThreadLocal<HttpServletResponse>();
16
17publicHttpServletRequestgetRequest(){
18returnrequestHodler.get();
19}
20
21publicHttpSessiongetSession(){
22returnrequestHodler.get().getSession();
23}
24
25publicServletContextgetServletContext(){
26returnrequestHodler.get().getSession().getServletContext();
27}
28
29publicHttpServletResponsegetResponse(){
30returnresponseHodler.get();
31}
32}
复制代码
3.5、View
一个视图类,对一些客户端响应操作进行封装,其中包括跳转的路径、展现到页面的数据、跳转方式
复制代码
1packageme.gacl.web.view;
2
3/**
4*视图模型
5**/
6publicclassView{
7
8privateStringurl;//跳转路径
9
10privateStringdispathAction=DispatchActionConstant.FORWARD;//跳转方式
11
12publicView(Stringurl){
13this.url=url;
14}
15
16publicView(Stringurl,Stringname,Objectvalue){
17this.url=url;
18ViewDataview=newViewData();
19view.put(name,value);
20}
21
22
23publicView(Stringurl,Stringname,StringdispathAction,Objectvalue){
24this.dispathAction=dispathAction;
25this.url=url;
26ViewDataview=newViewData();//请看后面的代码
27view.put(name,value);
28}
29
30
31publicStringgetUrl(){
32returnurl;
33}
34
35
36publicvoidsetUrl(Stringurl){
37this.url=url;
38}
39
40publicStringgetDispathAction(){
41returndispathAction;
42}
43
44publicvoidsetDispathAction(StringdispathAction){
45this.dispathAction=dispathAction;
46}
47}
复制代码
3.6、ViewData
request范围的数据存储类,当需要发送数据到客户端显示时,就可以将要显示的数据存储到ViewData类中。使用ViewData.put(Stringname,Objectvalue)方法往request对象中存数据。
复制代码
1packageme.gacl.web.view;
2
3importjavax.servlet.http.HttpServletRequest;
4
5importme.gacl.web.context.WebContext;
6
7/**
8*需要发送到客户端显示的数据模型
9*/
10publicclassViewData{
11
12privateHttpServletRequestrequest;
13
14publicViewData(){
15initRequest();
16}
17
18privatevoidinitRequest(){
19//从requestHodler中获取request对象
20this.request=WebContext.requestHodler.get();
21}
22
23publicvoidput(Stringname,Objectvalue){
24this.request.setAttribute(name,value);
25}
26}
复制代码
3.7、DispatchActionConstant
一个跳转方式的常量类
复制代码
1packageme.gacl.web.view;
2
3/**
4*跳转常量
5*/
6publicclassDispatchActionConstant{
7
8publicstaticStringFORWARD="forward";//服务器跳转
9
10publicstaticStringREDIRECT="redirect";//客户端跳转
11}
复制代码
四、Controller注解和RequestMapping注解测试
4.1、简单测试
编写一个LoginUI类,用于跳转到具体的jsp页面,代码如下:
复制代码
1packageme.gacl.web.UI;
2
3importme.gacl.annotation.Controller;
4importme.gacl.annotation.RequestMapping;
5importme.gacl.web.view.View;
6/**
7*使用Controller注解标注LoginUI类
8*/
9@Controller
10publicclassLoginUI{
11
12//使用RequestMapping注解指明forward1方法的访问路径
13@RequestMapping("LoginUI/Login2")
14publicViewforward1(){
15//执行完forward1方法之后返回的视图
16returnnewView("/login2.jsp");
17}
18
19//使用RequestMapping注解指明forward2方法的访问路径
20@RequestMapping("LoginUI/Login3")
21publicViewforward2(){
22//执行完forward2方法之后返回的视图
23returnnewView("/login3.jsp");
24}
25}
复制代码
运行结果如下所示:
4.2、复杂测试
编写用于处理用户登录请求的Controller,代码如下:
复制代码
1packageme.gacl.web.controller;
2
3importjava.io.IOException;
4importjavax.servlet.http.HttpServletRequest;
5importjavax.servlet.http.HttpServletResponse;
6importme.gacl.annotation.Controller;
7importme.gacl.annotation.RequestMapping;
8importme.gacl.web.context.WebContext;
9importme.gacl.web.view.View;
10importme.gacl.web.view.ViewData;
11
12/**
13*
14*@ClassName:LoginServlet2
15*@Description:处理用户登录的Servlet,
16*LoginServlet现在就是一个普通的java类,不是一个真正的Servlet
17*@author:孤傲苍狼
18*@date:2014-10-8上午12:07:58
19*
20*/
21@Controller//使用Controller注解标注LoginServlet2
22publicclassLoginServlet2{
23
24/**
25*@Method:loginHandle
26*@Description:处理以普通方式提交的请求
27*@Anthor:孤傲苍狼
28*
29*@returnView
30*/
31//使用RequestMapping注解标注loginHandle方法,指明loginHandle方法的访问路径是login/handle
32@RequestMapping("login/handle")
33publicViewloginHandle(){
34//创建一个ViewData对象,用于存储需要发送到客户端的响应数据
35ViewDataviewData=newViewData();
36//通过WebContext类获取当前线程中的HttpServletRequest对象
37HttpServletRequestrequest=WebContext.requestHodler.get();
38//接收提交上来的参数
39Stringusername=request.getParameter("usename");
40Stringpwd=request.getParameter("pwd");
41if(username.equals("gacl")&&pwd.equals("xdp")){
42request.getSession().setAttribute("usename",username);
43//将响应数据存储到ViewData对象中
44viewData.put("msg","欢迎您!"+username);
45//返回一个View对象,指明要跳转的视图的路径
46returnnewView("/index.jsp");
47}else{
48//将响应数据存储到ViewData对象中
49viewData.put("msg","登录失败,请检查用户名和密码是否正确!");
50//返回一个View对象,指明要跳转的视图的路径
51returnnewView("/login2.jsp");
52}
53}
54
55/**
56*@Method:ajaxLoginHandle
57*@Description:处理以AJAX方式提交的请求
58*@Anthor:孤傲苍狼
59*
60*@throwsIOException
61*/
62//使用RequestMapping注解标注ajaxLoginHandle方法,指明ajaxLoginHandle方法的访问路径是ajaxLogin/handle
63@RequestMapping("ajaxLogin/handle")
64publicvoidajaxLoginHandle()throwsIOException{
65//通过WebContext类获取当前线程中的HttpServletRequest对象
66HttpServletRequestrequest=WebContext.requestHodler.get();
67//接收提交上来的参数
68Stringusername=request.getParameter("usename");
69Stringpwd=request.getParameter("pwd");
70//通过WebContext类获取当前线程中的HttpServletResponse对象
71HttpServletResponseresponse=WebContext.responseHodler.get();
72if(username.equals("gacl")&&pwd.equals("xdp")){
73request.getSession().setAttribute("usename",username);
74response.getWriter().write("success");
75}else{
76response.getWriter().write("fail");
77}
78}
79}
复制代码
编写用于测试的jsp页面,代码如下所示:
Login2.jsp登录页面
复制代码
1<%@pagelanguage="java"pageEncoding="UTF-8"%>
2<!DOCTYPEHTML>
3<html>
4<head>
5<title>login2.jsp登录页面</title>
6</head>
7
8<body>
9<fieldset>
10<legend>用户登录</legend>
11<formaction="${pageContext.request.contextPath}/login/handle.do"method="post">
12用户名:<inputtype="text"value="${param.usename}"name="usename">
13<br/>
14密码:<inputtype="text"value="${param.pwd}"name="pwd">
15<br/>
16<inputtype="submit"value="登录"/>
17</form>
18</fieldset>
19<hr/>
20<labelstyle="color:red;">${msg}</label>
21</body>
22</html>
复制代码
login3.jsp登录页面
复制代码
1<%@pagelanguage="java"pageEncoding="UTF-8"%>
2<!DOCTYPEHTML>
3<html>
4<head>
5<title>login3登录页面</title>
6<scripttype="text/javascript"src="${pageContext.request.contextPath}/ajaxUtil.js"></script>
7<scripttype="text/javascript"src="${pageContext.request.contextPath}/js/Utils.js"></script>
8<scripttype="text/javascript">
9
10functionlogin(){
11Ajax.request({
12url:"${pageContext.request.contextPath}/ajaxLogin/handle.do",
13data:{
14"usename":document.getElementById("usename").value,
15"pwd":document.getElementById("pwd").value
16},
17success:function(xhr){
18onData(xhr.responseText);
19},
20error:function(xhr){
21
22}
23});
24}
25
26functiononData(responseText){
27if(responseText=="success"){
28//window.location.href="index.jsp";//改变url地址
29/*
30window.location.replace("url"):将地址替换成新url,
31该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,
32你不能通过“前进”和“后退”来访问已经被替换的URL,这个特点对于做一些过渡页面非常有用!
33*/
34location.replace(g_basePath+"/index.jsp");
35}else{
36alert("用户名和密码错误");
37}
38}
39</script>
40</head>
41
42<body>
43<fieldset>
44<legend>用户登录</legend>
45<form>
46用户名:<inputtype="text"name="usename"id="usename">
47<br/>
48密码:<inputtype="text"name="pwd"id="pwd">
49<br/>
50<inputtype="button"value="登录"onclick="login()"/>
51</form>
52</fieldset>
53</body>
54</html>
复制代码
index.jsp页面代码如下:
复制代码
1<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
2
3<!DOCTYPEHTML>
4<html>
5<head>
6<title>首页</title>
7</head>
8
9<body>
10登录的用户名:${usename}
11<br/>
12${msg}
13</body>
14</html>
复制代码
jsp页面中使用到的Utils.js代码如下:
复制代码
1//立即执行的js
2(function(){
3//获取contextPath
4varcontextPath=getContextPath();
5//获取basePath
6varbasePath=getBasePath();
7//将获取到contextPath和basePath分别赋值给window对象的g_contextPath属性和g_basePath属性
8window.g_contextPath=contextPath;
9window.g_basePath=basePath;
10})();
11
12/**
13*@author孤傲苍狼
14*获得项目根路径,等价于jsp页面中
15*<%
16StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
17%>
18*使用方法:getBasePath();
19*@returns项目的根路径
20*
21*/
22functiongetBasePath(){
23varcurWwwPath=window.document.location.href;
24varpathName=window.document.location.pathname;
25varpos=curWwwPath.indexOf(pathName);
26varlocalhostPath=curWwwPath.substring(0,pos);
27varprojectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);
28return(localhostPath+projectName);
29}
30
31/**
32*@author孤傲苍狼
33*获取Web应用的contextPath,等价于jsp页面中
34*<%
35Stringpath=request.getContextPath();
36%>
37*使用方法:getContextPath();
38*@returns/项目名称(/EasyUIStudy_20141104)
39*/
40functiongetContextPath(){
41returnwindow.document.location.pathname.substring(0,window.document.location.pathname.indexOf('\/',1));
42};
复制代码