增加对annotation的支持,查找@Controller下的@RequestMapping
代码:https://github.com/kuotian/springmvc_me
1. 注解
@Controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
//Controller(value="")
String value() default "";
}
@RequestMapping
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
// RequestMapping(value="")
String value() default "";
}
@ResponseBody
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseBody {
// RequestMapping(value="")
String value() default "";
}
@Target@Retention:元注解,作用就是给注解进行解释的
@Target:最终我们这个注解是要作用在什么地方(类、方法或者字段)
ElementType.TYPE:类
ElementType.METHOD:方法
Retention:最终我们这个注解是要保留到哪个阶段(源代码、class文件、运行时环境)
2. UserController 新的Handler
使用注解方式去编写的Handler,一个handler类中可以处理N个请求,和HttpRequestHandler是有很大的进步的。
UserController
- 类上加@Controller注解
- 方法上加@RequestMapping注解(便于HandlerMapping建立注解方式下的请求映射关系)
方法参数需要知道请求中的参数有哪些,我们就可以一一对应的写到我们Controller方法参数中去获取这些参数
package com.hdu.springmvc.controller;
//使用@Controller注解的类,就表示是一个Handler类
@Controller
public class UserController {
// http请求:http://localhost/queryUser4?id=10&name=messi
@RequestMapping("/queryUser4")
@ResponseBody
public Map<String, Object> queryUser(Integer id, String name) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("name", name);
return map;
}
// http请求:http://localhost/queryUser4?id=10&name=messi
@RequestMapping("/addUser4")
@ResponseBody
public String addUser() {
return "addUser4";
}
}
3. 编写新的HandlerAdapter
RequestMappingHandlerAdapter
package com.hdu.springmvc.handleradapter;
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1.将Object类型的handler强转为HandlerMethod类型
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 2.取出Method对象和Controller对象
Method method = handlerMethod.getMethod();
Object controller = handlerMethod.getHandler();
// 3.要处理方法参数的绑定(请求URL中的参数是字符串类型,而Controller方法的参数类型是多种多样的)
Object[] args = getParameter(request, method);
// 4.通过反射调用对应的方法,并得到返回值(就是调用Controller类中被RequestMapping注解所对应的方法)
Object returnValue = method.invoke(controller, args);
// 5.处理返回值
handleReturnValue(response, method, returnValue);
return null;
}
private Object[] getParameter(HttpServletRequest request, Method method) {
List<Object> parameterList = new ArrayList<Object>();
// 将请求URL中的参数,获取到一个Map集合中
Map<String, String[]> parameterMap = request.getParameterMap();
// 获取Controller方法的参数
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
// 获取参数名称
String name = parameter.getName();
// 需要根据方法中的参数名称去获取parameterMap中对应key的值
String[] stringValue = parameterMap.get(name);
// 获取目标方法参数类型
Class<?> type = parameter.getType();
// 类型转换
Object value = convertValue(stringValue, type);
parameterList.add(value);
}
return parameterList.toArray();
}
private Object convertValue(String[] stringValue, Class<?> type) {
if (stringValue == null || stringValue.length == 0) {
return null;
}
// TODO 使用策略模式去优化
if (type == List.class) {
return stringValue;
} else if (type == Integer.class) {
return Integer.parseInt(stringValue[0]);
} else if (type == String.class) {
return stringValue[0];
}
return null;
}
private void handleReturnValue(HttpServletResponse response, Method method, Object returnValue) throws Exception {
// 是否带有@ResponseBody注解
if (method.isAnnotationPresent(ResponseBody.class)) {
if (returnValue instanceof String) {
response.setContentType("text/plain;charset=utf8");
response.getWriter().write(returnValue.toString());
} else if (returnValue instanceof Map) {
response.setContentType("application/json;charset=utf8");
response.getWriter().write(JsonUtils.object2Json(returnValue));
} // ....
} else {
// 页面跳转处理
}
}
@Override
public boolean supports(Object handler) {
return (handler instanceof HandlerMethod);
}
}
4. 编写新的HandlerMapping
RequestMapping注解中的请求URL和它修饰的方法之间建立了映射关系。需要针对这种映射关系进行解析,然后放入Map集合中。
按理说一个请求URL应该对应一个Handler类。而现在我们对应的是一个Controller方法。
在注解方式编写的Controller类这种实现,Controller类不是真正的Handler类,真正的Handler类是Controller类+对应的方法。
对于这个组合,我们提供了一个对象去封装他们,这个对象HandlerMethod对象(【Object handler,Method method】)
最终我们得出结论:请求URL和HandlerMethod建立映射关系,HandlerMethod才是DispatcherServlet类中需要的Handler类,而Controller不是Handler类。
RequestMappingHandlerMapping
package com.hdu.springmvc.handlermapping;
// 对于注解方式的处理器建立映射关系
public class RequestMappingHandlerMapping implements HandlerMapping, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
// uri和处理器对象的映射集合
private Map<String, Object> mappings = new HashMap<>();
public void init() {
try {
// 1.要获取所有的BeanDefinition,获取Class对象
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
String beanClassName = beanDefinition.getBeanClassName();
Class<?> clazz = Class.forName(beanClassName);
// 2. 查找带有Controller注解的类
if(isHandler(clazz)){
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 3.获取该Controller类中所有带@RequestMapping注解的方法
if (method.isAnnotationPresent(RequestMapping.class)) {
// 4.取出@RequestMapping注解中的value值(请求URL)。
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String url = requestMapping.value();
// 5.获取@RequestMapping注解对应的方法的Method对象
// 6.根据BeanDefinition中的beanName从ioc容器中获取Controller对象
Object controller = beanFactory.getBean(beanDefinitionName);
// 7.讲Method对象和Controller对象封装到HandlerMethod对象中
HandlerMethod handlerMethod = new HandlerMethod(controller, method);
// 8.建立映射关系
mappings.put(url, handlerMethod);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isHandler(Class<?> clazz) {
return (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(RequestMapping.class));
}
@Override
public Object getHandler(HttpServletRequest request) throws Exception {
String uri = request.getRequestURI();
return mappings.get(uri);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
}
HandlerMethod
Method对象和Controller对象封装到HandlerMethod对象中。
package com.hdu.springmvc.handlermapping;
public class HandlerMethod {
private Object handler; // Controller
private Method method; // Method
public Object getHandler() {
return handler;
}
public void setHandler(Object handler) {
this.handler = handler;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public HandlerMethod(Object handler, Method method) {
super();
this.handler = handler;
this.method = method;
}
}
5.springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 首先配置handler,交给spring容器去管理该bean -->
<!-- 其次配置name属性为uri,便于建立映射关系 -->
<bean name="/addUser2"
class="com.hdu.springmvc.handler.AddUserHandler"></bean>
<bean name="/queryUser2"
class="com.hdu.springmvc.handler.QueryUserHandler"></bean>
<!-- 配置处理器映射器 -->
<bean class="com.hdu.springmvc.handlermapping.BeanNameUrlHandlerMapping"
init-method="init"></bean>
<bean class="com.hdu.springmvc.handlermapping.SimpleUrlHandlerMapping"
init-method="init"></bean>
<bean class="com.hdu.springmvc.handlermapping.RequestMappingHandlerMapping"
init-method="init"></bean>
<!-- 配置注解方式的处理器 -->
<bean class="com.hdu.springmvc.controller.UserController"></bean>
<!-- 配置处理器适配器 -->
<bean class="com.hdu.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean>
<bean class="com.hdu.springmvc.handleradapter.RequestMappingHandlerAdapter"></bean>
</beans>
6.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>AddUserServlet</servlet-name>
<servlet-class>com.hdu.springmvc.servlet.AddUserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddUserServlet</servlet-name>
<url-pattern>/addUser</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>QueryUserServlet</servlet-name>
<servlet-class>com.hdu.springmvc.servlet.QueryUserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>QueryUserServlet</servlet-name>
<url-pattern>/queryUser</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.hdu.springmvc.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
测试处理请求流程
- localhost\addUser 直接编写Servlet处理
- localhost\addUser2 --->DispatcherServlet --->BeanNameUrlHandlerMapping--->HttpRequestHandlerAdapter---> HttpRequestHandler 的 AddUserHandler
- localhost\addUser3 --->DispatcherServlet --->SimpleUrlHandlerMapping---> AddUserHandler
- localhost\addUser4 --->DispatcherServlet --->RequestMappingHandlerMapping---> RequestMappingHandlerAdapter--->UserController