手写实现Spring(IOC、DI),SpringMVC功能

spring和springMVC的用法相信大家都不陌生,我简单讲下我实现的思路

spring阶段 事项
配置 配置web.xml: init-param和Servlet
初始化启动 读取web.xml,加载spring配置文件init()
获取配置文件里的扫包路径,扫描得到相关的class集合
实例化相关的实体类,注入到ioc容器中
依赖注入(byType or byName)
获取handlerMaping url和method的映射关系
执行转发 获取url请求地址
根据url匹配对应的controller的method
通过反射调用对应的method,获取controller的返回结果,解析

代码

package com.chenpp.spring.web.servlet;public class CPDispatcherServlet extends HttpServlet{//存储对应的配置文件信息(config.properties)private static ConcurrentHashMap<String, String> initParams = new  ConcurrentHashMap<String, String>();//ioc容器:实例化classprivate static ConcurrentHashMap<String, Object>  iocMap = new  ConcurrentHashMap<String, Object>();//存储url和对应的方法的Mapping关系private static ConcurrentHashMap<Pattern, MethodMapping>  urlMethodMap = new  ConcurrentHashMap<Pattern, MethodMapping>();//存储扫描到的包路径下class类private static List<Class<?>> classes = new ArrayList<>();@Overridepublic void init(ServletConfig servletConfig) throws ServletException {//1.根据web.xml配置的init-param获取配置文件路径,读取扫包路径parseConfig(servletConfig);//2.根据扫包路径,递归获取到所有需要扫描的class[]doScanner(initParams.get(Constants.PACKAGE_SCANNING));//3.初始化@CPService和@CPController的beans,放入到IOC容器中initBeans(classes);//4.对使用了@CPAutowire注解的属性值进行依赖注入(反射机制)doDI();//5.遍历所有的@CPController的类和其上的方法,对url和method进行映射handlerMapping();System.out.println("spring 启动加载完成...........");}private void parseConfig(ServletConfig servletConfig) {String location = servletConfig.getInitParameter(Constants.CONTEXT_CONFIG_LOCATION);InputStream in = this.getClass().getClassLoader().getResourceAsStream(location);Properties properties = new Properties();try {properties.load(in);} catch (IOException e) {e.printStackTrace();}finally{try {if(in != null){in.close();}} catch (IOException e) {e.printStackTrace();}}//遍历properties,保存到initParamMap里for(Object key:properties.keySet()){Object value = properties.get(key);initParams.put(key.toString(), value.toString());}}private void doScanner(String packageName) {//根据path遍历所有class文件URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.","/"));File dir = new File(url.getFile());try{for(File file:dir.listFiles()){if(file.isDirectory()){//file是目录,递归doScanner(packageName + "." + file.getName());}else{//获取class文件路径和文件名String className = packageName + "." + file.getName().replace(".class", "").trim();Class<?> classFile  = Class.forName(className);classes.add(classFile);}}}catch (Exception e){}}private void initBeans(List<Class<?>> classes){try{//遍历所有的class文件,判断其上是否有CPController和CPService注解for(Class<?> clazz :classes){//对于CPController只需要存储beanName和对应bean的关系即可(一般不用于注入)if(clazz.isAnnotationPresent(CPController.class)){CPController controller = clazz.getAnnotation(CPController.class);String beanName = controller.value();//如果有定义属性值,以其为beanName,否则默认类名首字母小写if(StringUtils.isEmpty(beanName)){beanName = StringUtils.toFirstLowChar(clazz.getSimpleName());}iocMap.put(beanName, clazz.newInstance());continue;}else if(clazz.isAnnotationPresent(CPService.class)){//CPService注入有根据beanName和类型两种(接口类型)CPService service = clazz.getAnnotation(CPService.class);String beanName = service.value();//如果有定义属性值,以其为beanName,否则默认类名首字母小写if(StringUtils.isEmpty(beanName)){beanName = StringUtils.toFirstLowChar(clazz.getSimpleName());iocMap.put(beanName, clazz.newInstance());}//按照类型存储一个实例关系(为了方便按照类型注入)iocMap.put(clazz.getName(), clazz.newInstance());//按照接口的类型再存储一个实例关系(注入的时候方便按照接口类型来注入)Class<?>[] interfaces = clazz.getInterfaces();for (Class<?> i : interfaces) {iocMap.put(i.getName(), clazz.newInstance());}}//TODO ...其他关于Component的注解就先不考虑}}catch (Exception e){}}//执行依赖注入private void doDI()  {try{//遍历所有iocMap里的实例集合,判断其属性字段上是否有@CPAutowire注解for(Map.Entry<String,Object> entry:iocMap.entrySet()){Class<?> clazz = entry.getValue().getClass();Field[] fields = clazz.getDeclaredFields();for(Field field:fields){//CPAutowire默认使用byType的方式装配CPAutowire autoAnnotation = field.getAnnotation(CPAutowire.class);field.setAccessible(true);if(autoAnnotation != null ){CPQualifier qualifier = field.getAnnotation(CPQualifier.class);if(qualifier != null && !StringUtils.isEmpty(qualifier.value())){//按照名字注入field.set(entry.getValue(), iocMap.get(qualifier.value()));continue;}//否则按照类型注入field.set(entry.getValue(), iocMap.get(field.getType().getName()));}}}}catch (Exception e){}}private void handlerMapping() {for(Map.Entry<String,Object> entry:iocMap.entrySet()){Class<?> clazz = entry.getValue().getClass();//判断Controller类上是否有CPRequestMapping注解if(!clazz.isAnnotationPresent(CPRequestMapping.class)) continue;String baseUrl = clazz.getAnnotation(CPRequestMapping.class).value();Method[] methods = clazz.getDeclaredMethods();//遍历CPController上的Method 获取url与MethodMapping的映射关系for(Method method:methods){String methodUrl ="";if(method.isAnnotationPresent(CPRequestMapping.class)){methodUrl = method.getAnnotation(CPRequestMapping.class).value();}String regex = ("/"+baseUrl+methodUrl).replaceAll("/+", "/");Pattern pattern = Pattern.compile(regex);MethodMapping model = new MethodMapping(entry.getValue(),method);urlMethodMap.put(pattern, model);}}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {try{this.doDispatcher(req, resp);}catch (Exception e){resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));}}private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取实际的url请求String url = req.getRequestURI();String contextPath = req.getContextPath();url = url.replace(contextPath, "").replaceAll("/+", "/");//根据url请求去获取响应的MethodBean对象MethodMapping methodMapping = null;for(Pattern pattern: urlMethodMap.keySet()){if(pattern.matcher(url).matches()){methodMapping = urlMethodMap.get(pattern);}}//如果找不到匹配的url,则直接返回404if(methodMapping == null){resp.getWriter().println("404 not found");resp.flushBuffer();return;}//获取方法的参数类型 列表Class<?> [] paramTypes = methodMapping.getMethod().getParameterTypes();//用于存储实际的参数列表Object [] paramValues = new Object[paramTypes.length];//获取请求的参数列表(Request请求里的参数都是字符串类型的,如果一个参数出现多次,那么它的value就是String数组)Map<String,String[]> params = req.getParameterMap();for (Map.Entry<String, String[]> param : params.entrySet()) {//将数组参数转化为stringString value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");//如果找到匹配的参数名,则开始填充参数数组paramValuesif(!methodMapping.getParamIndexMapping().containsKey(param.getKey())){continue;}int index = methodMapping.getParamIndexMapping().get(param.getKey());paramValues[index] = convert(paramTypes[index],value);}//设置方法中的request和response对象if(methodMapping.getParamIndexMapping().containsKey(HttpServletRequest.class.getName())){int reqIndex = methodMapping.getParamIndexMapping().get(HttpServletRequest.class.getName());paramValues[reqIndex] = req;}if(methodMapping.getParamIndexMapping().containsKey(HttpServletResponse.class.getName())) {int respIndex = methodMapping.getParamIndexMapping().get(HttpServletResponse.class.getName());paramValues[respIndex] = resp;}//执行方法获得返回值Object  returnValue = "";try {returnValue = methodMapping.getMethod().invoke(methodMapping.getController(),paramValues);//如果方法有加CPResponseBody注解,则直接返回结果 TODO Controller上也可加,这里就没考虑这种情形if(methodMapping.getMethod().isAnnotationPresent(CPResponseBody.class)){resp.getWriter().println(returnValue);return;}//否则根据配置文件里配置的视图进行转发req.getRequestDispatcher(initParams.get(Constants.PAGE_PREFIX)+returnValue+initParams.get(Constants.PAGE_SUFFIX)).forward(req, resp);} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}/*** 转化参数类型,将String转化为实际的参数类型* */private Object convert(Class<?> paramType, String value) {if( int.class == paramType || Integer.class == paramType){return Integer.valueOf(value);}if( double.class == paramType || Double.class == paramType){return Double.valueOf(value);}//TODO 这里只是列举了几种常用的,可以继续完善...return value;}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {this.doPost(req,resp);}
}

