- 浏览: 1047943 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (1441)
- 软件思想&演讲 (9)
- 行业常识 (250)
- 时时疑问 (5)
- java/guava/python/php/ruby/R/scala/groovy (213)
- struct/spring/springmvc (37)
- mybatis/hibernate/JPA (10)
- mysql/oracle/sqlserver/db2/mongdb/redis/neo4j/GreenPlum/Teradata/hsqldb/Derby/sakila (268)
- js/jquery/jqueryUi/jqueryEaseyUI/extjs/angulrJs/react/es6/grunt/zepto/raphael (81)
- ZMQ/RabbitMQ/ActiveMQ/JMS/kafka (17)
- lucene/solr/nuth/elasticsearch/MG4J (167)
- html/css/ionic/nodejs/bootstrap (19)
- Linux/shell/centos (56)
- cvs/svn/git/sourceTree/gradle/ant/maven/mantis/docker/Kubernetes (26)
- sonatype nexus (1)
- tomcat/jetty/netty/jboss (9)
- 工具 (17)
- ETL/SPASS/MATLAB/RapidMiner/weka/kettle/DataX/Kylin (11)
- hadoop/spark/Hbase/Hive/pig/Zookeeper/HAWQ/cloudera/Impala/Oozie (190)
- ios/swift/android (9)
- 机器学习&算法&大数据 (18)
- Mesos是Apache下的开源分布式资源管理框架 (1)
- echarts/d3/highCharts/tableau (1)
- 行业技能图谱 (1)
- 大数据可视化 (2)
- tornado/ansible/twisted (2)
- Nagios/Cacti/Zabbix (0)
- eclipse/intellijIDEA/webstorm (5)
- cvs/svn/git/sourceTree/gradle/jira/bitbucket (4)
- jsp/jsf/flex/ZKoss (0)
- 测试技术 (2)
- splunk/flunm (2)
- 高并发/大数据量 (1)
- freemarker/vector/thymeleaf (1)
- docker/Kubernetes (2)
- dubbo/ESB/dubboX/wso2 (2)
最新评论
在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 * <p>ClassName: AnnotationHandleServlet<p>
23 * <p>Description: AnnotationHandleServlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求<p>
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.getUrl()).forward(request, response);
82 }else if(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
83 //使用客户端跳转方式
84 response.sendRedirect(request.getContextPath()+view.getUrl());
85 }else{
86 request.getRequestDispatcher(view.getUrl()).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 //如果配置了多个包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
111 if (basePackage.indexOf(",")>0) {
112 //按逗号进行分隔
113 String[] packageNameArr = basePackage.split(",");
114 for (String packageName : packageNameArr) {
115 initRequestMapingMap(packageName);
116 }
117 }else {
118 initRequestMapingMap(basePackage);
119 }
120 System.out.println("----初始化结束---");
121 }
122
123 /**
124 * @Method: initRequestMapingMap
125 * @Description:添加使用了Controller注解的Class到RequestMapingMap中
126 * @Anthor:孤傲苍狼
127 * @param packageName
128 */
129 private void initRequestMapingMap(String packageName){
130 Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
131 for (Class<?> clazz :setClasses) {
132 if (clazz.isAnnotationPresent(Controller.class)) {
133 Method [] methods = BeanUtils.findDeclaredMethods(clazz);
134 for(Method m:methods){//循环方法,找匹配的方法进行执行
135 if(m.isAnnotationPresent(RequestMapping.class)){
136 String anoPath = m.getAnnotation(RequestMapping.class).value();
137 if(anoPath!=null && !"".equals(anoPath.trim())){
138 if (RequestMapingMap.getRequesetMap().containsKey(anoPath)) {
139 throw new RuntimeException("RequestMapping映射的地址不允许重复!");
140 }
141 RequestMapingMap.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工具类主要是用来处理一些反射的操作
复制代码
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 * @param <T>
17 * @param clazz Person.class
18 * @return
19 */
20 public static <T> T instanceClass(Class<T> clazz){
21 if(!clazz.isInterface()){
22 try {
23 return clazz.newInstance();
24 } catch (InstantiationException e) {
25 e.printStackTrace();
26 } catch (IllegalAccessException e) {
27 e.printStackTrace();
28 }
29 }
30 return null;
31 }
32
33 /**
34 * 通过构造函数实例化
35 * @param <T>
36 * @param ctor
37 * @param args
38 * @return
39 * @throws IllegalArgumentException
40 * @throws InstantiationException
41 * @throws IllegalAccessException
42 * @throws InvocationTargetException
43 */
44 public static <T> T instanceClass(Constructor<T> ctor, Object... args)
45 throws IllegalArgumentException, InstantiationException,
46 IllegalAccessException, InvocationTargetException{
47 makeAccessible(ctor);
48 return ctor.newInstance(args);//调用构造方法实例化
49 }
50
51 /**
52 * 查找某个class的方法
53 * @param clazz
54 * @param methodName
55 * @param paramTypes
56 * @return
57 * @throws SecurityException
58 * @throws NoSuchMethodException
59 */
60 public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes){
61 try {
62 return clazz.getMethod(methodName, paramTypes);
63 } catch (NoSuchMethodException e) {
64 return findDeclaredMethod(clazz, methodName, paramTypes);//返回共有的方法
65 }
66 }
67
68 public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes){
69 try {
70 return clazz.getDeclaredMethod(methodName, paramTypes);
71 }
72 catch (NoSuchMethodException ex) {
73 if (clazz.getSuperclass() != null) {
74 return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
75 }
76 return null;
77 }
78 }
79
80 public static Method [] findDeclaredMethods(Class<?> clazz){
81 return clazz.getDeclaredMethods();
82 }
83
84 public static void makeAccessible(Constructor<?> ctor) {
85 if ((!Modifier.isPublic(ctor.getModifiers())
86 || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
87 && !ctor.isAccessible()) {
88 ctor.setAccessible(true);//如果是私有的 设置为true 使其可以访问
89 }
90 }
91
92 public static Field[] findDeclaredFields(Class<?> clazz){
93 return clazz.getDeclaredFields();
94 }
95 }
复制代码
3.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 Map<String, Class<?>> requesetMap = new HashMap<String, Class<?>>();
20
21 public static Class<?> getClassName(String path) {
22 return requesetMap.get(path);
23 }
24
25 public static void put(String path, Class<?> className) {
26 requesetMap.put(path, className);
27 }
28
29 public static Map<String, Class<?>> getRequesetMap() {
30 return requesetMap;
31 }
32 }
复制代码
3.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 Set<Class<?>> getClasses(String pack) {
31
32 // 第一个class类的集合
33 Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
34 // 是否循环迭代
35 boolean recursive = true;
36 // 获取包的名字 并进行替换
37 String packageName = pack;
38 String packageDirName = packageName.replace('.', '/');
39 // 定义一个枚举的集合 并进行循环来处理这个目录下的things
40 Enumeration<URL> dirs;
41 try {
42 dirs = Thread.currentThread().getContextClassLoader().getResources(
43 packageDirName);
44 // 循环迭代下去
45 while (dirs.hasMoreElements()) {
46 // 获取下一个元素
47 URL url = dirs.nextElement();
48 // 得到协议的名称
49 String protocol = url.getProtocol();
50 // 如果是以文件的形式保存在服务器上
51 if ("file".equals(protocol)) {
52 System.err.println("file类型的扫描");
53 // 获取包的物理路径
54 String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
55 // 以文件的方式扫描整个包下的文件 并添加到集合中
56 findAndAddClassesInPackageByFile(packageName, filePath,
57 recursive, classes);
58 } else if ("jar".equals(protocol)) {
59 // 如果是jar包文件
60 // 定义一个JarFile
61 System.err.println("jar类型的扫描");
62 JarFile jar;
63 try {
64 // 获取jar
65 jar = ((JarURLConnection) url.openConnection())
66 .getJarFile();
67 // 从此jar包 得到一个枚举类
68 Enumeration<JarEntry> entries = jar.entries();
69 // 同样的进行循环迭代
70 while (entries.hasMoreElements()) {
71 // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
72 JarEntry entry = entries.nextElement();
73 String name = entry.getName();
74 // 如果是以/开头的
75 if (name.charAt(0) == '/') {
76 // 获取后面的字符串
77 name = name.substring(1);
78 }
79 // 如果前半部分和定义的包名相同
80 if (name.startsWith(packageDirName)) {
81 int idx = name.lastIndexOf('/');
82 // 如果以"/"结尾 是一个包
83 if (idx != -1) {
84 // 获取包名 把"/"替换成"."
85 packageName = name.substring(0, idx)
86 .replace('/', '.');
87 }
88 // 如果可以迭代下去 并且是一个包
89 if ((idx != -1) || recursive) {
90 // 如果是一个.class文件 而且不是目录
91 if (name.endsWith(".class")
92 && !entry.isDirectory()) {
93 // 去掉后面的".class" 获取真正的类名
94 String className = name.substring(
95 packageName.length() + 1, name
96 .length() - 6);
97 try {
98 // 添加到classes
99 classes.add(Class
100 .forName(packageName + '.'
101 + className));
102 } catch (ClassNotFoundException e) {
103 // log
104 // .error("添加用户自定义视图类错误 找不到此类的.class文件");
105 e.printStackTrace();
106 }
107 }
108 }
109 }
110 }
111 } catch (IOException e) {
112 // log.error("在扫描用户定义视图时从jar包获取文件出错");
113 e.printStackTrace();
114 }
115 }
116 }
117 } catch (IOException e) {
118 e.printStackTrace();
119 }
120
121 return classes;
122 }
123
124 /**
125 * 以文件的形式来获取包下的所有Class
126 *
127 * @param packageName
128 * @param packagePath
129 * @param recursive
130 * @param classes
131 */
132 public static void findAndAddClassesInPackageByFile(String packageName,
133 String packagePath, final boolean recursive, Set<Class<?>> classes) {
134 // 获取此包的目录 建立一个File
135 File dir = new File(packagePath);
136 // 如果不存在或者 也不是目录就直接返回
137 if (!dir.exists() || !dir.isDirectory()) {
138 // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
139 return;
140 }
141 // 如果存在 就获取包下的所有文件 包括目录
142 File[] dirfiles = dir.listFiles(new FileFilter() {
143 // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
144 public boolean accept(File file) {
145 return (recursive && file.isDirectory())
146 || (file.getName().endsWith(".class"));
147 }
148 });
149 // 循环所有文件
150 for (File file : dirfiles) {
151 // 如果是目录 则继续扫描
152 if (file.isDirectory()) {
153 findAndAddClassesInPackageByFile(packageName + "."
154 + file.getName(), file.getAbsolutePath(), recursive,
155 classes);
156 } else {
157 // 如果是java类文件 去掉后面的.class 只留下类名
158 String className = file.getName().substring(0,
159 file.getName().length() - 6);
160 try {
161 // 添加到集合中去
162 //classes.add(Class.forName(packageName + '.' + className));
163 //经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
164 classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
165 } catch (ClassNotFoundException e) {
166 // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
167 e.printStackTrace();
168 }
169 }
170 }
171 }
172 }
复制代码
3.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 ThreadLocal<HttpServletRequest> requestHodler = new ThreadLocal<HttpServletRequest>();
15 public static ThreadLocal<HttpServletResponse> responseHodler = new ThreadLocal<HttpServletResponse>();
16
17 public HttpServletRequest getRequest(){
18 return requestHodler.get();
19 }
20
21 public HttpSession getSession(){
22 return requestHodler.get().getSession();
23 }
24
25 public ServletContext getServletContext(){
26 return requestHodler.get().getSession().getServletContext();
27 }
28
29 public HttpServletResponse getResponse(){
30 return responseHodler.get();
31 }
32 }
复制代码
3.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 getUrl() {
32 return url;
33 }
34
35
36 public void setUrl(String url) {
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>首页</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 };
复制代码
复制代码
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 * <p>ClassName: AnnotationHandleServlet<p>
23 * <p>Description: AnnotationHandleServlet作为自定义注解的核心处理器以及负责调用目标业务方法处理用户请求<p>
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.getUrl()).forward(request, response);
82 }else if(view.getDispathAction().equals(DispatchActionConstant.REDIRECT)){
83 //使用客户端跳转方式
84 response.sendRedirect(request.getContextPath()+view.getUrl());
85 }else{
86 request.getRequestDispatcher(view.getUrl()).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 //如果配置了多个包,例如:<param-value>me.gacl.web.controller,me.gacl.web.UI</param-value>
111 if (basePackage.indexOf(",")>0) {
112 //按逗号进行分隔
113 String[] packageNameArr = basePackage.split(",");
114 for (String packageName : packageNameArr) {
115 initRequestMapingMap(packageName);
116 }
117 }else {
118 initRequestMapingMap(basePackage);
119 }
120 System.out.println("----初始化结束---");
121 }
122
123 /**
124 * @Method: initRequestMapingMap
125 * @Description:添加使用了Controller注解的Class到RequestMapingMap中
126 * @Anthor:孤傲苍狼
127 * @param packageName
128 */
129 private void initRequestMapingMap(String packageName){
130 Set<Class<?>> setClasses = ScanClassUtil.getClasses(packageName);
131 for (Class<?> clazz :setClasses) {
132 if (clazz.isAnnotationPresent(Controller.class)) {
133 Method [] methods = BeanUtils.findDeclaredMethods(clazz);
134 for(Method m:methods){//循环方法,找匹配的方法进行执行
135 if(m.isAnnotationPresent(RequestMapping.class)){
136 String anoPath = m.getAnnotation(RequestMapping.class).value();
137 if(anoPath!=null && !"".equals(anoPath.trim())){
138 if (RequestMapingMap.getRequesetMap().containsKey(anoPath)) {
139 throw new RuntimeException("RequestMapping映射的地址不允许重复!");
140 }
141 RequestMapingMap.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工具类主要是用来处理一些反射的操作
复制代码
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 * @param <T>
17 * @param clazz Person.class
18 * @return
19 */
20 public static <T> T instanceClass(Class<T> clazz){
21 if(!clazz.isInterface()){
22 try {
23 return clazz.newInstance();
24 } catch (InstantiationException e) {
25 e.printStackTrace();
26 } catch (IllegalAccessException e) {
27 e.printStackTrace();
28 }
29 }
30 return null;
31 }
32
33 /**
34 * 通过构造函数实例化
35 * @param <T>
36 * @param ctor
37 * @param args
38 * @return
39 * @throws IllegalArgumentException
40 * @throws InstantiationException
41 * @throws IllegalAccessException
42 * @throws InvocationTargetException
43 */
44 public static <T> T instanceClass(Constructor<T> ctor, Object... args)
45 throws IllegalArgumentException, InstantiationException,
46 IllegalAccessException, InvocationTargetException{
47 makeAccessible(ctor);
48 return ctor.newInstance(args);//调用构造方法实例化
49 }
50
51 /**
52 * 查找某个class的方法
53 * @param clazz
54 * @param methodName
55 * @param paramTypes
56 * @return
57 * @throws SecurityException
58 * @throws NoSuchMethodException
59 */
60 public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes){
61 try {
62 return clazz.getMethod(methodName, paramTypes);
63 } catch (NoSuchMethodException e) {
64 return findDeclaredMethod(clazz, methodName, paramTypes);//返回共有的方法
65 }
66 }
67
68 public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes){
69 try {
70 return clazz.getDeclaredMethod(methodName, paramTypes);
71 }
72 catch (NoSuchMethodException ex) {
73 if (clazz.getSuperclass() != null) {
74 return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
75 }
76 return null;
77 }
78 }
79
80 public static Method [] findDeclaredMethods(Class<?> clazz){
81 return clazz.getDeclaredMethods();
82 }
83
84 public static void makeAccessible(Constructor<?> ctor) {
85 if ((!Modifier.isPublic(ctor.getModifiers())
86 || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
87 && !ctor.isAccessible()) {
88 ctor.setAccessible(true);//如果是私有的 设置为true 使其可以访问
89 }
90 }
91
92 public static Field[] findDeclaredFields(Class<?> clazz){
93 return clazz.getDeclaredFields();
94 }
95 }
复制代码
3.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 Map<String, Class<?>> requesetMap = new HashMap<String, Class<?>>();
20
21 public static Class<?> getClassName(String path) {
22 return requesetMap.get(path);
23 }
24
25 public static void put(String path, Class<?> className) {
26 requesetMap.put(path, className);
27 }
28
29 public static Map<String, Class<?>> getRequesetMap() {
30 return requesetMap;
31 }
32 }
复制代码
3.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 Set<Class<?>> getClasses(String pack) {
31
32 // 第一个class类的集合
33 Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
34 // 是否循环迭代
35 boolean recursive = true;
36 // 获取包的名字 并进行替换
37 String packageName = pack;
38 String packageDirName = packageName.replace('.', '/');
39 // 定义一个枚举的集合 并进行循环来处理这个目录下的things
40 Enumeration<URL> dirs;
41 try {
42 dirs = Thread.currentThread().getContextClassLoader().getResources(
43 packageDirName);
44 // 循环迭代下去
45 while (dirs.hasMoreElements()) {
46 // 获取下一个元素
47 URL url = dirs.nextElement();
48 // 得到协议的名称
49 String protocol = url.getProtocol();
50 // 如果是以文件的形式保存在服务器上
51 if ("file".equals(protocol)) {
52 System.err.println("file类型的扫描");
53 // 获取包的物理路径
54 String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
55 // 以文件的方式扫描整个包下的文件 并添加到集合中
56 findAndAddClassesInPackageByFile(packageName, filePath,
57 recursive, classes);
58 } else if ("jar".equals(protocol)) {
59 // 如果是jar包文件
60 // 定义一个JarFile
61 System.err.println("jar类型的扫描");
62 JarFile jar;
63 try {
64 // 获取jar
65 jar = ((JarURLConnection) url.openConnection())
66 .getJarFile();
67 // 从此jar包 得到一个枚举类
68 Enumeration<JarEntry> entries = jar.entries();
69 // 同样的进行循环迭代
70 while (entries.hasMoreElements()) {
71 // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
72 JarEntry entry = entries.nextElement();
73 String name = entry.getName();
74 // 如果是以/开头的
75 if (name.charAt(0) == '/') {
76 // 获取后面的字符串
77 name = name.substring(1);
78 }
79 // 如果前半部分和定义的包名相同
80 if (name.startsWith(packageDirName)) {
81 int idx = name.lastIndexOf('/');
82 // 如果以"/"结尾 是一个包
83 if (idx != -1) {
84 // 获取包名 把"/"替换成"."
85 packageName = name.substring(0, idx)
86 .replace('/', '.');
87 }
88 // 如果可以迭代下去 并且是一个包
89 if ((idx != -1) || recursive) {
90 // 如果是一个.class文件 而且不是目录
91 if (name.endsWith(".class")
92 && !entry.isDirectory()) {
93 // 去掉后面的".class" 获取真正的类名
94 String className = name.substring(
95 packageName.length() + 1, name
96 .length() - 6);
97 try {
98 // 添加到classes
99 classes.add(Class
100 .forName(packageName + '.'
101 + className));
102 } catch (ClassNotFoundException e) {
103 // log
104 // .error("添加用户自定义视图类错误 找不到此类的.class文件");
105 e.printStackTrace();
106 }
107 }
108 }
109 }
110 }
111 } catch (IOException e) {
112 // log.error("在扫描用户定义视图时从jar包获取文件出错");
113 e.printStackTrace();
114 }
115 }
116 }
117 } catch (IOException e) {
118 e.printStackTrace();
119 }
120
121 return classes;
122 }
123
124 /**
125 * 以文件的形式来获取包下的所有Class
126 *
127 * @param packageName
128 * @param packagePath
129 * @param recursive
130 * @param classes
131 */
132 public static void findAndAddClassesInPackageByFile(String packageName,
133 String packagePath, final boolean recursive, Set<Class<?>> classes) {
134 // 获取此包的目录 建立一个File
135 File dir = new File(packagePath);
136 // 如果不存在或者 也不是目录就直接返回
137 if (!dir.exists() || !dir.isDirectory()) {
138 // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
139 return;
140 }
141 // 如果存在 就获取包下的所有文件 包括目录
142 File[] dirfiles = dir.listFiles(new FileFilter() {
143 // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
144 public boolean accept(File file) {
145 return (recursive && file.isDirectory())
146 || (file.getName().endsWith(".class"));
147 }
148 });
149 // 循环所有文件
150 for (File file : dirfiles) {
151 // 如果是目录 则继续扫描
152 if (file.isDirectory()) {
153 findAndAddClassesInPackageByFile(packageName + "."
154 + file.getName(), file.getAbsolutePath(), recursive,
155 classes);
156 } else {
157 // 如果是java类文件 去掉后面的.class 只留下类名
158 String className = file.getName().substring(0,
159 file.getName().length() - 6);
160 try {
161 // 添加到集合中去
162 //classes.add(Class.forName(packageName + '.' + className));
163 //经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
164 classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
165 } catch (ClassNotFoundException e) {
166 // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
167 e.printStackTrace();
168 }
169 }
170 }
171 }
172 }
复制代码
3.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 ThreadLocal<HttpServletRequest> requestHodler = new ThreadLocal<HttpServletRequest>();
15 public static ThreadLocal<HttpServletResponse> responseHodler = new ThreadLocal<HttpServletResponse>();
16
17 public HttpServletRequest getRequest(){
18 return requestHodler.get();
19 }
20
21 public HttpSession getSession(){
22 return requestHodler.get().getSession();
23 }
24
25 public ServletContext getServletContext(){
26 return requestHodler.get().getSession().getServletContext();
27 }
28
29 public HttpServletResponse getResponse(){
30 return responseHodler.get();
31 }
32 }
复制代码
3.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 getUrl() {
32 return url;
33 }
34
35
36 public void setUrl(String url) {
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>首页</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 Boot
2017-09-26 09:51 287Spring Boot是由Pivotal团 ... -
spring AOP中切点
2017-08-25 09:59 901在spring AOP中,需要使用AspectJ的切点表达式 ... -
Spring JdbcTemplate详解
2017-07-19 16:12 588JdbcTemplate简介 Spring对数据库的操 ... -
Spring相关
2017-04-20 16:10 455我是一个Java开发者,之前知道Spring属于这个公司,就 ... -
spring.schemas、spring.handlers的使用
2017-02-28 13:55 1720报错信息:Configuration problem: Un ... -
Spring的一个命名空间的名称空间处理程序没有找到
2017-02-25 15:20 9411. 问题 本文将讨论Spring中最常见的配置问题 —— ... -
到底EJB是什么
2016-12-06 10:05 445到底EJB是什么?被口口相传的神神秘秘的,百度一番,总觉得 ... -
Spring MVC 和 Servlet 一样,都不是线程安全的
2016-04-28 01:06 675你的理解是对的,Spring MVC 和 Servlet 一 ... -
springmvc的control 的线程是否安全的问题
2016-05-31 10:09 357关于java Servlet,Struts,springM ... -
框架的一些学习
2016-02-03 14:53 257java aopalliance-1.0.jar这个包是AOP ... -
Metrics介绍和Spring的集成
2016-02-02 16:56 969Metrics可以为你的代码的运行提供无与伦 ... -
spring mvc常用的注解
2016-01-22 14:28 701spring mvc常用的注解: ... -
Spring的常用注解
2016-01-20 16:07 654Spring2.5引入注解式处理器 @Controlle ... -
通过Spring的配置,添加多个数据源,制作多个qlMapClient,iBatis或Hibernate的各个DAO
2016-12-27 10:25 513通过Spring的配置,添加多个数据源,制作多个qlMapCl ... -
springmvc避免IE执行AJAX时,返回JSON出现下载文件
2017-01-01 23:35 862<!-- 避免IE执行AJAX时,返回JSON出现下载文 ... -
springMVC的@ResponseBody出现乱码解决方法
2017-01-02 00:23 376使用@ResponseBody出现乱码解决方法 1、 Re ... -
Spring中的Bean的注入方式
2017-01-02 00:27 535一 setter方法注入 配置文件如下: <bean ... -
spring发送邮件配置文件
2017-01-02 00:27 12701、发送邮件配置文件springmail_config ... -
@Resource和@Autowire的区别
2017-01-02 00:27 698@Resource和@Autowire的区别 在java代码中 ... -
Spring中继承并简化了Quartz
2017-01-02 00:27 666Quartz是一个强大的企业级任务调度框架,Spring中继承 ...
相关推荐
Spring、SpringMVC和Mybatis是Java开发中最常用的三大开源框架,它们的整合使用,通常被称为SSM框架。这个框架组合提供了完整的后端服务解决方案,包括依赖注入(DI)、面向切面编程(AOP)、模型-视图-控制器(MVC...
SpringMVC是Spring框架的一个模块,专为构建Web应用程序提供模型-视图-控制器(MVC)架构。这个“springMVC练手代码”压缩包包含的资源可以帮助初学者或开发者深入了解并实践SpringMVC的基本操作和核心概念。 首先...
SpringMVC是一款强大的Java web开发框架,用于构建高效、可维护的Web应用程序。在这个"SpringMVC demo 完整源码实例下载"中,我们能够深入理解并学习SpringMVC的核心概念和实际应用。 首先,SpringMVC是Spring框架...
【狂神SpringMVC配套课程代码】一共8个模块,大概可以看我的博客,都是自己学配套整理的 欢迎大家作为学习SpringMVC的参考!! 下面附上狂神B站课程网址,和我的博客笔记(共8章) 狂神老师B站课程:...
SpringMVC是一个强大的Java Web开发框架,由Spring社区开发,它是Spring生态系统的重要组成部分,主要用于构建后端服务。SpringMVC以其灵活的配置、高度模块化和优秀的性能深受开发者喜爱。在这个"springmvc实战项目...
### SpringMVC基础知识详解 #### 一、SpringMVC简介 **SpringMVC**是Spring框架中的一个重要组成部分,主要用于Web应用程序的开发。它遵循MVC(Model-View-Controller)设计模式,帮助开发者构建清晰、可维护的Web...
SpringMVC 是一款基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的重要组成部分,主要用于构建 MVC(Model-View-Controller)模式的 Web 应用程序。本教程将深入探讨 SpringMVC 的核心概念、配置以及实际应用。...
SpringMVC和SQLiteJDBC是两个在Java开发中常见的组件,它们分别用于构建Web应用程序和服务端数据存储。这里我们详细探讨这两个技术以及它们如何协同工作。 **SpringMVC** SpringMVC是Spring框架的一个模块,专门...
本教程将详细阐述如何使用四个关键组件——Maven、SpringMVC、MyBatis和Log4j——来搭建一个强大的Web应用框架,旨在提高开发效率并优化项目管理。 **Maven** 是一个流行的项目管理和综合工具,它通过统一的构建...
【SpringMVC】 SpringMVC是Spring框架的一部分,它是一个用于构建Web应用程序的轻量级、模型-视图-控制器(MVC)架构。SpringMVC通过将业务逻辑、控制逻辑和显示逻辑分离,提高了代码的可维护性和可测试性。在...
SpringMVC、Hibernate和Spring是Java开发中三大核心框架,它们各自负责应用程序的不同层面:SpringMVC用于处理HTTP请求和响应,Hibernate则是持久层框架,负责数据库操作,而Spring作为全能容器,提供依赖注入和面向...
SpringMVC和MyBatis是Java Web开发中的两个核心框架,它们在构建高效、模块化的应用程序方面发挥着重要作用。SpringMVC是Spring框架的一部分,主要负责处理HTTP请求和响应,而MyBatis则是一个轻量级的持久层框架,...
**SpringMVC 入门小程序详解** SpringMVC是Spring框架的一个重要模块,它是一个用于构建Web应用程序的轻量级、模型-视图-控制器(MVC)框架。本入门程序旨在帮助初学者理解并掌握SpringMVC的基本概念和工作流程,...
SpringMVC和Mybatis是Java开发中非常流行的两个框架,它们在企业级Web应用开发中起着关键作用。SpringMVC作为Spring框架的一部分,主要负责处理HTTP请求和响应,而Mybatis则是一个轻量级的持久层框架,专注于数据库...
SpringMVC 拦截器项目是一个典型的 Web 应用开发示例,它利用 SpringMVC 框架中的拦截器(Interceptor)机制来实现特定的功能,如权限控制、日志记录、性能统计等。SpringMVC 是 Spring 框架的一部分,专为构建基于 ...
《尚硅谷SpringMVC部分全套教学文档笔记》涵盖了SpringMVC框架的核心概念和技术,通过一系列章节深入浅出地讲解了SpringMVC的各个方面。以下是基于这些文档内容的详细知识点总结: 1. **SpringMVC概述与HelloWorld*...
Java基于Spring+SpringMVC+MyBatis实现的学生信息管理系统源码,SSM+Vue的学生管理系统。 Java基于Spring+SpringMVC+MyBatis实现的学生信息管理系统源码,SSM+Vue的学生管理系统。 Java基于Spring+SpringMVC+...
SpringMVC是Spring框架的一部分,专门用于构建Web应用程序的模型-视图-控制器(MVC)架构。在本文中,我们将深入探讨SpringMVC 5.0版本的关键特性、使用方法以及它如何增强Web开发的效率。 首先,SpringMVC 5.0是...
在本项目中,我们主要探讨的是如何将SpringMVC、MyBatis、PostgreSQL数据库以及Maven构建工具进行有效的整合,以实现一个高效且模块化的Web应用开发环境。以下是关于这些技术及其整合的关键知识点的详细说明: **1....