一直以来,逃避学习。不喜欢看源代码,不学新技术。呆在一个公司无所事事。面临找工作时,心里很慌,于是从新开始学习源码。从零开始直接开始撸代码。
1、建立一个Maven项目,项目结构如下图所示。
pom.xml 导入 servlet jar包
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency>
2、建立一些springmvc常用注解RequestMapper 、Controller、Server、Autowried、RequestMapper等等注解。我们自己手写可以使用自己独特的注解,区分原版的。我使用的是视频老师定义的注解。
EnjoyController
EnjoyServer
EnjoyRequestMapper
EnjoyAutowried
EnjoyRequestParam
注解@EnjoyController
@Target(ElementType.TYPE) //作用域 类 @Retention(RetentionPolicy.RUNTIME) // 保留至运行时 @Documented public @interface EnjoyController { String value() default ""; }
注解@EnjoyServer
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EnjoyServer { String value() default ""; }
@EnjoyRequestMapper
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EnjoyRequestMapper { String value() default ""; }
@EnjoyAutowried
@Target(ElementType.FIELD) //作用域 成员变量 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EnjoyAutowried { String value() default ""; }
@EnjoyRequestParam
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EnjoyRequestParam { String value() default ""; }
3、创建Controller类和Service类 (当然你也可以扩展Dao类) 暂时模拟二个类 模拟正常的springmvc
HelloController controller层
package com.enjoy.controller; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.enjoy.annation.EnjoyAutowried; import com.enjoy.annation.EnjoyController; import com.enjoy.annation.EnjoyRequestMapper; import com.enjoy.annation.EnjoyRequestParam; import com.enjoy.server.HelloServer; @EnjoyRequestMapper("/hello") @EnjoyController public class HelloController { @EnjoyAutowried HelloServer helloServer; @EnjoyRequestMapper("/query") public void query(HttpServletRequest request, HttpServletResponse response, @EnjoyRequestParam("name") String name, @EnjoyRequestParam("age") String age) throws IOException { String result = helloServer.query(name, age); // ctrl +2 PrintWriter writer = response.getWriter(); writer.println(result); } }
HelloServer server业务层
package com.enjoy.server; public interface HelloServer { public String query(String name, String age); }
HelloServerImpl
package com.enjoy.server.impl; import com.enjoy.annation.EnjoyServer; import com.enjoy.server.HelloServer; @EnjoyServer public class HelloServerImpl implements HelloServer { public String query(String name, String age) { return "name:" + name + "\r\nage" + age; } }
4、创建DispatcherServlet类,并通过web.xml加载
本质上DIspatcherServlet就是一个Servlet,我们通过web.xml让其容器一启动就开始加载。
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>com.enjoy.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具有其最核心的几个功能
1、把用Controller、Server修饰的类 统一用容器管理。容器一般用HashMap储存。
2、依赖注入
3、控制纷发
对应Dispatcher也应做到以上几点。
1、扫描包路径 doScanPackage
2、进行容器管理 doInstance
3、依赖注入 doIoc
4、控制纷发 handlerMapping
具体代码
package com.enjoy.dispatcher; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.enjoy.annation.EnjoyAutowried; import com.enjoy.annation.EnjoyController; import com.enjoy.annation.EnjoyRequestMapper; import com.enjoy.annation.EnjoyRequestParam; import com.enjoy.annation.EnjoyServer; public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private List<String> classNames = new ArrayList<String>(); // 存在当前加载的所有的类 private Map<String, Object> beans = new HashMap<String, Object>(); // 存放所有请求 Object->method.invoke()调用方法 private Map<String, Object> handlerMap = new HashMap<String, Object>(); @Override public void init() throws ServletException { // 1.扫描需要的实例化的类 doScanPackage("com.enjoy"); // 2.实例化 doInstance(); if (!beanEmpty()) { // 3.将IOC容器中的service对象设置给controller层定义的field上 doIoc(); // 4.建立path与method的映射关系 handlerMapping(); } } private boolean beanEmpty() { if (beans.isEmpty()) { System.out.println("bean is empty......"); return true; } return false; } // 获取EnjoyRequestMapper hello/query private void handlerMapping() { for (Map.Entry<String, Object> entry : beans.entrySet()) { // 获取实例 Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); // 获取注解当前为EnjoyRequestMapper的类 if (clazz.isAnnotationPresent(EnjoyRequestMapper.class)) { EnjoyRequestMapper clazzRm = clazz.getAnnotation(EnjoyRequestMapper.class); String clazzpath = clazzRm.value(); // 第一个 RequestMapper值 /hello // 获取其方法内 RequestMapper第二的值 Method[] methods = clazz.getMethods(); for (Method method : methods) { //// 判断方法上注解为RequestMappin if (method.isAnnotationPresent(EnjoyRequestMapper.class)) { EnjoyRequestMapper methodRm = method.getAnnotation(EnjoyRequestMapper.class); String methodpath = methodRm.value(); // key: /hello/query value:method handlerMap.put(clazzpath + methodpath, method); } } } } } private void doIoc() { for (Map.Entry<String, Object> entry : beans.entrySet()) { // 获取实例 Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); // 我们的代码只有HelloController有依赖注入 故只考虑注解EnjoyController if (clazz.isAnnotationPresent(EnjoyController.class)) { // 获取全部的成员变量 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 如果当前的成员变量使用注解EnjoyAutowried 自动注入 则给其赋值 if (field.isAnnotationPresent(EnjoyAutowried.class)) { // 获取自动注入的值 当然为空 EnjoyAutowried autowried = field.getAnnotation(EnjoyAutowried.class); String key = autowried.value(); if (key.equals("")) { // key: helloServer 自动注入 别写错 要根据key去容器取对象 key = field.getName(); } try { // 给filed赋值 field.setAccessible(true); field.set(instance, beans.get(key)); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { continue; } } } } } // 加载扫描类 private void doInstance() { for (String className : classNames) { try { // 获取反射 Class<?> clazz = Class.forName(className); // 判断当前类是否注解EnjoyController类 if (clazz.isAnnotationPresent(EnjoyController.class)) { // 反射 生成对象 Object obj = clazz.newInstance(); // 通过EnjoyRequestMapper获取值,作为beans的key EnjoyRequestMapper requestMapping = clazz.getAnnotation(EnjoyRequestMapper.class); String key = requestMapping.value(); // key:/hello value:对象 beans.put(key, obj); } else if (clazz.isAnnotationPresent(EnjoyServer.class)) { Object obj = clazz.newInstance(); // value 反射 生成类对象 // 我们代码 key为空 EnjoyServer annotation = clazz.getAnnotation(EnjoyServer.class); String key = annotation.value(); if (key.equals("")) { // 约定 key取Server父类名,第一个字母小写 Class<?>[] interfaces = clazz.getInterfaces(); if (interfaces.length > 0) { // 父类存在 key = toLowerFirstWord(interfaces[0].getSimpleName()); } else { // 父类不存在 key = toLowerFirstWord(className.substring(className.lastIndexOf(".") + 1)); // HelloController } } // key:helloServer value:helloServerImpl对象 beans.put(key, obj); } else { // 可扩展 continue; } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void doScanPackage(String basePackage) { URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/")); /** * 第一次fileStr:E:/apache-tomcat-7.0.94/webapps/enjoyMvc/WEB-INF/classes/com/enjoy */ String fileStr = url.getFile(); File file = new File(url.getFile()); /** * 第一次 listFiles:{annation,controller,dispatcher,server} */ String[] listFiles = file.list(); for (String path : listFiles) { File filepath = new File(fileStr + path); if (filepath.isDirectory()) { // 遍历 doScanPackage(basePackage + "." + path); } else { /** * 第一次 basePackage:com.enjoy.annation path:Autowried.class */ classNames.add(basePackage + "." + path.replace(".class", "")); } } } // 将name 第一个字编变小写 private String toLowerFirstWord(String name) { char[] charArray = name.toCharArray(); charArray[0] += 32; return String.valueOf(charArray); } @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) { // /enjoyMvc/hello/query String uri = req.getRequestURI(); // /enjoyMvc String content = req.getContextPath(); // /hello/query String path = uri.replaceAll(content, ""); try { // 根据 约定 去取对应调取的方法 Method method = (Method) handlerMap.get(path); // 根据约定 去取对应 Bean对象 Object bean = beans.get("/" + path.split("/")[1]); // 获取方法参数 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<?>[] paramClazzs = method.getParameterTypes(); // 保存参数值 Object paramValues[] = new Object[paramClazzs.length]; // 获取方法的参数列表 Class<?>[] parameterTypes = method.getParameterTypes(); // 方法的参数列表 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(); // 获取 自定义 EnjoyRequestParam 设置的值 如:name ,age Annotation[] paraAns = annotations[paramIndex]; for (Annotation paramAn : paraAns) { if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) { EnjoyRequestParam ep = (EnjoyRequestParam) paramAn; String value = ep.value(); return req.getParameter(value); } } return null; } }
一切就绪,开始启动Tomcat,在浏览器输入http://localhost:8080/enjoyMvc/hello/query?name=laolei&age=22。测试字写springmvc效果。
得到以下结构,则为成功