public class MethodMapping {private  Method method;private Object controller;protected Map<String,Integer> paramIndexMapping; //参数顺序public Method getMethod() {return method;}public void setMethod(Method method) {this.method = method;}public Object getController() {return controller;}public void setController(Object controller) {this.controller = controller;}public Map<String, Integer> getParamIndexMapping() {return paramIndexMapping;}public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {this.paramIndexMapping = paramIndexMapping;}public MethodMapping(Object controller, Method method){this.controller = controller;this.method = method;paramIndexMapping = new HashMap<String,Integer>();putParamIndexMapping(method);}/*** 根据方法获取对应参数和下标的Mapping** */private void putParamIndexMapping(Method method) {//遍历Method中的所有参数,获取其对应的参数名和下标Parameter[] params = method.getParameters();for(int i = 0 ; i < params.length ; i++){Class<?> type = params[i].getType();if(type == HttpServletRequest.class || type == HttpServletResponse.class){paramIndexMapping.put(type.getName(),i);continue;}Annotation[] annotations = params[i].getAnnotations();String paramName  = getAnnotationParamName(annotations);if(StringUtils.isEmpty(paramName)){//想要通过反射获取参数名而不是arg0,需要在编译时指定“-parameters”选项paramName = params[i].getName();}paramIndexMapping.put(paramName,i);}}private String getAnnotationParamName(Annotation[] annotations){for(Annotation a : annotations) {if (a instanceof CPRequestParam) {return ((CPRequestParam) a).value();}}return "";}}

执行效果:



源码地址:
https://github.com/dearfulan/cp-springmvc/tree/master/

手写实现Spring(IOC、DI),SpringMVC基础功能相关推荐

