手写SpringMvc
1,SpringMvc的实现,依托于Tomcat的Servlet,新建一个Maven工程,把工程修改成war包、并且引入Servlet包
2,回忆Tomcat启动时所作之事,会加载servlet程序,并且会马上执行init()方法,那么便可以把整个SpringMvc执行过程写在此方法中,分析,SpringMVC整个过程,首先其实是Spring的启动注入过程,
1,扫描路径 =====》2,实例化(将所有被@component或者@service等等注解标记的放入Spring容器)=====》3,注入(所有被@autowried注解标记的属性赋值)=====》4,访问路径和执行方法相对应 ====》5,执行方法
第五步执行方法放在doPost()方法中,
3,具体执行步骤
整个过程包含三个容器
整个项目结构
自定义的servlet继承HttpServlet
以及JavaWeb项目的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>MyDispatcherServlet</servlet-name> <servlet-class>com.jy.servlet.MyDispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
1)在类加载时利用反射获取其所在的路径,然后遍历判断所有的文件夹和.class文件,然后利用反射,并且放入 类名容器
private void packageScan(String basePackage) { //获取到此类所在路径, URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")); //文件路径 /D:/D:/java/com/jy //项目部署到tomcat上时,文件夹变为D:/Tomcat 8.5/******,此时将Tomcat和8.5之间的空格替换掉 String urlFile = url.getPath().replace("%20"," "); File file = new File(urlFile); //路径下所有的文件及目录 String[] list = file.list(); //遍历所有文件及目录 for (String fileStr : list) { File filepath = new File(urlFile + fileStr + "/"); if (filepath.isDirectory()) { packageScan(basePackage + "." + fileStr); } else { //说明是com.jy.xxx.xxx.class classNames.add(basePackage + "." + fileStr); } } }
2)实例化类名容器中的所有的类
//实例化对象 private void instance() { for (String className : classNames) { try { Class<?> clazz = Class.forName(className.replace(".class", "")); if (clazz.isAnnotationPresent(MyService.class)) { //创建对象 Object object = clazz.newInstance(); //获取注解中写入的bean的名字 String beanName = clazz.getAnnotation(MyService.class).value(); beans.put(beanName, object); } else if (clazz.isAnnotationPresent(MyController.class)) { //创建对象 Object object = clazz.newInstance(); //获取注解中写入的bean的名字 String beanName = clazz.getAnnotation(MyController.class).value(); beans.put(beanName, object); } else { continue; } //将new出来的对象放入spring容器 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
3)依赖注入
private void doAutowied() { //遍历容器 for (Map.Entry<String, Object> entry : beans.entrySet()) { //获取容器中的对象 Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(MyAutowired.class)) { //打开权限 field.setAccessible(true); String fieldName = field.getName(); try { field.set(instance,beans.get(fieldName) ); } catch (IllegalAccessException e) { e.printStackTrace(); } }else { continue; } } } }
4)将访问路径和所执行的方法对应起来
private void urlMappring() { for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); if (clazz.isAnnotationPresent(MyController.class)) { //获取到类上的MyRequestMapping对象 MyRequestMapping mapping1 = clazz.getAnnotation(MyRequestMapping.class); //获取到类上MyRequestMapping的value值 String requestMapping1 = mapping1.value(); //获取所有的方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(MyRequestMapping.class)) { MyRequestMapping mapping2 = method.getAnnotation(MyRequestMapping.class); //方法上的requestMapping注解的value值 String requestMapping2 = mapping2.value(); //将方法存入容器中 handleMap.put(requestMapping1 + requestMapping2, method); } else { continue; } } } } }
5)利用反射执行方法
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 工程名/com/jy String uri = req.getRequestURI(); // 获取到工程名 String contextPath = req.getContextPath(); //uri去掉工程名 String path = uri.replace(contextPath, ""); Method method = (Method) handleMap.get(path); //获取到所有的属性值并且遍历 Field[] fields = this.getClass().getFields(); for (Field field : fields) { if (field.isAnnotationPresent(MyAutowired.class)){ } } //获取到方法所在类的类名 String simpleName = method.getDeclaringClass().getSimpleName(); //将类名转小写 String sb = simpleName.substring(0,1).toLowerCase()+simpleName.substring(1); //根据类名获取到容器中的对象 Object bean = beans.get(sb); try { //执行方法 method.invoke(bean,"哈哈哈"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }
4,收获
1)对反射等基础知识理解更加深刻
2)进一步理解Spring容器以及SpringMvc整个执行过程
3)对注解的理解及使用加深