[02][02][01] 用30个类手写Spring V2.0版本之顶层设计IOC与DI

[TOC]

在完全掌握 Spring 系统结构,实现原理,在理解设计模式的基础上,自己动手写一个高仿真版本的 Spring 框架,以达彻理解 Spring 的目的,感受作者创作意图

1. 从 Servlet 到 Applicationcontext

在 300 行代码提炼 Spring 设计精华的课程中我们已经了解 SpringMVC 的入口是 DispatcherSerlvet,我们实现了 DispatcherServlet 的 init() 方法.在 init() 方法中完成了 IOC 容器的初始化,而在我们使用 Spring 的经验中,我们见得最多的是 Applicationcontext,似乎 Spring 托管的所有实例 Bean 都可以通过调用 getBean() 方法枚得.那么 Applicationcontext 又是从何而来的呢?从 Spring 源码中我们可以看到,DispatcherServlet 的类图如下

DispatcherServlet 继承了 FrameworkServlet , FrameworkServlet 继承了 HttpServletBean ,HttpServletBean 继承了 HttpServlet.在 HttpServletBean 的 init() 方法中调用了 FrameworkServlet 的 initServletBean() 方法,在 initServletBean() 方法中初始化 WebApplicationContext 实例.茬 initServletBean() 方法中调用了 DispatcherServlet 重写的 onRefresh() 方法.在 DispatcherServlet 的 onRefresh() 方法中又调用了 initStrategies() 方法,初始化 SpringMVC 的九大组件

其实,上面复杂的调用关系,我们可以简单的得出一个结论:就是在 Servlet 的 init() 方法中初始化了 IOC 容器和 SpringMVC 所依赖的九大组件

2. 项目环境搭建

2.1 application.properties 配置

还是先从 application.properties 文件开始,用 application.properties 来代替 application.xml,具体配置如下∶

#托管的类扫描包路径#
scanPackage=com.gupaoedu.vip.demo

2.2 pom.Xml 配置

接下来看 pom.xml 的配置,主要关注 jar 依赖∶


    
    2.4




    
        javax.servlet
        servlet-api
        ${servlet.api.version)
        provided
    
    

2.3 web.xml 配置



    Gupao Web Application
    
        gpmvc
        com.gupaoedu.vip.spring.framework.webmvc.servlet.GPDispatcherServlet
        
            contextConfigLocation
            application.properties
        
        1
    
    
        gpmvc
        /*
    

2.4 GPDispatcherServlet 实现

/**
 * 委派模式
 * 职责:负责任务调度,请求分发
 */
public class GPDispatcherServlet extends HttpServlet {
    private GPApplicationContext applicationContext;

    //IoC容器,key默认是类名首字母小写,value就是对应的实例对象
    private Map ioc = new HashMap();

    private Map handlerMapping = new HashMap();

    @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、委派,根据URL去找到一个对应的Method并通过response返回
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
        }

    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        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;
        }

        Map params = req.getParameterMap();

        Method method = this.handlerMapping.get(url);

        //获取形参列表
        Class [] parameterTypes = method.getParameterTypes();
        Object [] paramValues = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            Class paramterType = parameterTypes[i];
            if(paramterType == HttpServletRequest.class){
                paramValues[i] = req;
            }else if(paramterType == HttpServletResponse.class){
                paramValues[i] = resp;
            }else if(paramterType == String.class){
                //通过运行时的状态去拿到你
                Annotation[] [] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length ; j ++) {
                    for(Annotation a : pa[i]){
                        if(a instanceof GPRequestParam){
                            String paramName = ((GPRequestParam) a).value();
                            if(!"".equals(paramName.trim())){
                               String value = Arrays.toString(params.get(paramName))
                                       .replaceAll("\\[|\\]","")
                                       .replaceAll("\\s+",",");
                                paramValues[i] = value;
                            }
                        }
                    }
                }

            }
        }

        //暂时硬编码
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        //赋值实参列表
        method.invoke(ioc.get(beanName),paramValues);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化Spring核心IoC容器
        applicationContext = new GPApplicationContext(config.getInitParameter("contextConfigLocation"));

        //==============MVC部分==============
        //5、初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("GP Spring framework is init.");
    }

    private void doInitHandlerMapping() {
        if(ioc.isEmpty()){ return;}

        for (Map.Entry entry : ioc.entrySet()) {
            Class clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(GPController.class)){ continue; }


            //相当于提取 class上配置的url
            String baseUrl = "";
            if(clazz.isAnnotationPresent(GPRequestMapping.class)){
                GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //只获取public的方法
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(GPRequestMapping.class)){continue;}
                //提取每个方法上面配置的url
                GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);

                // //demo//query
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
                handlerMapping.put(url,method);
                System.out.println("Mapped : " + url + "," + method);
            }
        }
    }

    //自己写,自己用
    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
