SpringMVC
SpringMVC的运行流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理映射器
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 执行处理器(Controller,也叫后端控制器)
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户
回顾Servlet
概念
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,可以很方便的收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Servlet声明周期
Servlet 加载—>实例化—>服务—>销毁。
- init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。
- service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
- destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
手写SpringMVC基本实现
/**
* 1.自定义前端控制器DispatcherServlet
* 拦截所有请求 (springmvc 基于servlet实现)
* 2.继承HttpServlet 重写init()方法(只会执行一次)
* 2.1获取当前包下所有的类
* 2.2利用反射机制初始化所有的类,存放在SpringMVC容器中
* key(beanId),value(当前实例对象)
* 2.3初始化HandlerMapping方法,将url和方法对应
* 使用反射技术读取类的信息,存放在map集合中
* key为url请求地址,value为子map集合,
* key为方法名称,value为实例对象
* 2.3.1 遍历springmvc bean容器 获取bean对象
* 2.3.2 判断类上是否有加url映射注解
* 2.3.3 判断方法上是否有加url映射地址
* 2.3.4 拼接完整路径,装到SpringMVC容器中
*3.重写doGet()、doPost()方法
* 3.1获取请求url地址
* 3.2从Map集合中获取控制对象
* 3.3.使用url地址获取方法
* 3.4.使用java的反射机制调用方法
* 3.5.调用视图转换器渲染给页面展示
*/
//1.自定义前端控制器DispatcherServlet
public class DispatcherServletDemo extends HttpServlet {
// springmvc 容器对象 key:类名id ,value 对象
private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();
// springmvc 容器对象 key:url ,value map key:方法名称 ,value 对象
private ConcurrentHashMap<String, Map<String,Object>> urlMappings = new ConcurrentHashMap<String, Map<String,Object>>();
//2.重写init()方法
@Override
public void init() throws ServletException {
//2.1获取当前包下所有的类
List<Class<?>> classes = ClassUtil.getClasses("controller");
// 2.2利用反射机制初始化所有的类,存放在SpringMVC容器中
try {
loadSpringMVCBeanMap(classes);
}catch (Exception e){
}
//2.3初始化HandlerMapping方法,将url和方法对应
handlerMapping();
}
/**
*利用反射机制初始化所有的类,存放在SpringMVC容器中
* key(beanId),value(当前实例对象)
* @param classes
*/
private void loadSpringMVCBeanMap(List<Class<?>> classes) throws Exception {
for(Class classInfo:classes){
//是否有自定义Controller注解
ControllerDemo controllerDemo = (ControllerDemo) classInfo.getDeclaredAnnotation(ControllerDemo.class);
if(controllerDemo != null){
//beanId默认首字母小写
String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
//实例化对象
Object obj = classInfo.newInstance();
//装到SpringMVC容器中
springmvcBeans.put(beanId,obj);
}
}
}
/**
* 2.3初始化HandlerMapping方法,将url和方法对应
* 2.3.1 遍历springmvc bean容器 获取bean对象
* 2.3.2 判断类上是否有加url映射注解
* 2.3.3 判断方法上是否有加url映射地址
* 2.3.4 拼接完整路径,装到SpringMVC容器中
*/
private void handlerMapping() {
//2.3.1 遍历springmvc bean容器
for(Map.Entry<String,Object> mvcbean : springmvcBeans.entrySet()){
//获取bean对象
Object object = mvcbean.getValue();
//2.3.2 判断类上是否有加url映射注解
Class<?> classInfo = object.getClass();
RequestMappingDemo requestMappingDemo = classInfo.getDeclaredAnnotation(RequestMappingDemo.class);
String baseUrl = "";
if(requestMappingDemo != null){
baseUrl = requestMappingDemo.value();
}
//2.3.3 判断方法上是否有加url映射地址
Method[] methods = classInfo.getMethods();
for(Method method:methods){
RequestMappingDemo requestMappingDemo1 = method.getDeclaredAnnotation(RequestMappingDemo.class);
if(requestMappingDemo1 != null){
//2.3.4 拼接完整路径,装到SpringMVC容器中
baseUrl = baseUrl + requestMappingDemo1.value();
//map key:方法名称 ,value 对象
Map<String,Object> map = new HashMap<String, Object>();
map.put(method.getName(),object);
//springmvc 容器对象 key:url ,value map
urlMappings.put(baseUrl,map);
}
}
}
}
//3.重写doGet()方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
/**
* 3.重写doGet()、doPost()方法
* * 3.1获取请求url地址
* * 3.2从Map集合中获取控制对象
* * 3.3.使用url地址获取方法和实例对象
* * 3.4.使用java的反射机制调用方法
* * 3.5.调用视图转换器渲染给页面展示
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//3.1获取请求url地址
String requestURI = req.getRequestURI();
//3.2从Map集合中获取控制对象
Map<String, Object> map = urlMappings.get(requestURI);
//3.3.根据url地址获取方法
String methodName = map.entrySet().iterator().next().getKey();
//获得实例对象
Object object = map.entrySet().iterator().next().getValue();
try {
//3.4.使用java的反射机制调用方法
String resultPage = (String) object.getClass().getMethod(methodName).invoke(object);
//3.5.调用视图转换器渲染给页面展示
extResourceViewResolver(resultPage, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
//3.5.调用视图转换器渲染给页面展示
private void extResourceViewResolver(String resultPage, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 根路径
String prefix = "/";
String suffix = ".jsp";
req.getRequestDispatcher(prefix + resultPage + suffix).forward(req, resp);
}
}