通过实现Mini版本的Spring,深入了解Spring的底层原理
这里用 application.properties 来代替 application.xml 文件,具体配置内容如下:
scanPackage=com.gupaoedu.demo所有依赖于 web 容器的项目,都是从读取 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/j2ee" xmlns:javaee="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/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>Gupao Web Application</display-name> <servlet> <servlet-name>gpmvc</servlet-name> <servlet-class>cn.xlystar.v1.servlet.XLYispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>gpmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>其中 XLYispatcherServlet 是有自己模拟 Spring 实现的核心功能类
@XLYController 注解:
package cn.xlystar.mvc.annotation; import java.lang.annotation.*; @Documented @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface XLYController { String value() default ""; }@XLYService 注解:
package cn.xlystar.mvc.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XLYService { String value() default ""; }@XLYAutowired 注解:
package cn.xlystar.mvc.annotation; import java.lang.annotation.*; @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XLYAutowired { String value() default ""; }@XLYRequestMapping 注解:
package cn.xlystar.mvc.annotation; import java.lang.annotation.*; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XLYRequestMapping { String value() default ""; }@XLYRequestParam注解:
package cn.xlystar.mvc.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XLYRequestParam { String value() default ""; }配置业务实现类 DemoService:
package cn.xlystar.demo.service.impl; import cn.xlystar.demo.service.DemoService; import cn.xlystar.mvc.annotation.XLYService; @XLYService public class DemoServiceImpl implements DemoService { }配置请求入口类 DemoController :
package cn.xlystar.demo.controller; import cn.xlystar.demo.service.DemoService; import cn.xlystar.mvc.annotation.XLYAutowired; import cn.xlystar.mvc.annotation.XLYController; import cn.xlystar.mvc.annotation.XLYRequestMapping; import cn.xlystar.mvc.annotation.XLYRequestParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @XLYController @XLYRequestMapping("/demo") public class DemoController { @XLYAutowired private DemoService demoService; @XLYRequestMapping("/query.*") public void query(HttpServletRequest req, HttpServletResponse resp, @XLYRequestParam("name") String name){ // String result = demoService.get(name); String result = "My name is " + name; try { resp.getWriter().write(result); } catch (IOException e) { e.printStackTrace(); } } @XLYRequestMapping("/add") public void add(HttpServletRequest req, HttpServletResponse resp, @XLYRequestParam("a") Integer a, @XLYRequestParam("b") Integer b){ try { resp.getWriter().write(a + "+" + b + "=" + (a + b)); } catch (IOException e) { e.printStackTrace(); } } @XLYRequestMapping("/sub") public void add(HttpServletRequest req, HttpServletResponse resp, @XLYRequestParam("a") Double a, @XLYRequestParam("b") Double b){ try { resp.getWriter().write(a + "-" + b + "=" + (a - b)); } catch (IOException e) { e.printStackTrace(); } } @XLYRequestMapping("/remove") public String remove(@XLYRequestParam("id") Integer id){ return "" + id; } }所有的核心逻辑都写在了init()中:
package cn.xlystar.mvc.v1.servlet; import cn.xlystar.mvc.annotation.XLYAutowired; import cn.xlystar.mvc.annotation.XLYController; import cn.xlystar.mvc.annotation.XLYRequestMapping; import cn.xlystar.mvc.annotation.XLYService; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class XLYDispatcherServlet extends HttpServlet { private Map<String, Object> mapping = new HashMap<String, Object>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { doDispatcher(req, resp); } catch (Exception e) { resp.getWriter().write("500 Exception" + Arrays.toString(e.getStackTrace())); } } private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException { String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replace(contextPath,"").replaceAll("/+","/"); if (!this.mapping.containsKey(url)) {resp.getWriter().write("404 Not Found!");return;} Method method = (Method) this.mapping.get(url); Map<String, String[]> paras = req.getParameterMap(); method.invoke(method.getDeclaringClass().getName(), new Object[]{req, resp, paras, paras.get("name")[0]}); } @Override public void init(ServletConfig config) throws ServletException { InputStream is = null; try { Properties configContext = new Properties(); is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation")); configContext.load(is); String scanPackage = configContext.getProperty("scanPackage"); doScanner(scanPackage); for (String className : mapping.keySet()) { if (!className.contains(".")) {continue;} Class<?> clazz = Class.forName(className); if(clazz.isAnnotationPresent(XLYController.class)){ mapping.put(className,clazz.newInstance()); String baseUrl = ""; if (clazz.isAnnotationPresent(XLYRequestMapping.class)) { XLYRequestMapping requestMapping = clazz.getAnnotation(XLYRequestMapping.class); baseUrl = requestMapping.value(); } Method[] methods = clazz.getMethods(); for (Method method : methods) { if (!method.isAnnotationPresent(XLYRequestMapping.class)) { continue; } XLYRequestMapping requestMapping = method.getAnnotation(XLYRequestMapping.class); String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/"); mapping.put(url, method); System.out.println("Mapped " + url + "," + method); } }else if(clazz.isAnnotationPresent(XLYService.class)){ XLYService service = clazz.getAnnotation(XLYService.class); String beanName = service.value(); if("".equals(beanName)){beanName = clazz.getName();} Object instance = clazz.newInstance(); mapping.put(beanName,instance); for (Class<?> i : clazz.getInterfaces()) { mapping.put(i.getName(),instance); } }else {continue;} } for (Object object : mapping.values()) { if(object == null){continue;} Class clazz = object.getClass(); if(clazz.isAnnotationPresent(XLYController.class)){ Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if(!field.isAnnotationPresent(XLYAutowired.class)){continue; } XLYAutowired autowired = field.getAnnotation(XLYAutowired.class); String beanName = autowired.value(); if("".equals(beanName)){beanName = field.getType().getName();} field.setAccessible(true); try { field.set(mapping.get(clazz.getName()),mapping.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } catch (Exception e) { }finally { if(is != null){ try {is.close();} catch (IOException e) { e.printStackTrace(); } } } System.out.print("GP MVC Framework is init"); } private void doScanner(String scanPackage) { URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/")); File fileDir = new File(url.getFile()); for (File file : fileDir.listFiles()) { if (file.isDirectory()) { doScanner(scanPackage+"."+file.getName()); } else { if (!file.getName().endsWith(".class")) {continue;} String className = (scanPackage +"."+file.getName().replace(".class","")); mapping.put(className,null); } } } }在 V1 版本上进了优化,采用了常用的设计模式(工厂模式、单例模式、委派模式、策略模式),将 init()方法中的代 码进行封装装。按照之前的实现思路,先搭基础框架,再填肉注血,具体代码如下:
package cn.xlystar.mvc.v2.servlet; import cn.xlystar.mvc.annotation.*; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.*; public class XLYDIspatcherServlet extends HttpServlet { //存储aplication.properties的配置内容 private Properties contextConfig = new Properties(); // 扫描到的类名: x.x.x.Xxx private List<String> classNames = new ArrayList<String>(); // IOC容器,保存实例化对象 <xxx, new Xxx> private Map<String, Object> ioc = new HashMap<String, Object>(); // Controller中的Mapping对应关系 private Map<String, Method> handlerMapping = new HashMap<String, Method>(); @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 { try { doDispatch(req, resp); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException, IOException { String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replaceAll(contextPath,"").replaceAll("/+","/"); if(!this.handlerMapping.containsKey(url)){ resp.getWriter().write("404 Not Found!!"); return; } Method method = this.handlerMapping.get(url); //第一个参数:方法所在的实例 //第二个参数:调用时所需要的实参 Map<String,String[]> params = req.getParameterMap(); //获取方法的形参列表 Class<?> [] parameterTypes = method.getParameterTypes(); //保存请求的url参数列表 Map<String,String[]> parameterMap = req.getParameterMap(); //保存赋值参数的位置 Object [] paramValues = new Object[parameterTypes.length]; //按根据参数位置动态赋值 for (int i = 0; i < parameterTypes.length; i ++){ Class parameterType = parameterTypes[i]; if(parameterType == HttpServletRequest.class){ paramValues[i] = req; continue; }else if(parameterType == HttpServletResponse.class){ paramValues[i] = resp; continue; }else if(parameterType == String.class){ XLYRequestParam requestParam = (XLYRequestParam)parameterType.getAnnotation(XLYRequestParam.class); if(parameterMap.containsKey(requestParam.value())) { for (Map.Entry<String,String[]> param : parameterMap.entrySet()){ String value = Arrays.toString(param.getValue()) .replaceAll("\\[|\\]","") .replaceAll("\\s",","); paramValues[i] = value; } } } } //投机取巧的方式 String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName()); method.invoke(ioc.get(beanName),paramValues); } @Override public void init(ServletConfig servletConfig) throws ServletException { // 1. 加载配置文件 doLoadConfig(servletConfig.getInitParameter("contextConfigLocation")); // 2. 扫描相关的类 doScanner(contextConfig.getProperty("scanPackage")); // 3. 初始化所有相关类的实例,放入IOC容器中 doInstance(); // 4. 完成依赖注入DI doAutowired(); // 5. 初始化handlerMapping intitHandleMapping(); System.out.println("XLY Spring Framework is init..."); } private void intitHandleMapping() { if(ioc.isEmpty()){ return; } for (Map.Entry<String, Object> entry : ioc.entrySet()) { Class<?> clazz = entry.getValue().getClass(); if(!clazz.isAnnotationPresent(XLYController.class)){ continue; } String baseUrl = ""; //获取Controller的url配置 if(clazz.isAnnotationPresent(XLYRequestMapping.class)){ XLYRequestMapping requestMapping = clazz.getAnnotation(XLYRequestMapping.class); baseUrl = requestMapping.value(); } //获取Method的url配置 Method[] methods = clazz.getMethods(); for (Method method : methods) { //没有加RequestMapping注解的直接忽略 if(!method.isAnnotationPresent(XLYRequestMapping.class)){ continue; } //映射URL XLYRequestMapping requestMapping = method.getAnnotation(XLYRequestMapping.class); // /demo/query // (//demo//query) String url = ("/" + baseUrl + "/" + requestMapping.value()) .replaceAll("/+", "/"); handlerMapping.put(url,method); System.out.println("Mapped " + url + "," + method); } } } private void doAutowired() { if (ioc.isEmpty()) {return;} for (Map.Entry<String, Object> entry : ioc.entrySet()) { Field[] declaredFields = entry.getValue().getClass().getDeclaredFields(); for (Field field : declaredFields) { if (!field.isAnnotationPresent(XLYAutowired.class)) { return; } String trim = field.getAnnotation(XLYAutowired.class).value().trim(); if ("".equals(trim)) { trim = field.getType().getName(); } field.setAccessible(true); try { field.set(entry.getValue(), ioc.get(trim)); } catch (IllegalAccessException e) { e.printStackTrace(); continue; } } } } private void doInstance() { if (classNames.isEmpty()) { return; } try { for (String className : classNames) { Class<?> aClass = Class.forName(className); if (aClass.isAnnotationPresent(XLYController.class)) { Object o = aClass.newInstance(); String beanName = toLowerFirstCase(aClass.getSimpleName()); ioc.put(beanName, o); } else if (aClass.isAnnotationPresent(XLYService.class)) { //1、默认的类名首字母小写 String beanName = toLowerFirstCase(aClass.getSimpleName()); //2、自定义命名 XLYService service = aClass.getAnnotation(XLYService.class); if(!"".equals(service.value())){ beanName = service.value(); } Object instance = aClass.newInstance(); ioc.put(beanName, instance); for (Class<?> i : aClass.getInterfaces()) { if (ioc.containsKey(i.getName())) { throw new Exception("The beanName is exists!!"); } ioc.put(i.getName(),instance); } } else { continue; } } } catch (Exception e) { e.printStackTrace(); } } private String toLowerFirstCase(String simpleName) { char[] chars = simpleName.toCharArray(); chars[0] += 32; return String.valueOf(chars); } private void doScanner(String scanPackage) { URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/")); File dir = new File(url.getFile()); for (File file : dir.listFiles()) { if (file.isDirectory()) { doScanner(scanPackage + "." + file.getName()); } else { if (!file.getName().endsWith(".class")) { continue; } String className = (scanPackage + "." + file.getName().replace(".class", "")); classNames.add(className); } } } private void doLoadConfig(String contextConfigLocation) { InputStream fs = null; try { fs = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); contextConfig.load(fs); } catch (IOException e) { e.printStackTrace(); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } } }在 V2 版本中,基本功能以及完全实现,但代码的优雅程度还不如人意。譬如 HandlerMapping 还不能像 SpringMVC 一样支持正则,url 参数还不支持强制类型转换,在反射调用前还需要重新获取 beanName,在 V3 版本中,我们继续优化。 首先,改造 HandlerMapping,在真实的 Spring 源码中,HandlerMapping 其实是一个 List 而非 Map。List 中的元 素是一个自定义的类型。现在我们来仿真写一段代码,先定义一个内部类 Handler 类:
package cn.xlystar.mvc.v3.servlet; import cn.xlystar.mvc.annotation.*; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class XLYDispatcherServlet extends HttpServlet { //保存application.properties配置文件中的内容 private Properties contextConfig = new Properties(); //保存扫描的所有的类名 private List<String> classNames = new ArrayList<String>(); //传说中的IOC容器,我们来揭开它的神秘面纱 //为了简化程序,暂时不考虑ConcurrentHashMap // 主要还是关注设计思想和原理 private Map<String,Object> ioc = new HashMap<String,Object>(); //保存url和Method的对应关系 // private Map<String,Method> handlerMapping = new HashMap<String,Method>(); //思考:为什么不用Map //你用Map的话,key,只能是url //Handler 本身的功能就是把url和method对应关系,已经具备了Map的功能 //根据设计原则:冗余的感觉了,单一职责,最少知道原则,帮助我们更好的理解 private List<Handler> handlerMapping = new ArrayList<Handler>(); @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 { //6、调用,运行阶段 try { doDispatch(req,resp); } catch (Exception e) { e.printStackTrace(); resp.getWriter().write("500 Exection,Detail : " + Arrays.toString(e.getStackTrace())); } } private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { Handler handler = getHandler(req); if(handler == null){ // if(!this.handlerMapping.containsKey(url)){ resp.getWriter().write("404 Not Found!!!"); return; } //获得方法的形参列表 Class<?> [] paramTypes = handler.getParamTypes(); Object [] paramValues = new Object[paramTypes.length]; Map<String,String[]> params = req.getParameterMap(); for (Map.Entry<String, String[]> parm : params.entrySet()) { String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]","") .replaceAll("\\s",","); if(!handler.paramIndexMapping.containsKey(parm.getKey())){continue;} int index = handler.paramIndexMapping.get(parm.getKey()); paramValues[index] = convert(paramTypes[index],value); } if(handler.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) { int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName()); paramValues[reqIndex] = req; } if(handler.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) { int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName()); paramValues[respIndex] = resp; } Object returnValue = handler.method.invoke(handler.controller,paramValues); if(returnValue == null || returnValue instanceof Void){ return; } resp.getWriter().write(returnValue.toString()); } private Handler getHandler(HttpServletRequest req) { if(handlerMapping.isEmpty()){return null;} //绝对路径 String url = req.getRequestURI(); //处理成相对路径 String contextPath = req.getContextPath(); url = url.replaceAll(contextPath,"").replaceAll("/+","/"); for (Handler handler : this.handlerMapping) { Matcher matcher = handler.getPattern().matcher(url); if(!matcher.matches()){ continue;} return handler; } return null; } //url传过来的参数都是String类型的,HTTP是基于字符串协议 //只需要把String转换为任意类型就好 private Object convert(Class<?> type,String value){ //如果是int if(Integer.class == type){ return Integer.valueOf(value); } else if(Double.class == type){ return Double.valueOf(value); } //如果还有double或者其他类型,继续加if //这时候,我们应该想到策略模式了 //在这里暂时不实现,希望小伙伴自己来实现 return value; } //初始化阶段 @Override public void init(ServletConfig config) throws ServletException { //1、加载配置文件 doLoadConfig(config.getInitParameter("contextConfigLocation")); //2、扫描相关的类 doScanner(contextConfig.getProperty("scanPackage")); //3、初始化扫描到的类,并且将它们放入到ICO容器之中 doInstance(); //4、完成依赖注入 doAutowired(); //5、初始化HandlerMapping initHandlerMapping(); System.out.println("XLY Spring framework is init."); } //初始化url和Method的一对一对应关系 private void initHandlerMapping() { if(ioc.isEmpty()){ return; } for (Map.Entry<String, Object> entry : ioc.entrySet()) { Class<?> clazz = entry.getValue().getClass(); if(!clazz.isAnnotationPresent(XLYController.class)){continue;} //保存写在类上面的@XLYRequestMapping("/demo") String baseUrl = ""; if(clazz.isAnnotationPresent(XLYRequestMapping.class)){ XLYRequestMapping requestMapping = clazz.getAnnotation(XLYRequestMapping.class); baseUrl = requestMapping.value(); } //默认获取所有的public方法 for (Method method : clazz.getMethods()) { if(!method.isAnnotationPresent(XLYRequestMapping.class)){continue;} XLYRequestMapping requestMapping = method.getAnnotation(XLYRequestMapping.class); //优化 String regex = ("/" + baseUrl + "/" + requestMapping.value()) .replaceAll("/+","/"); Pattern pattern = Pattern.compile(regex); this.handlerMapping.add(new Handler(pattern,entry.getValue(),method)); // handlerMapping.put(url,method); System.out.println("Mapped :" + pattern + "," + method); } } } //自动依赖注入 private void doAutowired() { if(ioc.isEmpty()){return;} for (Map.Entry<String, Object> entry : ioc.entrySet()) { //Declared 所有的,特定的 字段,包括private/protected/default //正常来说,普通的OOP编程只能拿到public的属性 Field[] fields = entry.getValue().getClass().getDeclaredFields(); for (Field field : fields) { if(!field.isAnnotationPresent(XLYAutowired.class)){continue;} XLYAutowired autowired = field.getAnnotation(XLYAutowired.class); //如果用户没有自定义beanName,默认就根据类型注入 //这个地方省去了对类名首字母小写的情况的判断,这个作为课后作业 //小伙伴们自己去完善 String beanName = autowired.value().trim(); if("".equals(beanName)){ //获得接口的类型,作为key待会拿这个key到ioc容器中去取值 beanName = field.getType().getName(); } //如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值 //反射中叫做暴力访问, 强吻 field.setAccessible(true); try { //用反射机制,动态给字段赋值 field.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } private void doInstance() { //初始化,为DI做准备 if(classNames.isEmpty()){return;} try { for (String className : classNames) { Class<?> clazz = Class.forName(className); //什么样的类才需要初始化呢? //加了注解的类,才初始化,怎么判断? //为了简化代码逻辑,主要体会设计思想,只举例 @Controller和@Service, // @Componment...就一一举例了 if(clazz.isAnnotationPresent(XLYController.class)){ Object instance = clazz.newInstance(); //Spring默认类名首字母小写 String beanName = toLowerFirstCase(clazz.getSimpleName()); ioc.put(beanName,instance); }else if(clazz.isAnnotationPresent(XLYService.class)){ //1、自定义的beanName XLYService service = clazz.getAnnotation(XLYService.class); String beanName = service.value(); //2、默认类名首字母小写 if("".equals(beanName.trim())){ beanName = toLowerFirstCase(clazz.getSimpleName()); } Object instance = clazz.newInstance(); ioc.put(beanName,instance); //3、根据类型自动赋值,投机取巧的方式 for (Class<?> i : clazz.getInterfaces()) { if(ioc.containsKey(i.getName())){ throw new Exception("The “" + i.getName() + "” is exists!!"); } //把接口的类型直接当成key了 ioc.put(i.getName(),instance); } }else { continue; } } }catch (Exception e){ e.printStackTrace(); } } //如果类名本身是小写字母,确实会出问题 //但是我要说明的是:这个方法是我自己用,private的 //传值也是自己传,类也都遵循了驼峰命名法 //默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况 //为了简化程序逻辑,就不做其他判断了,大家了解就OK //其实用写注释的时间都能够把逻辑写完了 private String toLowerFirstCase(String simpleName) { char [] chars = simpleName.toCharArray(); //之所以加,是因为大小写字母的ASCII码相差32, // 而且大写字母的ASCII码要小于小写字母的ASCII码 //在Java中,对char做算学运算,实际上就是对ASCII码做算学运算 chars[0] += 32; return String.valueOf(chars); } //扫描出相关的类 private void doScanner(String scanPackage) { //scanPackage = com.gupaoedu.demo ,存储的是包路径 //转换为文件路径,实际上就是把.替换为/就OK了 //classpath URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/")); File classPath = new File(url.getFile()); for (File file : classPath.listFiles()) { if(file.isDirectory()){ doScanner(scanPackage + "." + file.getName()); }else{ if(!file.getName().endsWith(".class")){ continue;} String className = (scanPackage + "." + file.getName().replace(".class","")); classNames.add(className); } } } //加载配置文件 private void doLoadConfig(String contextConfigLocation) { //直接从类路径下找到Spring主配置文件所在的路径 //并且将其读取出来放到Properties对象中 //相对于scanPackage=com.gupaoedu.demo 从文件中保存到了内存中 InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { contextConfig.load(fis); } catch (IOException e) { e.printStackTrace(); }finally { if(null != fis){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } //保存一个url和一个Method的关系 public class Handler { //必须把url放到HandlerMapping才好理解吧 private Pattern pattern; //正则 private Method method; private Object controller; private Class<?> [] paramTypes; public Pattern getPattern() { return pattern; } public Method getMethod() { return method; } public Object getController() { return controller; } public Class<?>[] getParamTypes() { return paramTypes; } //形参列表 //参数的名字作为key,参数的顺序,位置作为值 private Map<String,Integer> paramIndexMapping; public Handler(Pattern pattern, Object controller, Method method) { this.pattern = pattern; this.method = method; this.controller = controller; paramTypes = method.getParameterTypes(); paramIndexMapping = new HashMap<String, Integer>(); putParamIndexMapping(method); } private void putParamIndexMapping(Method method){ //提取方法中加了注解的参数 //把方法上的注解拿到,得到的是一个二维数组 //因为一个参数可以有多个注解,而一个方法又有多个参数 Annotation[] [] pa = method.getParameterAnnotations(); for (int i = 0; i < pa.length ; i ++) { for(Annotation a : pa[i]){ if(a instanceof XLYRequestParam){ String paramName = ((XLYRequestParam) a).value(); if(!"".equals(paramName.trim())){ paramIndexMapping.put(paramName, i); } } } } //提取方法中的request和response参数 Class<?> [] paramsTypes = method.getParameterTypes(); for (int i = 0; i < paramsTypes.length ; i ++) { Class<?> type = paramsTypes[i]; if(type == HttpServletRequest.class || type == HttpServletResponse.class){ paramIndexMapping.put(type.getName(),i); } } } // private } }至此,Mini版本Spring已然完成!