手写实现简易的Spring MVC xml版本。
代码:https://github.com/kuotian/springmvc_me
- 用户请求:localhost\addUser2
- DispatcherServlet接收请求,通过HandlerMapping去查找合适的Handler
- 根据处理器获取相应的适配器,执行相应的处理器的方法去处理请求
- 返回执行结果
1. 使用Servlet实现
localhost/addUser
package com.hdu.springmvc.servlet;
public class AddUserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=utf8");
resp.getWriter().write("添加成功");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
localhost/queryUser
package com.hdu.springmvc.servlet;
public class QueryUserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=utf8");
resp.getWriter().write("查询成功");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
2. 使用Servlet的弊端 及 解决方案
分析使用Servlet实现BS结构服务处理的弊端
- 对于程序员来说,会编写大量的Servlet代码,很不爽。
- Servlet是由tomcat产生的,产生过多的Servlet对象,会导致tomcat本身启动的时候会有压力。
解决方案
-
编写Servlet基类BaseServlet,抽取相同的逻辑。业务处理逻辑写在子类的Servlet。
请求url:
localhost/userSersvlet?method=add
localhost/userSersvlet?method=queryBaseServlet{ doGet(){ 获取method参数值 根据参数值调用响应的方法 } } UserServlet extends BasServlet{ add(){} query(){} }
-
编写全局Servlet去接收所有Servlet请求,然后在该Servlet中进行请求分发,由处理器类去处理具体的请求。 ---> Spring MVC
其中,处理器类不需要实现Servlet接口,它只是一个普通的JavaBean。不需要产生额外的Servlet类。
DispatcherServlet{ doGet(){ url if("/queryUser".equals(url)){ 找相应的处理器去处理 } } } //接口(标准或者规范) UserHandler{ xxx }
3. 手写简易Spring MVC
DispatcherServlet
作用:
接收请求、响应结果
查找处理器
执行相应的处理器(调用处理器的方法去处理请求)
AbstractHttpServlet
package com.hdu.springmvc.servlet;
public abstract class AbstractHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatch(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
/**
* 抽象模板设计模式的钩子方法
*/
public abstract void doDispatch(HttpServletRequest request, HttpServletResponse response);
}
DispatcherServlet
package com.hdu.springmvc.servlet;
public class DispatcherServlet extends AbstractHttpServlet {
private DefaultListableBeanFactory beanFactory;
// 处理器映射器的策略集合
private List<HandlerMapping> handlerMappings;
// 处理器适配器的策略集合
private List<HandlderAdapter> handlerAdapters;
public void init(ServletConfig config){
// 从web.xml中获取springmvc的配置文件路径
String contextConfigLocation = config.getInitParameter("contextConfigLocation");
// 初始化spring容器,需要提前一次性初始化bean实例
initSpringContainer(contextConfigLocation);
// 初始化策略集合
initHandlerMappings();
initHandlerAdapters();
}
private void initSpringContainer(String contextConfigLocation) {
beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
Resource resource = new ClassPathResource(contextConfigLocation);
reader.loadBeanDefinitions(resource);
}
private void initHandlerMappings() {
// 从spring容器中,初始化所有的HandlerMapping的策略
handlerMappings = new ArrayList<>(beanFactory.getBeansOfType(HandlerMapping.class).values());
}
private void initHandlerAdapters() {
handlerAdapters = new ArrayList<>(beanFactory.getBeansOfType(HandlderAdapter.class).values());
}
@Override
public void doDispatch(HttpServletRequest request, HttpServletResponse response) {
try {
// 根据请求查找处理器类(HandlerMapping)
Object handler = getHandler(request);
// 去执行处理器类的方法(HandlerAdapter)
// // 使用策略模式+适配器模式去优化以下代码
// if (handler instanceof HttpRequestHandler) {
// ((HttpRequestHandler) handler).handleRequest(request, response);
// } else if (handler instanceof SimpleControllerHandler) {
// ((SimpleControllerHandler) handler).handleRequest(request, response);
// }
if(handler == null){
return;
}
// 先找处理器适配器
HandlderAdapter ha = getHandlerAdapter(handler);
if (ha == null) {
return;
}
// 再去调用处理器适配器的统一处理请求方法
ModelAndView mv = ha.handleRequest(handler, request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
private Object getHandler(HttpServletRequest request) throws Exception {
String uri = request.getRequestURI();
// 使用策略模式
if (handlerMappings != null && handlerMappings.size() > 0) {
for (HandlerMapping handlerMapping : handlerMappings) {
Object handler = handlerMapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
private HandlderAdapter getHandlerAdapter(Object handler) {
// 如果HandlerAdapter1能处理handler,则返回HandlerAdapter1
// 如果HandlerAdapter2能处理handler,则返回HandlerAdapter2
// 如果HandlerAdapter3能处理handler,则返回HandlerAdapter3
if (handlerAdapters != null && handlerAdapters.size() > 0) {
for (HandlderAdapter handlderAdapter : handlerAdapters) {
// 判断该处理器适配器是否支持该处理器
if (handlderAdapter.supports(handler)) {
return handlderAdapter;
}
}
}
return null;
}
}
HandlerMapping
作用:请求映射和查找请求
建立映射的方式有多种,所以说HandlerMapping应该有多个。每个HandlerMapping负责的映射来源是不一样的。
- BeanNameUrlHandlerMapping:通过bean标签的name属性和bean标签对应的实例建立映射关系
- SimpleUrlHandlerMapping:手动建立映射。(充数用)
HandlerMapping
package com.hdu.springmvc.handlermapping.iface;
// 定义处理器映射器的编写规范
public interface HandlerMapping {
/**
* 根据请求查找处理器
* @param request
* @return
* @throws Exception
*/
Object getHandler(HttpServletRequest request) throws Exception;
}
BeanNameUrlHandlerMapping
package com.hdu.springmvc.handlermapping;
// 通过bean标签的name属性和bean标签对应的实例建立映射关系
public class BeanNameUrlHandlerMapping implements HandlerMapping, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
// uri和处理器对象的映射集合
private Map<String, Object> mappings = new HashMap<>();
/**
* BeanNameUrlHandlerMapping的初始化方法
*/
public void init(){
// 从spring容器中(BeanFactory)获取所有的handler的BeanDefinition对象
// Spring 容器中所有 JavaBean 的名称
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
// 判断bean的name属性,是否是以/开头
if( beanDefinitionName.startsWith("/") ) {
// 取出bean的name属性,并且获取bean实例
Object bean = beanFactory.getBean(beanDefinitionName);
// 建立映射关系
mappings.put(beanDefinitionName, bean);
}
}
}
@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;
}
}
SimpleUrlHandlerMapping
package com.hdu.springmvc.handlermapping;
public class SimpleUrlHandlerMapping implements HandlerMapping, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
// uri和处理器对象的映射集合
private Map<String, Object> mappings = new HashMap<>();
// 手动填充
public void init() {
mappings.put("/addUser3", new AddUserHandler());
mappings.put("/queryUser3", new QueryUserHandler());
}
@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;
}
}
HandlerAdapter
作用:将DispatcherServlet类中使用的Handler类,适配成统一的HandlerAdapter类型。
HttpRequestHandler ---> HttpRequestHandlerAdapter --->HandlerAdapter 类型
HandlderAdapter
package com.hdu.springmvc.handleradapter.iface;
// DispatcherServlet类中希望见到的统一的目标接口
public interface HandlderAdapter {
/**
* 处理请求
* @param handler 处理器类
*/
ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception;
/**
* 就是判断该适配器是否适合该处理器,方便策略模式使用时进行调用
* @param handler
*/
boolean supports(Object handler);
}
HttpRequestHandlerAdapter
package com.hdu.springmvc.handleradapter;
public class HttpRequestHandlerAdapter implements HandlderAdapter {
@Override
public ModelAndView handleRequest(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
((HttpRequestHandler) handler).handleRequest(request, response);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
}
Handler
是一个普通的Java类,它不是Servlet类。
作用:处理请求。
-
HttpRequestHandler接口(规范大家如何编写Handler类去处理请求)
void handleRequest(request,response);
-
SimpleControllerHandler接口
ModelAndView handleRequest(request,response);
对于处理器类的处理结果,有可能还需要处理,则把处理结果和最终显示的视图名称封装到一个对象中。Handler类的编写方式有多种,而且类型都无法统一。需要有个适配器处理。
SimpleControllerHandler
// 定义处理器的编写规范
// 这种规范要求返回一个返回值ModelAndView对象
public interface SimpleControllerHandler {
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
HttpRequestHandler
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
AddUserHandler
package com.hdu.springmvc.handler;
public class AddUserHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/plain;charset=utf8");
response.getWriter().write("AddUserHandler添加成功");
}
}
QueryUserHandler
package com.hdu.springmvc.handler;
public class QueryUserHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/plain;charset=utf8");
response.getWriter().write("QueryUserHandler添加成功");
}
}
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.handleradapter.HttpRequestHandlerAdapter"></bean>
</beans>
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