//        if(chars[0] > )
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

3. IOC 顶层结构设计

3.1 annotation(自定义配置) 模块

Annotation 的代码实现我们还是沿用 mini 版本的不变,复制过来便可

3.1.1 @GPService 注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService{
    String value() default"";
}

3.1.2 @GPAutowired 注解

@Target({ElementType. FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired{
    String value() default"";
}

3.1.3 @GPController 注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController{
    String value() default"";
}

3.1.4 @GPRequestMapping 注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {
    String value() default"";
}

3.1.5 @GPRequestParam 注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {
    String value() default"";
}

3.2 beans(配置封装) 模块

3.2.1 GPBeanDefinition

public class GPBeanDefinition {
    private String factoryBeanName;
    private String beanClassName;

    public String getFactoryBeanName() {
        return factoryBeanName;
    }

    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }

    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }
}

3.2.2 GPBeanWrapper

public class GPBeanWrapper {
    private Object wrapperInstance;
    private Class wrappedClass;
    
    public GPBeanWrapper(Object instance) {
        this.wrapperInstance = instance;
        this.wrappedClass = instance.getClass();
    }

    public Object getWrapperInstance() {
        return wrapperInstance;
    }

    public Class getWrappedClass() {
        return wrappedClass;
    }
}

3.3 context(IOC 容器) 模块

3.3.1 GPApplicationContext

/**
 * 职责:完成Bean的创建和DI
 */
public class GPApplicationContext {

    private GPBeanDefinitionReader reader;

    private Map beanDefinitionMap = new HashMap();

    private Map factoryBeanInstanceCache = new HashMap();
    private Map factoryBeanObjectCache = new HashMap();

