在模拟SpringMVC之前,我们先部署一个web项目到Tomcat下,并进行简单的访问
web.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> </web-app>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>hello world</h1> </body> </html>
部署到Tomcat后,进行访问
为什么我们通过http://localhost:8080/helloworld,就可以访问到index.jsp呢?
第一,当Tomcat启动后,除了在本地开启8080端口外,还会对项目进行一个虚拟目录映射(Tomcat是server.xml中)
第二,我们访问Tomcat服务的虚拟目录时,如果没有指定Serlvet时,他会有一个缺省的DefaultServlet(Tomcat的web.xml文件)
上面对web项目的访问方式: http://localhost:8080/helloworld
下面我们模拟访问SpringMVC: http://localhost:8080/springmvc/hello/index
模拟SpringMVC总线:
1.自己模拟Controller注解
2.自己模拟RequestMapper注解
3.自己模拟RequestParam注解
4.自己模拟MyService注解
5.自己模拟Autoware注解
6.自己模拟DispatcherServlet
7.搭建web项目测试
模拟Controller注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyController { String value() default ""; }
模拟RequestMapper注解
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyRequestMapper { String value() default ""; }
模拟RequestParam注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyRequestParam { String value() default ""; }
模拟MyService注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyService { String value() default ""; }
模拟Autoware注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAutowried { String value() default ""; }
模拟DispatcherServlet
public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 2571175759534212451L; private List<String> classNames = new ArrayList<String>(); //指定包下的所有对象 private Map<String, Object> beans = new HashMap<String, Object>(); //扫描到的所有的对象 private Map<String, Object> handlerMap = new HashMap<String, Object>(); //所有请求URL和对象方法的映射 @Override public void init() throws ServletException { doScanPackage("cn.enn"); //扫描哪些对象需要创建 doInstance(); //创建对象实例 doIoc(); //依赖注入 handlerMapping(); //URL和方法映射 } //1.把我们指定的cn.enn下的所有class文件加载到集合中 private void doScanPackage(String basePackage) { URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/")); String fileStr = url.getFile(); File file = new File(url.getFile()); String[] listFiles = file.list(); for (String path : listFiles) { File filepath = new File(fileStr + path); if (filepath.isDirectory()) { doScanPackage(basePackage + "." + path); } else { classNames.add(basePackage + "." + path.replace(".class", "")); } } } //2.把加载的对象,实例化对象 private void doInstance() { for (String className : classNames) { try { Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(MyController.class)) { //映射Controller对象 Object obj = clazz.newInstance(); MyRequestMapper requestMapping = clazz.getAnnotation(MyRequestMapper.class); String key = requestMapping.value(); // /hello->HelloController beans.put(key, obj); } else if (clazz.isAnnotationPresent(MyService.class)) { //映射Service对象 Object obj = clazz.newInstance(); MyService annotation = clazz.getAnnotation(MyService.class); String key = annotation.value(); if (key.equals("")) { Class<?>[] interfaces = clazz.getInterfaces(); if (interfaces.length > 0) { key = toLowerFirstWord(interfaces[0].getSimpleName()); //如果父类存在,key为父类的名称 } else { key = toLowerFirstWord(className.substring(className.lastIndexOf(".") + 1)); //如果父类不存在,则以当前类名称为key } } // 比如HelloService ,名称会映射成 helloService // helloService -> cn.enn.service.impl.HelloServiceImpl beans.put(key, obj); } else { // 可扩展 component 等注解 continue; } } catch (Exception e) { e.printStackTrace(); } } } // 将name 第一个字编变小写 private String toLowerFirstWord(String name) { char[] charArray = name.toCharArray(); charArray[0] += 32; return String.valueOf(charArray); } //基于MyAutowried注解,进行注入对象 private void doIoc() { for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); //注入Controller对象中的Filed if (clazz.isAnnotationPresent(MyController.class)) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(MyAutowried.class)) { //处理MyAutowried注解 MyAutowried autowried = field.getAnnotation(MyAutowried.class); String key = autowried.value(); if (key.equals("")) { key = field.getName(); //如果没有设置key的名字,则默认是属性名 } try { field.setAccessible(true); field.set(instance, beans.get(key)); //设置值 } catch (Exception e) { e.printStackTrace(); } } else { //你可以自己扩展@resource注解 continue; } } }else { //可自己扩容Service的属性注入 } } } //方法映射 // /hello/index --> HelloController.index() private void handlerMapping() { for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); //只解析加了Controller的注解类 if (clazz.isAnnotationPresent(MyRequestMapper.class)) { MyRequestMapper clazzRm = clazz.getAnnotation(MyRequestMapper.class); String clazzpath = clazzRm.value(); //这个是在Controller类上加的 RequestMapper // 获取其方法内 RequestMapper值 Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(MyRequestMapper.class)) { MyRequestMapper methodRm = method.getAnnotation(MyRequestMapper.class); String methodpath = methodRm.value(); // /hello/index-->HelloController.index() handlerMap.put(clazzpath + methodpath, method); } } }else { //自己扩容RestController注解 } } } //处理请求 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 根据不同请求,处理不同业务 doDispatch(req, resp); } //处理请求 private void doDispatch(HttpServletRequest req, HttpServletResponse resp) { // /springmvc/hello/index String uri = req.getRequestURI(); // /springmvc String content = req.getContextPath(); // /hello/index String path = uri.replaceAll(content, ""); try { Method method = (Method) handlerMap.get(path); //真实调用的方法 Object bean = beans.get("/" + path.split("/")[1]); //获取到对应的Controller对象 Object[] args = handle(req, resp, bean, method);//请求传递的参数 method.invoke(bean, args); //调用方法 } catch (Exception e) { e.printStackTrace(); } } // 获取 http传过来的参数 private Object[] handle(HttpServletRequest req, HttpServletResponse resp, Object bean, Method method) { // 获取方法的参数列表 Class<?>[] parameterTypes = method.getParameterTypes(); // 保存参数值 Object paramValues[] = new Object[parameterTypes.length]; // 方法的参数列表 for (int i = 0; i < parameterTypes.length; i++) { String requestParam = parameterTypes[i].getSimpleName(); if (requestParam.equals("HttpServletRequest")) { paramValues[i] = req; continue; } if (requestParam.equals("HttpServletResponse")) { paramValues[i] = resp; continue; } if (requestParam.equals("String")) { paramValues[i] = argumentResolver(req, method, i); } } return paramValues; } //获取参数的值 private Object argumentResolver(HttpServletRequest req, Method method, int paramIndex) { Annotation[][] annotations = method.getParameterAnnotations(); Annotation[] paraAns = annotations[paramIndex]; for (Annotation paramAn : paraAns) { //只过滤自定义的MyRequestParam注解 if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) { MyRequestParam ep = (MyRequestParam) paramAn; String value = ep.value(); return req.getParameter(value); } } return null; } }
搭建web项目测试
HelloService
public interface HelloService { public String hello(String name); }
HelloServiceImpl
@MyService public class HelloServiceImpl implements HelloService { public String hello(String name) { return "hello: " + name; } }
HelloController
@MyController @MyRequestMapper("/hello") public class HelloController { @MyAutowried private HelloService helloService; @MyRequestMapper("/index") public void index(HttpServletResponse response, @MyRequestParam("name") String name) throws IOException { String result = helloService.hello(name); PrintWriter writer = response.getWriter(); writer.println(result); } }
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.dispatcher.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
测试访问
到此,一个简单的SpringMVC模拟完成了,其实我们从SpringMVC的源码中可以看到,我们的请求会被DispatcherServlet去拦截
spring源码地址: https://github.com/spring-projects/spring-framework
我们在使用SpringMVC时,会在web.xml中配置如下内容:
<servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>