030:完全高仿SpringMVC框架
1 纯手写SpringMVC框架代码演示
2 创建DispatcherServlet前端控制
引入maven依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
初始化DispatcherServlet
public class HttpServletBean extends HttpServlet {
/**
* SpringMVC思路:
* 在项目启动时候定义好控制层和url映射关联存放到Map集合中
* 1.扫包获取class中的方法,判断是否有加上@RequestMapping注解,如果有的话存放到map集合中
* 2.key:url:value 方法
* 访问请求,根据url查找对应的执行方法,再通过java反射执行。
*/
@Override
public void init() throws ServletException {
// 初始化springmvc bean的对象,url与方法关联
initServletBean();
}
protected void initServletBean() {
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
doService(req,resp);
}
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
}
}
public class FrameworkServlet extends HttpServletBean{
@Override
protected void initServletBean() {
onRefresh();
}
protected void onRefresh() {
}
@Override
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
}
}
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void onRefresh() {
initStrategies();
}
private void initStrategies() {
System.out.println("DispatcherServlet容器开始初始化了!");
}
@Override
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("蚂蚁课堂 666");
}
}
3 创建ServletContainerInitializer
注册DispatcherServlet到Servlet容器中
Resource目录下创建META-INF/services/javax.servlet.ServletContainerInitializer文件
内容:com.mayikt.servlet.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* onStartup servlet容器初始化的时候调用该方法
*
* @param classInfos 获取WebApplicationInitializer所有子类信息
* @param servletContext
* @throws ServletException
*/
public void onStartup(Set<Class<?>> classInfos, ServletContext servletContext) throws ServletException {
for (Class<?> classInfo : classInfos) {
// classInfo都是WebApplicationInitializer类的子类
try {
Method method = classInfo.getMethod("onStartup", ServletContext.class);
Object object = classInfo.newInstance();
method.invoke(object, servletContext);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
public class AbstractDispatcherServletInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
// 1.开始注册DispatcherServlet
ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcherServlet", new DispatcherServlet());
dispatcherServlet.addMapping("/"); // 拦截所有的请求
}
}
断点启动tomcat
4 解决注册Servlet类不生效的原因
启动tomcat,注册Servlet类不生效,原因是web.xml配置冲突。
删除webapp/WEB-INF下面的三个xml文件。
断点启动tomcat:
5 HandlerMapping对象初始化
注解类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScan {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
配置类
@ComponentScan("com.mayikt.controller")
public class SpringMvcConfig {
}
控制层
@Controller
public class PayController {
@RequestMapping("/pay")
public String pay() {
return "pay";
}
}
存放url映射对应的value
public class HandlerMethod {
// 请求方法对应的bean对象
private Object bean;
private Method method;
public HandlerMethod(Object bean, Method method) {
this.bean = bean;
this.method = method;
}
public Object getBean() {
return bean;
}
public Method getMethod() {
return method;
}
}
RequestMappingHandlerMapping
public class RequestMappingHandlerMapping {
// 初始化SpringMVC bean对象、url与方法关联存放到Map集合中
private Map<String, HandlerMethod> registry = new HashMap<String, HandlerMethod>();
public void initHandlerMappings() {
ComponentScan componentScan = SpringMvcConfig.class.getDeclaredAnnotation(ComponentScan.class);
String springMvcPackage = componentScan.value();
if (StringUtils.isEmpty(springMvcPackage)) {
return;
}
// 1.使用java反射机制,查找该包下com.mayikt.controller有哪些控制类
Set<Class<?>> classes = ReflexUtils.getClasses(springMvcPackage);
// 2.循环遍历每个类
for (Class<?> classInfo : classes) {
// 3.判断类上面是否有Controller注解
Controller controller = classInfo.getDeclaredAnnotation(Controller.class);
if (controller == null) {
continue;
}
// 4.遍历控制层类中方法是否有加上RequestMapping注解
Method[] declaredMethods = classInfo.getDeclaredMethods();
for (Method method : declaredMethods) {
RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
if (requestMapping == null) {
continue;
}
String url = requestMapping.value();
// 存放hashMap集合,key url,value Method
registry.put(url, new HandlerMethod(newInstance(classInfo), method));
}
}
}
private Object newInstance(Class classInfo) {
try {
Object value = classInfo.newInstance();
return value;
} catch (Exception e) {
return null;
}
}
public HandlerMethod getHandlerMethod(String url) {
return registry.get(url);
}
}
DispatcherServlet 初始化的时候装配RequestMappingHandlerMapping
public class DispatcherServlet extends FrameworkServlet {
private RequestMappingHandlerMapping requestMappingHandlerMapping;
public DispatcherServlet() {
requestMappingHandlerMapping = new RequestMappingHandlerMapping();
}
@Override
protected void onRefresh() {
initStrategies();
}
private void initStrategies() {
requestMappingHandlerMapping.initHandlerMappings();
}
@Override
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("蚂蚁课堂 666");
}
}
6 纯手写SpringMVC执行流程01
HandlerExecutionChain
public class HandlerExecutionChain {
HandlerMethod handlerMethod;
// 拦截器
public HandlerExecutionChain(HandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
}
public ModelAndView handler() throws InvocationTargetException, IllegalAccessException {
// 1.使用java反射机制执行请求方法
Method method = handlerMethod.getMethod();
Object bean = handlerMethod.getBean();
Object viewName = method.invoke(bean, null);
ModelAndView modelAndView = new ModelAndView((String) viewName);
return modelAndView;
}
}
ModelAndView
public class ModelAndView {
// 跳转页面名称
private String viewName;
public ModelAndView(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
}
DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
private RequestMappingHandlerMapping requestMappingHandlerMapping;
public DispatcherServlet() {
requestMappingHandlerMapping = new RequestMappingHandlerMapping();
}
@Override
protected void onRefresh() {
initStrategies();
}
private void initStrategies() {
requestMappingHandlerMapping.initHandlerMappings();
}
@Override
protected void doService(HttpServletRequest req, HttpServletResponse resp) {
doDispatch(req, resp);
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
try {
// 1.处理请求url
String requestURI = req.getRequestURI();
// 2.根据url查找对应的Handler
HandlerExecutionChain handler = getHandler(requestURI);
// 3.使用java的反射机制执行该方法,返回对应的ModelAndView
ModelAndView modelAndView = handler.handler();
// 4.开始渲染视图层
render(modelAndView, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
private HandlerExecutionChain getHandler(String url) {
HandlerMethod handlerMethod = requestMappingHandlerMapping.getHandlerMethod(url);
if (handlerMethod == null) {
return null;
}
HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(handlerMethod);
return handlerExecutionChain;
}
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
throw new Exception("没有查找到对应的请求");
}
public void render(ModelAndView modelAndView, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String viewName = modelAndView.getViewName();
req.getRequestDispatcher("/WEB-INF/view/" + viewName + ".jsp").forward(req, resp);
}
}
7 纯手写SpringMVC执行流程02
webapp/WEB-INF/view/pay.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
纯手写低配版springmvc 蚂蚁课堂666
</body>
</html>
运行测试:
断点调试流程: