java
javaweb学习总结(四十九)——简单模拟sping mvc -凯发ag旗舰厅登录网址下载
在spring mvc中,将一个普通的java类标注上controller注解之后,再将类中的方法使用requestmapping注解标注,那么这个普通的java类就够处理web请求,示例代码如下:
1 /** 2 * 使用controller注解标注loginui类 3 */ 4 @controller 5 public class loginui { 6 7 //使用requestmapping注解指明forward1方法的访问路径 8 @requestmapping("loginui/login2") 9 public view forward1(){ 10 //执行完forward1方法之后返回的视图 11 return new view("/login2.jsp"); 12 } 13 14 //使用requestmapping注解指明forward2方法的访问路径 15 @requestmapping("loginui/login3") 16 public view forward2(){ 17 //执行完forward2方法之后返回的视图 18 return new view("/login3.jsp"); 19 } 20 }spring通过java annotation就可以注释一个类为action ,在方法上添加上一个java annotation 就可以配置请求的路径了,那么这种机制是如何实现的呢,今天我们使用"自定义注解 servlet"来简单模拟一下spring mvc中的这种注解配置方式。
一、编写注解
1.1、controller注解
开发controller注解,这个注解只有一个value属性,默认值为空字符串,代码如下:
1 package me.gacl.annotation; 2 3 import java.lang.annotation.elementtype; 4 import java.lang.annotation.retention; 5 import java.lang.annotation.retentionpolicy; 6 import java.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) 17 public @interface controller { 18 19 public string value() default ""; 20 }1.2、requestmapping注解
开发requestmapping注解,用于定义请求路径,这个注解只有一个value属性,默认值为空字符串,代码如下:
1 package me.gacl.annotation; 2 3 import java.lang.annotation.elementtype; 4 import java.lang.annotation.retention; 5 import java.lang.annotation.retentionpolicy; 6 import java.lang.annotation.target; 7 8 /** 9 * 定义请求路径的java annotation 10 */ 11 @target(elementtype.method) 12 @retention(retentionpolicy.runtime) 13 public @interface requestmapping { 14 15 public string value() default ""; 16 }以上就是我们自定义的两个注解,注解的开发工作就算是完成了,有了注解之后,那么就必须针对注解来编写处理器,否则我们开发的注解配置到类或者方法上面是不起作用的,这里我们使用servlet来作为注解的处理器。
二、编写核心的注解处理器
2.1、开发annotationhandleservlet
这里使用一个servlet来作为注解处理器,编写一个annotationhandleservlet,代码如下:
1 package me.gacl.web.controller; 2 3 import java.io.ioexception; 4 import java.lang.reflect.invocationtargetexception; 5 import java.lang.reflect.method; 6 import java.util.set; 7 import javax.servlet.servletconfig; 8 import javax.servlet.servletexception; 9 import javax.servlet.http.httpservlet; 10 import javax.servlet.http.httpservletrequest; 11 import javax.servlet.http.httpservletresponse; 12 import me.gacl.annotation.controller; 13 import me.gacl.annotation.requestmapping; 14 import me.gacl.util.beanutils; 15 import me.gacl.util.requestmapingmap; 16 import me.gacl.util.scanclassutil; 17 import me.gacl.web.context.webcontext; 18 import me.gacl.web.view.dispatchactionconstant; 19 import me.gacl.web.view.view; 20 21 /** 22 *classname: annotationhandleservlet
23 *
description: annotationhandleservlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求
24 * @author xudp
25 * @version 1.0 v
26 */
27 public class annotationhandleservlet extends httpservlet {
28
29 private string parerequesturi(httpservletrequest request){
30 string path = request.getcontextpath() "/";
31 string requesturi = request.getrequesturi();
32 string midurl = requesturi.replacefirst(path, "");
33 string lasturl = midurl.substring(0, midurl.lastindexof("."));
34 return lasturl;
35 }
36
37 public void doget(httpservletrequest request, httpservletresponse response)
38 throws servletexception, ioexception {
39 this.excute(request, response);
40 }
41
42 public void dopost(httpservletrequest request, httpservletresponse response)
43 throws servletexception, ioexception {
44 this.excute(request, response);
45 }
46
47 private void excute(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception{
48 //将当前线程中httpservletrequest对象存储到threadlocal中,以便在controller类中使用
49 webcontext.requesthodler.set(request);
50 //将当前线程中httpservletresponse对象存储到threadlocal中,以便在controller类中使用
51 webcontext.responsehodler.set(response);
52 //解析url
53 string lasturl = parerequesturi(request);
54 //获取要使用的类
55 class clazz = requestmapingmap.getrequesetmap().get(lasturl);
56 //创建类的实例
57 object classinstance = beanutils.instanceclass(clazz);
58 //获取类中定义的方法
59 method [] methods = beanutils.finddeclaredmethods(clazz);
60 method method = null;
61 for(method m:methods){//循环方法,找匹配的方法进行执行
62 if(m.isannotationpresent(requestmapping.class)){
63 string anopath = m.getannotation(requestmapping.class).value();
64 if(anopath!=null && !"".equals(anopath.trim()) && lasturl.equals(anopath.trim())){
65 //找到要执行的目标方法
66 method = m;
67 break;
68 }
69 }
70 }
71 try {
72 if(method!=null){
73 //执行目标方法处理用户请求
74 object retobject = method.invoke(classinstance);
75 //如果方法有返回值,那么就表示用户需要返回视图
76 if (retobject!=null) {
77 view view = (view)retobject;
78 //判断要使用的跳转方式
79 if(view.getdispathaction().equals(dispatchactionconstant.forward)){
80 //使用服务器端跳转方式
81 request.getrequestdispatcher(view.get).forward(request, response);
82 }else if(view.getdispathaction().equals(dispatchactionconstant.redirect)){
83 //使用客户端跳转方式
84 response.sendredirect(request.getcontextpath() view.get);
85 }else{
86 request.getrequestdispatcher(view.get).forward(request, response);
87 }
88 }
89 }
90 } catch (illegalargumentexception e) {
91 e.printstacktrace();
92 } catch (illegalaccessexception e) {
93 e.printstacktrace();
94 } catch (invocationtargetexception e) {
95 e.printstacktrace();
96 }
97 }
98
99 @override
100 public void init(servletconfig config) throws servletexception {
101 /**
102 * 重写了servlet的init方法后一定要记得调用父类的init方法,
103 * 否则在service/doget/dopost方法中使用getservletcontext()方法获取servletcontext对象时
104 * 就会出现java.lang.nullpointerexception异常
105 */
106 super.init(config);
107 system.out.println("---初始化开始---");
108 //获取web.xml中配置的要扫描的包
109 string basepackage = config.getinitparameter("basepackage");
110 //如果配置了多个包,例如:
这里说一下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>annotationhandleservletservlet-name> 3 <servlet-class>me.gacl.web.controller.annotationhandleservletservlet-class> 4 <init-param> 5 <description>配置要扫描包及其子包, 如果有多个包,以逗号分隔description> 6 <param-name>basepackageparam-name> 7 <param-value>me.gacl.web.controller,me.gacl.web.uiparam-value> 8 9 init-param> 10 <load-on-startup>1load-on-startup> 11 servlet> 12 13 <servlet-mapping> 14 <servlet-name>annotationhandleservletservlet-name> 15 16 <url-pattern>*.dourl-pattern> 17 servlet-mapping>三、相关代码讲解
3.1、beanutils
beanutils工具类主要是用来处理一些反射的操作
1 package me.gacl.util; 2 3 import java.lang.reflect.constructor; 4 import java.lang.reflect.field; 5 import java.lang.reflect.invocationtargetexception; 6 import java.lang.reflect.method; 7 import java.lang.reflect.modifier; 8 9 /** 10 * 对java反射中操作的一些封装 11 */ 12 public class beanutils { 13 14 /** 15 * 实例化一个class 16 * @param3.2、requestmapingmap
该类是用于存储方法的访问路径,annotationhandleservlet初始化时会将类(使用controller注解标注的那些类)中使用了requestmapping注解标注的那些方法的访问路径存储到requestmapingmap中。
1 package me.gacl.util; 2 3 import java.util.hashmap; 4 import java.util.map; 5 6 /** 7 * @classname: requestmapingmap 8 * @description: 存储方法的访问路径 9 * @author: 孤傲苍狼 10 * @date: 2014-11-16 下午6:31:43 11 * 12 */ 13 public class requestmapingmap { 14 15 /** 16 * @field: requesetmap 17 * 用于存储方法的访问路径 18 */ 19 private static map3.3、scanclassutil
扫描某个包下面的类的工具类
1 package me.gacl.util; 2 3 import java.io.file; 4 import java.io.filefilter; 5 import java.io.ioexception; 6 import java.net.jarurlconnection; 7 import java.net.url; 8 import java.net.urldecoder; 9 import java.util.enumeration; 10 import java.util.linkedhashset; 11 import java.util.set; 12 import java.util.jar.jarentry; 13 import java.util.jar.jarfile; 14 15 /** 16 * @classname: scanclassutil 17 * @description: 扫描指定包或者jar包下面的class 18 * @author: 孤傲苍狼 19 * @date: 2014-11-16 下午6:34:10 20 * 21 */ 22 public class scanclassutil { 23 24 /** 25 * 从包package中获取所有的class 26 * 27 * @param pack 28 * @return 29 */ 30 public static set3.4、webcontext
webcontext主要是用来存储当前线程中的httpservletrequest和httpservletresponse,当别的地方需要使用httpservletrequest和httpservletresponse,就可以通过requesthodler和responsehodler获取,通过webcontext.java这个类 ,我们可以在作为controller的普通java类中获取当前请求的request、response或者session相关请求类的实例变量,并且线程间互不干扰的,因为用到了threadlocal这个类。
1 package me.gacl.web.context; 2 3 import javax.servlet.servletcontext; 4 import javax.servlet.http.httpservletrequest; 5 import javax.servlet.http.httpservletresponse; 6 import javax.servlet.http.httpsession; 7 8 /** 9 * webcontext主要是用来存储当前线程中的httpservletrequest和httpservletresponse 10 * 当别的地方需要使用httpservletrequest和httpservletresponse,就可以通过requesthodler和responsehodler获取 11 **/ 12 public class webcontext { 13 14 public static threadlocal3.5、view
一个视图类,对一些客户端响应操作进行封装,其中包括跳转的路径 、展现到页面的数据、跳转方式
1 package me.gacl.web.view; 2 3 /** 4 * 视图模型 5 **/ 6 public class view { 7 8 private string url;//跳转路径 9 10 private string dispathaction = dispatchactionconstant.forward;//跳转方式 11 12 public view(string url) { 13 this.url = url; 14 } 15 16 public view(string url,string name,object value) { 17 this.url = url; 18 viewdata view = new viewdata(); 19 view.put(name, value); 20 } 21 22 23 public view(string url,string name,string dispathaction ,object value) { 24 this.dispathaction = dispathaction; 25 this.url = url; 26 viewdata view = new viewdata();//请看后面的代码 27 view.put(name, value); 28 } 29 30 31 public string get { 32 return url; 33 } 34 35 36 public void set { 37 this.url = url; 38 } 39 40 public string getdispathaction() { 41 return dispathaction; 42 } 43 44 public void setdispathaction(string dispathaction) { 45 this.dispathaction = dispathaction; 46 } 47 }3.6、viewdata
request范围的数据存储类,当需要发送数据到客户端显示时,就可以将要显示的数据存储到viewdata类中。使用viewdata.put(string name,object value)方法往request对象中存数据。
1 package me.gacl.web.view; 2 3 import javax.servlet.http.httpservletrequest; 4 5 import me.gacl.web.context.webcontext; 6 7 /** 8 * 需要发送到客户端显示的数据模型 9 */ 10 public class viewdata { 11 12 private httpservletrequest request; 13 14 public viewdata() { 15 initrequest(); 16 } 17 18 private void initrequest(){ 19 //从requesthodler中获取request对象 20 this.request = webcontext.requesthodler.get(); 21 } 22 23 public void put(string name,object value){ 24 this.request.setattribute(name, value); 25 } 26 }3.7、dispatchactionconstant
一个跳转方式的常量类
1 package me.gacl.web.view; 2 3 /** 4 * 跳转常量 5 */ 6 public class dispatchactionconstant { 7 8 public static string forward = "forward";//服务器跳转 9 10 public static string redirect = "redirect";//客户端跳转 11 }四、controller注解和requestmapping注解测试
4.1、简单测试
编写一个loginui类,用于跳转到具体的jsp页面,代码如下:
1 package me.gacl.web.ui; 2 3 import me.gacl.annotation.controller; 4 import me.gacl.annotation.requestmapping; 5 import me.gacl.web.view.view; 6 /** 7 * 使用controller注解标注loginui类 8 */ 9 @controller 10 public class loginui { 11 12 //使用requestmapping注解指明forward1方法的访问路径 13 @requestmapping("loginui/login2") 14 public view forward1(){ 15 //执行完forward1方法之后返回的视图 16 return new view("/login2.jsp"); 17 } 18 19 //使用requestmapping注解指明forward2方法的访问路径 20 @requestmapping("loginui/login3") 21 public view forward2(){ 22 //执行完forward2方法之后返回的视图 23 return new view("/login3.jsp"); 24 } 25 }运行结果如下所示:
4.2、复杂测试
编写用于处理用户登录请求的controller,代码如下:
1 package me.gacl.web.controller; 2 3 import java.io.ioexception; 4 import javax.servlet.http.httpservletrequest; 5 import javax.servlet.http.httpservletresponse; 6 import me.gacl.annotation.controller; 7 import me.gacl.annotation.requestmapping; 8 import me.gacl.web.context.webcontext; 9 import me.gacl.web.view.view; 10 import me.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 22 public class loginservlet2 { 23 24 /** 25 * @method: loginhandle 26 * @description:处理以普通方式提交的请求 27 * @anthor:孤傲苍狼 28 * 29 * @return view 30 */ 31 //使用requestmapping注解标注loginhandle方法,指明loginhandle方法的访问路径是login/handle 32 @requestmapping("login/handle") 33 public view loginhandle(){ 34 //创建一个viewdata对象,用于存储需要发送到客户端的响应数据 35 viewdata viewdata = new viewdata(); 36 //通过webcontext类获取当前线程中的httpservletrequest对象 37 httpservletrequest request = webcontext.requesthodler.get(); 38 //接收提交上来的参数 39 string username =request.getparameter("usename"); 40 string pwd = request.getparameter("pwd"); 41 if (username.equals("gacl") && pwd.equals("xdp")) { 42 request.getsession().setattribute("usename", username); 43 //将响应数据存储到viewdata对象中 44 viewdata.put("msg", "欢迎您!" username); 45 //返回一个view对象,指明要跳转的视图的路径 46 return new view("/index.jsp"); 47 }else { 48 //将响应数据存储到viewdata对象中 49 viewdata.put("msg", "登录失败,请检查用户名和密码是否正确!"); 50 //返回一个view对象,指明要跳转的视图的路径 51 return new view("/login2.jsp"); 52 } 53 } 54 55 /** 56 * @method: ajaxloginhandle 57 * @description: 处理以ajax方式提交的请求 58 * @anthor:孤傲苍狼 59 * 60 * @throws ioexception 61 */ 62 //使用requestmapping注解标注ajaxloginhandle方法,指明ajaxloginhandle方法的访问路径是ajaxlogin/handle 63 @requestmapping("ajaxlogin/handle") 64 public void ajaxloginhandle() throws ioexception{ 65 //通过webcontext类获取当前线程中的httpservletrequest对象 66 httpservletrequest request = webcontext.requesthodler.get(); 67 //接收提交上来的参数 68 string username =request.getparameter("usename"); 69 string pwd = request.getparameter("pwd"); 70 //通过webcontext类获取当前线程中的httpservletresponse对象 71 httpservletresponse response = webcontext.responsehodler.get(); 72 if (username.equals("gacl") && pwd.equals("xdp")) { 73 request.getsession().setattribute("usename", username); 74 response.getwriter().write("success"); 75 }else { 76 response.getwriter().write("fail"); 77 } 78 } 79 }编写用于测试的jsp页面,代码如下所示:
login2.jsp登录页面
1 <%@ page language="java" pageencoding="utf-8"%> 2 doctype html> 3 <html> 4 <head> 5 <title>login2.jsp登录页面title> 6 head> 7 8 <body> 9 <fieldset> 10 <legend>用户登录legend> 11 <form action="${pagecontext.request.contextpath}/login/handle.do" method="post"> 12 用户名:<input type="text" value="${param.usename}" name="usename"> 13 <br/> 14 密码:<input type="text" value="${param.pwd}" name="pwd"> 15 <br/> 16 <input type="submit" value="登录"/> 17 form> 18 fieldset> 19 <hr/> 20 <label style="color: red;">${msg}label> 21 body> 22 html>login3.jsp登录页面
1 <%@ page language="java" pageencoding="utf-8"%> 2 doctype html> 3 <html> 4 <head> 5 <title>login3登录页面title> 6 <script type="text/javascript" src="${pagecontext.request.contextpath}/ajaxutil.js">script> 7 <script type="text/javascript" src="${pagecontext.request.contextpath}/js/utils.js">script> 8 <script type="text/javascript"> 9 10 function login(){ 11 ajax.request({ 12 url : "${pagecontext.request.contextpath}/ajaxlogin/handle.do", 13 data : { 14 "usename" : document.getelementbyid("usename").value, 15 "pwd" : document.getelementbyid("pwd").value 16 }, 17 success : function(xhr) { 18 ondata(xhr.responsetext); 19 }, 20 error : function(xhr) { 21 22 } 23 }); 24 } 25 26 function ondata(responsetext){ 27 if(responsetext=="success"){ 28 //window.location.href="index.jsp";//改变url地址 29 /* 30 window.location.replace("url"):将地址替换成新url, 31 该方法通过指定url替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后, 32 你不能通过“前进”和“后 退”来访问已经被替换的url,这个特点对于做一些过渡页面非常有用! 33 */ 34 location.replace(g_basepath"/index.jsp"); 35 }else{ 36 alert("用户名和密码错误"); 37 } 38 } 39 script> 40 head> 41 42 <body> 43 <fieldset> 44 <legend>用户登录legend> 45 <form> 46 用户名:<input type="text" name="usename" id="usename"> 47 <br/> 48 密码:<input type="text" name="pwd" id="pwd"> 49 <br/> 50 <input type="button" value="登录" onclick="login()"/> 51 form> 52 fieldset> 53 body> 54 html>index.jsp页面代码如下:
1 <%@ page language="java" import="java.util.*" pageencoding="utf-8"%> 2 3 doctype html> 4 <html> 5 <head> 6 <title>凯发ag旗舰厅登录网址下载首页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 4 var contextpath = getcontextpath(); 5 //获取basepath 6 var basepath = getbasepath(); 7 //将获取到contextpath和basepath分别赋值给window对象的g_contextpath属性和g_basepath属性 8 window.g_contextpath = contextpath; 9 window.g_basepath = basepath; 10 })(); 11 12 /** 13 * @author 孤傲苍狼 14 * 获得项目根路径,等价于jsp页面中 15 * <% 16 string basepath = request.getscheme() "://" request.getservername() ":" request.getserverport() path "/"; 17 %> 18 * 使用方法:getbasepath(); 19 * @returns 项目的根路径 20 * 21 */ 22 function getbasepath() { 23 var curwwwpath = window.document.location.href; 24 var pathname = window.document.location.pathname; 25 var pos = curwwwpath.indexof(pathname); 26 var localhostpath = curwwwpath.substring(0, pos); 27 var projectname = pathname.substring(0, pathname.substr(1).indexof('/') 1); 28 return (localhostpath projectname); 29 } 30 31 /** 32 * @author 孤傲苍狼 33 * 获取web应用的contextpath,等价于jsp页面中 34 * <% 35 string path = request.getcontextpath(); 36 %> 37 * 使用方法:getcontextpath(); 38 * @returns /项目名称(/easyuistudy_20141104) 39 */ 40 function getcontextpath() { 41 return window.document.location.pathname.substring(0, window.document.location.pathname.indexof('\/', 1)); 42 };测试结果如下:
以上就是对spring mvc的简单模拟。
与50位技术专家面对面20年技术见证,附赠技术全景图总结
以上是凯发ag旗舰厅登录网址下载为你收集整理的javaweb学习总结(四十九)——简单模拟sping mvc的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得凯发ag旗舰厅登录网址下载网站内容还不错,欢迎将凯发ag旗舰厅登录网址下载推荐给好友。
- 上一篇: android 截图并保存到相册
- 下一篇: