0%

Spring MVC系列-(8) Spring MVC 流程

Spring.png

8. Spring MVC 流程

8.1 Spring MVC 介绍

Spring MVC 是目前主流的MVC框架之一。两个核心点:

  • 处理器映射:选择使用哪个控制器来处理请求
  • 视图解析器:选择结果应该如何渲染

运行原理可以用下图表示:

160870009-5aae79b0723bb_articlex.png

  1. 首先用户发送请求,DispatcherServlet实现了Servlet接口,整个请求处理流:HttpServlet.service

-> FrameworkServlet.doGet
-> FrameworkServlet.processRequest
-> DispatcherServlet.doService
-> DispatcherServlet.doDispatch。
-> doDispatch(HttpServletRequest request, HttpServletResponse response)方法
即为整个spring mvc的处理流程。

  1. 获取url请求对应的处理方法,遍历handlerMappings列表,获取对象HandlerExecutionChain(包含一个处理器 handler 如HandlerMethod 对象、多个 HandlerInterceptor 拦截器对象)。此处的handlerMappings列表为上下文中所有HandlerMapping接口的实现类(如图中列举了4个),遍历handlerMappings列表,针对每个handlerMapping试图获取HandlerExecutionChain,一旦成功(不为null),即返回。

  2. 取对应的 HandlerAdapter,HandlerAdapter 将会把2中的handler包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。DispatcherServlet中的HandlerAdapter列表如图中所列的3种,依次遍历,调用HanderAdapter.supports判断是否支持。

  3. 调用Controller的具体方法处理请求,并返回一个 ModelAndView。HandlerAdapter会为每一个请求生成一个ServletInvocableHandlerMethod实例,核心方法invokeAndHandle,包括输入参数的处理和返回数据的解析。

  4. 视图解析,遍历DispatcherServlet的ViewResolver列表,获取对应的View对象,入口方法DispatcherServlet.processDispatchResult

  5. 渲染,调用5中获取的View的render方法,完成对Model数据的渲染。此处的 Model 实际是一个 Map 数据结构。

  6. DispatcherServlet 将6中渲染后的数据返回响应给用户,到此一个流程结束。

从代码角度讲,可以分为如下几步:

其中灰色标识主流程,绿色为DispathcerServlet.doDispatch方法中的流程,红色为HandlerAdapter.handler方法中的流程

392441468-5aae7a3d0f372_articlex.png


参考:

8.2 Servlet

在介绍如何手动实现Spring MVC之前,首先介绍下Servlet的相关概念,因为后续的实现Spring MVC也是基于DispatcherServle实现的。

8.2.1 Servlet的生命周期

Servlet的生命周期分为4个阶段:实例化–>初始化–>执行处理–>销毁。

(1)实例化——new:服务器第一次被访问时,加载一个Servlet容器,而且只会被加载一次。
(2)初始化——init:创建完Servlet容器后,会调用仅执行一次的init()初始化方法,用于初始化Servlet对象,无论多少台客户端在服务器运行期间访问都不会再执行init()方法。
(3)执行处理——service()方法:HttpServlet的抽象类提供了doGet()、doPost()……等方法。对应了request请求的发送方法,与之相匹配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

(4)销毁——destroy:在服务器关闭或重启时,Servlet会调用destroy方法来销毁,将Servlet容器标记为垃圾文件,让GC做回收处理。我们编写的Servlet是调用了GenericServlet抽象类的destroy方法:

8.2.2 Servlet的工作原理

1、首先简单解释一下Servlet接收和响应客户请求的过程:

客户发送一个请求,Servlet是调用service()方法对请求进行响应的,通过源代码可见,service()方法中对请求的方式进行了匹配。选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet()、doPost()等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

2、每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。

3、Servlet是单例模式,线程是不安全的,因此在service()方法中尽量不要操作全局变量。但实际上,可以通过使用session和application来代替全局变量,只是会加大服务器负载。

8.2.3 如何判断是get请求还是post请求?

8.2.4 过滤器Filter原理

生命周期:程序启动调用Filter的init()方法(永远只调用一次),程序停止调用Filter的destroy()方法(永远只调用一次),doFilter()方法每次的访问请求如果符合拦截条件都会调用(程序第一次运行,会在servlet调用init()方法以后调用,不管第几次,都在调用doGet(),doPost()方法之前)。

工作原理:在Servlet作为过滤器使用时,它可以对客户的请求进行处理。处理完成后,它会交给下一个过滤器处理,这样,客户的请求在过滤链里逐个处理,直到请求发送到目标为止。

例如,某网站里有提交“修改的注册信息”的网页,当用户填写完修改信息并提交后,服务器在进行处理时需要做两项工作:判断客户端的会话是否有效;对提交的数据进行统一编码。这两项工作可以在由两个过滤器组成的过滤链里进行处理。当过滤器处理成功后,把提交的数据发送到最终目标;如果过滤器处理不成功,将把视图派发到指定的错误页面。

过滤器与拦截器的不同之处:

总结: 两者的本质区别:拦截器是基于java的反射机制的,而过滤器是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。

8.3 手写Spring MVC

这部分将基于HttpServlet实现Spring MVC的整个流程,

  1. 首先是在XML中定义整个程序的入口,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<display-name>DispatcherServlet</display-name>
<description></description>
<servlet-class>com.enjoy.james.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

  1. 接着实现核心流程类DispatcherServlet,用于初始化IOC容器,同时来处理request请求。

主要的流程分为如下:

  • scanPackage函数,扫描类路径下的所有类,得到所有的类名。
  • instance函数,将上一步得到的所有类进行实例化,然后将bean实例的名称和实例化的对象保存。
  • ioc函数主要完成依赖注入,需要把service实例注入到controller类中。
  • HandlerMapping函数把controller类中的URI和对应的method保存起来,后续进行request处理时,需要找到对应method。

下面是各个函数的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

/**
* Servlet implementation class DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

List<String> classNames = new ArrayList<String>();

Map<String, Object> beans = new HashMap<String, Object>();

Map<String, Object> handlerMap = new HashMap<String, Object>();

Properties prop = null;

private static String HANDLERADAPTER = "jamesHandlerAdapter";

/**
* Default constructor.
*/
public DispatcherServlet() {
// TODO Auto-generated constructor stub
}

/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
// 1、我们要根据一个基本包进行扫描,扫描里面的子包以及子包下的类
scanPackage("com.enjoy");

for (String classname : classNames) {
System.out.println(classname);
}

// 2、我们肯定是要把扫描出来的类进行实例化
instance();
for (Map.Entry<String, Object> entry : beans.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}

// 3、依赖注入,把service层的实例注入到controller
ioc();

// 4、建立一个path与method的映射关系
HandlerMapping();
for (Map.Entry<String, Object> entry : handlerMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}

}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取到请求路径 /james-springmvc/james/query
String uri = request.getRequestURI();

String context = request.getContextPath();
//将 "/james-springmvc/james/query" 去掉"/james-springmvc"
String path = uri.replace(context, "");
//根据请求路径来获取要执行的方法
Method method = (Method) handlerMap.get(path);
//拿到控制类
JamesController instance = (JamesController) beans.get("/" + path.split("/")[1]);
//处理器
HandlerAdapterService ha = (HandlerAdapterService) beans.get(HANDLERADAPTER);


/*
* @RequestMapping("/order")
* order(@RequestBody String params, @RequestHeader @RequestParam String param1){//参数有多种类型接收方式
* }
*/




Object[] args = ha.hand(request, response, method, beans);

try {
method.invoke(instance, args);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private void HandlerMapping() {
if (beans.entrySet().size() <= 0) {
System.out.println("没有类的实例化!");
return;
}

for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();

Class<?> clazz = instance.getClass();
// 拿所有Controller的类
if (clazz.isAnnotationPresent(EnjoyController.class)) {
//@com.enjoy.james.annotation.EnjoyRequestMapping(value=/james)
EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
.getAnnotation(EnjoyRequestMapping.class);
// 获取Controller类上面的EnjoyRequestMapping注解里的请求路径
String classPath = requestMapping.value();
// 获取控制类里的所有方法
Method[] methods = clazz.getMethods();
// 获取方法上的EnjoyRequestMapping设置的路径,与方法名建立映射关系
for (Method method : methods) {
//判断哪些方法上使用EnjoyRequestMapping路径注解
if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
//@com.enjoy.james.annotation.EnjoyRequestMapping(value=/query)
EnjoyRequestMapping methodrm = (EnjoyRequestMapping) method
.getAnnotation(EnjoyRequestMapping.class);
String methodPath = methodrm.value();
// 把方法上与路径建立映射关系( /james/query--->public void com.enjoy.james.controller.JamesController.query )
handlerMap.put(classPath + methodPath, method);
} else {
continue;
}
}
}
}
}

// 初始化IOC容器
private void ioc() {

if (beans.entrySet().size() <= 0) {
System.out.println("没有类的实例化!");
return;
}
//将实例化好的bean遍历,
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();//获取bean实例

Class<?> clazz = instance.getClass();//获取类,用来判断类里声明了哪些注解(主要是针对控制类里的判断,比如使用了@Autowired @Qualifier,对这些注解进行解析)
//判断该类是否使用了EnjoyController注解
if (clazz.isAnnotationPresent(EnjoyController.class)) {
Field[] fields = clazz.getDeclaredFields();// 拿到类里面的属性
// 判断是否声明了自动装配(依赖注入)注解,比如@Autrowired @Qualifier
for (Field field : fields) {
if (field.isAnnotationPresent(EnjoyQualifier.class)) {
EnjoyQualifier qualifier = (EnjoyQualifier) field.getAnnotation(EnjoyQualifier.class);
//拿到@EnjoyQualifier("JamesServiceImpl")里的指定要注入的bean名字"JamesServiceImpl"
String value = qualifier.value();

field.setAccessible(true);
try {
// 从MAP容器中获取"JamesServiceImpl"对应的bean,并注入实例控制层bean,解决依赖注入
field.set(instance, beans.get(value));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
continue;
}
}
} else {
continue;
}
}
}

private void instance() {
if (classNames.size() <= 0) {
System.out.println("包扫描失败!");
return;
}
//遍历扫描到的class文件,将需要实例化的类(加了注解的类)进行反射创建对象(像注解就不需要实例化)
for (String className : classNames) {
// com.enjoy.james.service.impl.JamesServiceImpl.class
String cn = className.replace(".class", "");

try {
Class<?> clazz = Class.forName(cn);//拿到class类,用来实例化
// 将扫描到的类,获取类名,并判断是否标记了EnjoyController注解
if (clazz.isAnnotationPresent(EnjoyController.class)) {
EnjoyController controller = (EnjoyController) clazz.getAnnotation(EnjoyController.class);
Object instance = clazz.newInstance();
//获取对应的请求路径"/james"
EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
.getAnnotation(EnjoyRequestMapping.class);
String rmvalue = requestMapping.value();//得到"/james"请求路径
//用路径做为key,对应value为实例化对象
beans.put(rmvalue, instance);
} else if (clazz.isAnnotationPresent(EnjoyService.class)) {
//获取当前clazz类的注解(通过这个注解可得到当前service的id) @com.enjoy.james.annotation.EnjoyService(value=JamesServiceImpl)
EnjoyService service = (EnjoyService) clazz.getAnnotation(EnjoyService.class);
Object instance = clazz.newInstance();
//put(JamesServiceImpl,instance)
beans.put(service.value(), instance);
} else {
continue;
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

private void scanPackage(String basePackage) {
//扫描编译好的类路径下所有的类
URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(basePackage));

String fileStr = url.getFile();

File file = new File(fileStr);
//拿到所有类com.enjoy下的james文件夹
String[] filesStr = file.list();

for (String path : filesStr) {
File filePath = new File(fileStr + path);//扫描com.enjoy.james下的所有class类

//递归调用扫描,如果是路径,继续扫描
if (filePath.isDirectory()) {
// com.enjoy.james
scanPackage(basePackage + "." + path);
} else {
classNames.add(basePackage + "." + filePath.getName());//如果是class文件则加入List集合(待生成bean)
}
}
}

private String replaceTo(String basePackage) {
return basePackage.replaceAll("\\.", "/");
}

}

  1. doPost函数中,完成了对request的主要处理,这里的主要流程首先是拿到处理request的方法,然后从request中将arguments全部取出来,最后利用反射进行调用方法。

以下是整个项目的结构,

Screen Shot 2020-02-21 at 3.37.13 PM.png

  • annotation部分定义了各种注解,类似的实现如下:
1
2
3
4
5
6
@Target({java.lang.annotation.ElementType.TYPE}) //作用范围:用在接口或类上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//明该注解将被包含在javadoc中 @Inherited:说明子类可以继承父类中的该注解
public @interface EnjoyController {
String value() default "";
}
  • ArgumentResolver部分实现了对request参数的解析,这里使用了策略模式。
1
2
3
4
5
6
7
8
9
10
public interface ArgumentResolver {

public boolean support(Class<?> type, int paramIndex, Method method);

//参数解析方法
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class<?> type,
int paramIndex,//参数索引下坐标,有很多注解,你得知道是哪个参数的注解,每个参数的索引顺序不一样
Method method);
}

下面是不同的实现类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@EnjoyService("httpServletRequestArgumentResolver")
public class HttpServletRequestArgumentResolver implements ArgumentResolver {
//判断传进来的参数是否为request
public boolean support(Class<?> type, int paramIndex, Method method) {
return ServletRequest.class.isAssignableFrom(type);
}
////如果返回的参数是request,则直接返回
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class<?> type, int paramIndex,
Method method) {
return request;
}

}

@EnjoyService("httpServletResponseArgumentResolver")
public class HttpServletResponseArgumentResolver implements ArgumentResolver {
//判断传进来的参数是否为response
public boolean support(Class<?> type, int paramIndex, Method method) {
return ServletResponse.class.isAssignableFrom(type);
}
//如果返回的参数是response,则直接返回
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class<?> type, int paramIndex,
Method method) {
return response;
}

}

下面这是关键的处理参数的实现类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@EnjoyService("requestParamArgumentResolver")
//解析声明注解为RequestParam, 获取注解的值
public class RequestParamArgumentResolver implements ArgumentResolver {
//判断传进来的参数是否为EnjoyRequestParam
public boolean support(Class<?> type, int paramIndex, Method method) {

Annotation[][] an = method.getParameterAnnotations();

Annotation[] paramAns = an[paramIndex];

for (Annotation paramAn : paramAns) {
//判断传进的paramAn.getClass()是不是 EnjoyRequestParam 类型
if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
return true;
}
}
return false;
}
//参数解析,并获取注解的值
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class<?> type, int paramIndex,
Method method) {

Annotation[][] an = method.getParameterAnnotations();

Annotation[] paramAns = an[paramIndex];

for (Annotation paramAn : paramAns) {
if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
EnjoyRequestParam rp = (EnjoyRequestParam)paramAn;

String value = rp.value();
return request.getParameter(value);
}
}

return null;
}

}
  • Controller类是整个request的入口,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.enjoy.james.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.enjoy.james.annotation.EnjoyController;
import com.enjoy.james.annotation.EnjoyQualifier;
import com.enjoy.james.annotation.EnjoyRequestHeader;
import com.enjoy.james.annotation.EnjoyRequestMapping;
import com.enjoy.james.annotation.EnjoyRequestParam;
import com.enjoy.james.service.JamesService;

