网上不少关于springmvc的分析,但不多有手动实现的,这里经过一个简单的demo实现mvc基本功能,仅供学习参考。java
咱们先简单了解下springmvc请求流程,以下图:
从图上得知,最早处理请求的是dispatcherServlet,它接受请求并查询获得拦截器链HandlerExecutionChain,HandlerAdapter通过适配器调用具体的处理器(Controller).web
查看源码能够到spring-webmvc.jar中,org.springframework.web.servlet/DispatcherServlet.class,其中方法doDispatch()完成了一个请求到返回数据的完整操做.spring
下面咱们开始动手了,先建立一个javaweb工程,写一个Servlet,以下:浏览器
@WebServlet(urlPatterns = "/*", loadOnStartup = 1) public class DispatcherServlet extends HttpServlet { private List<String> clzList = new ArrayList<>(); private Map<String, Object> beansMap = new HashMap<>(); private Map<String, Object> urlMapping = new HashMap<>(); @Override public void init() throws ServletException { try { //扫描配置 这里是包名 scanPackages("com.iti.smvc"); //实例化对象 doInstance(); //创建对象之间的依赖ioc ioc(); //创建url到controller的映射 doMapping(); } catch (Exception e) { e.printStackTrace(); } } }
注:servlet3.0再也不须要web.xml, 这里将成员变量写入servlet并非很好的实现,会致使线程不安全
这个servlet要作一些初始化的工做,如:
1.扫描包名,将class对象装入clzList列表
2.遍历clzList列表,实例化有Controller和Service标注的类
3.依赖注入,将service注入到controller
4.创建url与controller中方法url的mapping关系安全
咱们依次来实现他们:
下面的是扫描配置方法mvc
private void scanPackages(String packageUrl) { String fileUrl = getClass().getClassLoader().getResource("/"+packageUrl.replaceAll("\\.", "/")).getFile(); File scanfile = new File(fileUrl); String[] fileList = scanfile.list(); for (String fileName: fileList) { File file = new File(fileUrl+fileName); if (file.isDirectory()) { scanPackages(packageUrl + "." + fileName);; } else { clzList.add(packageUrl + "." + fileName.replace(".class", "")); } } }
接着是对象实例化:app
private void doInstance() throws Exception{ if (clzList.size()>0) { for (String clzName : clzList) { Class<?> clzClass = Class.forName(clzName); if (clzClass.isAnnotationPresent(Controller.class)) { //for controller RequestMapping rm = clzClass.getAnnotation(RequestMapping.class); beansMap.put(rm.value(), clzClass.newInstance()); } else if (clzClass.isAnnotationPresent(Service.class)) { Service service = clzClass.getAnnotation(Service.class); beansMap.put(service.value(), clzClass.newInstance()); } } } }
接着是依赖注入,实际上是反射:ide
private void ioc() throws Exception{ if (beansMap.size()>0) { for (Map.Entry<String, Object> entry : beansMap.entrySet()) { Field[] fields = entry.getValue().getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Autowired.class)) { Qualifier anno = field.getAnnotation(Qualifier.class); field.set(entry.getValue(), beansMap.get(anno.value())); } } } } }
最后是创建请求url与controller的mapping关系,以下:学习
private void doMapping() { if (beansMap.size()>0) { for (Map.Entry<String, Object> entry: beansMap.entrySet()) { Class<? extends Object> obj = entry.getValue().getClass(); if (obj.isAnnotationPresent(Controller.class)){ RequestMapping rm = obj.getAnnotation(RequestMapping.class); Method[] methods = obj.getMethods(); for (Method method: methods) { if (method.isAnnotationPresent(RequestMapping.class)) { RequestMapping anno = method.getAnnotation(RequestMapping.class); urlMapping.put(rm.value() + anno.value(), method); } } } } } }
还要把注解建立下:测试
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Controller { String value() default ""; } @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Qualifier { String value() default ""; } @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestMapping { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Service { String value() default ""; }
此时就能够启动服务完成初始化工做了
下面咱们要建立一个sevice方法来接收url请求
@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String requestURI = req.getRequestURI(); String contextPath = req.getContextPath(); //获得请求地址 String path = requestURI.replace(contextPath, ""); Method method = (Method)urlMapping.get(path); if (method != null) { try { Object obj = method.invoke(beansMap.get("/"+path.split("/")[1])); resp.getOutputStream().write(obj.toString().getBytes()); } catch (Exception e) { e.printStackTrace(); } } }
上面是获得请求url,而后从urlMapping获得要调用的method,再经过反射调用,最后经过outputStream输出到浏览器上。
下面编写一个controller和service测试下
controller代码以下:
@Controller @RequestMapping("/hello") public class HelloController { @Autowired @Qualifier("helloservice") Helloservice helloservice; @RequestMapping("/sayHello") public String sayHello() { //System.out.println(helloservice.hello()); return "controller"; } }
service代码以下:
@Service("helloservice") public class Helloservice { public String hello() { return "hello"; } }
请求url:http://localhost:8080/hello/sayHello学习交流,欢迎加群:64691032