  1. 手把手教你写一个spring IOC容器

    本文分享自华为云社区<手把手教你写一个spring IOC容器>,原文作者:技术火炬手. spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功 ...

  2. 从 0 开始手写一个 Spring MVC 框架,向高手进阶

    转载自   从 0 开始手写一个 Spring MVC 框架,向高手进阶 Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的,但了解之后才知道有很多巧妙的设计在里面.如 ...

  3. 自己手写一个Spring MVC框架

    想要了解Spring MVC框架的原理,探究框架是如何设计的,不错的学习方式是阅读源码,然后自己手写一个框架.本文带领大家简化的手写一个Spring MVC框架. Spring框架对于Java后端程序 ...

  4. spring源码分析01-(前期准备)spring核心原理解析和手写简易spring

    1.本文主要介绍内容 本文会把Spring中核心知识点大概解释下.可以对Spring的底层有一个整体的大致了解.主要内容包括: 手写简易spring框架,帮助更好理解spring. 代码点击链接自取 ...

  5. 手写模拟spring扫描底层实现

    手写模拟spring扫描底层实现 前言 1.ApplicationContext和AppConfig 2.@Component和@ComponentScan注解 3.UserService和Test ...

  6. springaop事务逻辑原理_太狠了!阿里大牛手写的Spring核心面试笔记:IOC+AOP+MVC+事务...

    Spring作为现在最流行的java 开发技术,其内部源码设计非常优秀.如果你不会Spring,那么很可能面试官会让你回家等通知. Spring是什么? 有一个工地,几百号人在用铁锹铲子挖坑. 如果开 ...

  7. 玩转Spring——Spring IOC/DI

    什么是IOC ioc :Inversion of Control,即控制反转. 它不是一种技术,而是一种设计思想,即java程序中获取对象的方式发生反转,由最初的new方式创建,转变成由第三方框架创建 ...

  8. spring ioc di 原理解析

    spring ioc原理(看完后大家可以自己写一个spring) 控制反转/依赖注入 其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.Java程序员都知道:java程序中 ...

  9. 我居然手写了Spring框架

    手写完了 刚参加工作那会接触java还是用的struct的时代,后面在SSH火爆时代的时候我转战.net,多年之后公司转java技术栈已经是Spring的天下,源码嚼了很多遍于是很想尝试把这套东西用在 ...

最新文章

  1. 跨境电商三单对碰三单申报流程详解
  2. “云计算+DevOps”的正确打开方式
  3. 使用OAuth 2 / OpenID Connect的SSO的Spring Boot 2本机方法
  4. 密码与确认密码自动验证html,HTML确认密码
  5. 关于MFC自动生成的各个类的指针访问
  6. 苹果发布 iOS 和 macOS 更新,修复已遭利用0day
  7. ReportViewer 安装
  8. 19电子设计速成实战宝典pdf_开发宝典丛书:Visual C++编程实战宝典PDF
  9. Atitit 提升可读性 流畅接口 1.1. 大接口vs 小接口 小接口可用流畅api串接起来 1 1.2. 部分comm fun可用大接口固化 1 2. 流畅接口 方法连 “Fluent接口
  10. 程序人生之常见术语与名词解释
  11. 实现手风琴抽屉式网页特效
  12. 机器学习--人口普查数据分析
  13. 刘林仙版《薛刚反唐》整理
  14. java刮刮乐,Canvas实现简单刮刮乐效果
  15. 尝试在Mac上编译DNX
  16. Go学习之编码实现区块链 - 【blockchain】
  17. 全年营业额怎么计算_怎么查看一个公司的年营业额?
  18. 重置form表单中的input值
  19. 【java】关于java编程语言开发
  20. windows系统修复 | 使用 System File Checker (SFC) scannow 命令修复问题

热门文章

  1. cookie和session常见问题
  2. IDEA同时使用maven和gradle
  3. Feign深入学习(一)
  4. 200822C阶段一文件
  5. Data Lake Analytics: 使用DataWorks来调度DLA任务
  6. 基于 DataLakeAnalytics 做跨地域的数据分析 1
  7. mybatis中mapUnderscoreToCamelCase自动驼峰命名转换
  8. 粘性控件,滑动停留StickLayout(导航栏滑动停留)
  9. 双系统Ubuntu无法访问windows磁盘分区解决方法
  10. 面向连接的套接字通信工作流程