@EnjoyController
@EnjoyRequestMapping("/james")
public class JamesController {

@EnjoyQualifier("JamesServiceImpl")
private JamesService jamesService;

@EnjoyRequestMapping("/query")
public void query(HttpServletRequest request, HttpServletResponse response,
@EnjoyRequestParam("name") String name,
@EnjoyRequestParam("age") String age) {

try {
PrintWriter pw = response.getWriter();
String result = jamesService.query(name,age);
pw.write(result);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@EnjoyRequestMapping("/insert")
public void insert(HttpServletRequest request,
HttpServletResponse response) {
try {
PrintWriter pw = response.getWriter();
String result = jamesService.insert("0000");

pw.write(result);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@EnjoyRequestMapping("/update")
public void update(HttpServletRequest request,
HttpServletResponse response, String param) {
try {
PrintWriter pw = response.getWriter();
String result = jamesService.update(param);

pw.write(result);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
  • JamesHandlerAdapter完成了从request中提取参数,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@EnjoyService("jamesHandlerAdapter")
public class JamesHandlerAdapter implements HandlerAdapterService {
//对method方法里的参数进行处理
public Object[] hand(HttpServletRequest request,//需要传入request,拿请求的参数
HttpServletResponse response, Method method,//执行的方法,可以拿到当前待执行的方法有哪些参数
Map<String, Object> beans) {
//拿到当前待执行的方法有哪些参数
Class<?>[] paramClazzs = method.getParameterTypes();
//根据参数的个数,new 一个参数的数组,将方法里的所有参数赋值到args来
Object[] args = new Object[paramClazzs.length];

//1、要拿到所有实现了ArgumentResolver这个接口的实现类
Map<String, Object> argumentResolvers = getBeansOfType(beans,
ArgumentResolver.class);

int paramIndex = 0;
int i = 0;
//对每一个参数进行循环,每个参数都有特殊处理(比如RequestParam的处理类为 RequestParamArgumentResolver )
for (Class<?> paramClazz : paramClazzs) {
//哪个参数对应了哪个参数解析类,用策略模式来找
for (Map.Entry<String, Object> entry : argumentResolvers.entrySet()) {
ArgumentResolver ar = (ArgumentResolver)entry.getValue();

if (ar.support(paramClazz, paramIndex, method)) {
args[i++] = ar.argumentResolver(request,
response,
paramClazz,
paramIndex,
method);
}
}
paramIndex++;
}

return args;
}
//获取实现了ArgumentResolver接口的所有实例(其实就是每个参数的注解实例)
private Map<String, Object> getBeansOfType(Map<String, Object> beans,//所有bean
Class<?> intfType) //类型的实例
{

Map<String, Object> resultBeans = new HashMap<String, Object>();

for (Map.Entry<String, Object> entry : beans.entrySet()) {
//拿到实例-->反射对象-->它的接口(接口有多实现,所以为数组)
Class<?>[] intfs = entry.getValue().getClass().getInterfaces();

if (intfs != null && intfs.length > 0) {
for (Class<?> intf : intfs) {
//接口的类型与传入进来的类型一样,把实例加到resultBeans里来
if (intf.isAssignableFrom(intfType)) {
resultBeans.put(entry.getKey(), entry.getValue());
}
}
}
}

return resultBeans;
}

}
  • 最后的service中定义了基本的操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@EnjoyService("JamesServiceImpl")
public class JamesServiceImpl implements JamesService {

public String query(String name, String age) {

return "{name="+name+",age="+age+"}";
}

public String insert(String param) {
// TODO Auto-generated method stub
return "insert successful.....";
}

public String update(String param) {
// TODO Auto-generated method stub
return "update successful.....";
}

}

关于DispatcherServlet可以参考:

参考:


本文由『后端精进之路』原创,首发于博客 http://teckee.github.io/ , 转载请注明出处

搜索『后端精进之路』关注公众号,立刻获取最新文章和价值2000元的BATJ精品面试课程

后端精进之路.png