    public GPApplicationContext(String... configLocations) {
        //1、加载配置文件
        reader = new GPBeanDefinitionReader(configLocations);

        try {
            //2、解析配置文件,封装成BeanDefinition
            List beanDefinitions = reader.loadBeanDefinitions();

            //3、把BeanDefintion缓存起来
            doRegistBeanDefinition(beanDefinitions);

            doAutowrited();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void doAutowrited() {
        //调用getBean()
        //这一步,所有的Bean并没有真正的实例化,还只是配置阶段
        for (Map.Entry beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    private void doRegistBeanDefinition(List beanDefinitions) throws Exception {
        for (GPBeanDefinition beanDefinition : beanDefinitions) {
            if(this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
        }
    }

    //Bean的实例化,DI是从而这个方法开始的
    public Object getBean(String beanName){
        //1、先拿到BeanDefinition配置信息
        GPBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //2、反射实例化newInstance();
        Object instance = instantiateBean(beanName,beanDefinition);
        //3、封装成一个叫做BeanWrapper
        GPBeanWrapper beanWrapper = new GPBeanWrapper(instance);
        //4、保存到IoC容器
        factoryBeanInstanceCache.put(beanName,beanWrapper);
        //5、执行依赖注入
        populateBean(beanName,beanDefinition,beanWrapper);

        return beanWrapper.getWrapperInstance();
    }

    private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
        //可能涉及到循环依赖?
        //A{ B b}
        //B{ A b}
        //用两个缓存,循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值
        Object instance = beanWrapper.getWrapperInstance();

        Class clazz = beanWrapper.getWrappedClass();

        //在Spring中@Component
        if(!(clazz.isAnnotationPresent(GPController.class) || clazz.isAnnotationPresent(GPService.class))){
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        for (Field field : clazz.getDeclaredFields()) {
            if(!field.isAnnotationPresent(GPAutowired.class)){ continue; }

            GPAutowired autowired = field.getAnnotation(GPAutowired.class);

            //如果用户没有自定义的beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                //field.getType().getName() 获取字段的类型
                autowiredBeanName = field.getType().getName();
            }

            //暴力访问
            field.setAccessible(true);

            try {
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
                    continue;
                }
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    //创建真正的实例对象
    private Object instantiateBean(String beanName, GPBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class clazz = Class.forName(className);
            //2、默认的类名首字母小写
            instance = clazz.newInstance();
            this.factoryBeanObjectCache.put(beanName, instance);
        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }

    public Object getBean(Class beanClass){
        return getBean(beanClass.getName());
    }
}

3.3.2 GPBeanDefinitionReader

public class GPBeanDefinitionReader{
    private List registyBeanClasses = new ArraylList();
    private Properties config = new Properties();

    //固定配置文件中的 key,相对于 xml 的规范
    private final String SCAN_PACKAGE ="scanPackage";

    public GPBeanDefinitionReader(String... locations) {
        //通过 URL 定位找到其所对应的文件,然后转换为文件流

        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[].replace("clspath:",""))) {
            config.load(is);
        } catch(IOException e){
            e.printStackTrace();
        }

        doScanner(config.getProperty(SCAN_PACKAGE));
    }

    private void doScanner(String scanPackage) {
        //转换为文件路径,实际上就是把.替换为/就 OK 了
        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.gtNlame();
            } else {
                if (!file.getName().endswith(".class") {
                    continue;
                }

                String className = (scanPackage + "." + file.getName().replace(".class",""));
                registyBeanClasses.add(className);
            }
        }
    }

    public Properties getConfig(){
        return this.config;
    }

    //把配置文件中扫描到的所有的配置信息转换为 GPBeanDefinition 对象,以便于之后 IOC 操作方便
    public List loadBeanDefinitions() {
        List result = new ArrayList();
        try {
            for (String className: registyBeanClasses) {
                Class beanClass = Class.forName(className);

                //如果是一个接口,是不能实例化的/用它实现类来实例化
                if (beanClass.isInterface()) {
                    continue;
                }

                //beanName 有三种情况∶
                //1、默认是类名首字母小写
                //2、自定义名字
                //3、接口注入
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getsSimpleName()), beanClass.getName()));

                Class[]interfaces= beanClass.getInterfaces();

                for(Class i : interfaces){
                    //如果是多个实现类,只能覆盖
                    //为什么?因为 Spring 没那么智能,就是这么傻
                    //这个时候,可以自定义名字
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    //把每一个配信息解析成一个 BeanDefinition
    private GPBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {
        GPBeanDefinition beanDefinition = new GPBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        return beanDefinition;
    }

    //如果类名本身是小写字母,确实会出问题
    //但是我要说明的是∶这个方法是我自己用,private 的
    //传值也是自己传,类也都遵循了驼峰命名法
    //默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况

    //为了简化程序逻辑,就不做其他判断了,大家了解就 OK
    //其实用写注释的时间都能够把逻辑写完了
    private String tolLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        //之所以加,是因为大小写字母的 ASCIT 码相差 32,
        //而且大写字母的 ASCII 码要小于小写字母的 ASCII 码
        //在 Java 中,对 char 做算学运算,实际上就是对 ASCII 码做算学运算
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

你可能感兴趣的:([02][02][01] 用30个类手写Spring V2.0版本之顶层设计IOC与DI)