• 100行代码撸完SpringIOC容器


    用过Spring框架的人一定都知道Spring的依赖注入控制反转;通俗的讲就是负责实例化对象 和 管理对象间的依赖 实现解耦。

    我们来对比两段代码:

    UserController{
        UserService userService = new UserService();
        userService.insert(user);
    }
    
    UserController{
        @Autowwired
        UserService userService;
        userService.insert(user);
    }

    乍一看好像没什么区别,好像都是一样的。在controller里面创建了一个service对象然后调用它里面的方法。但是换个角度想想, 如果还有2个,3个,甚至n个类需要用到这个service呢,那它岂不是要被创建n次,这样就会极大的浪费资源,分分钟就内存溢出了。

    企业开发案例:

    我们只需要在xml配置文件里面指定配置参数,然后在类上加上spring注解它就能帮我们管理对象了,那它又是怎么实现的呢? 答案是: xml解析 + 反射 + 工厂模式

    手写IOC容器,演示它的加载过程:

    1. 模仿spring,我们也定义一个注解

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyService {
    
    }
    
    
    @Target({ TYPE, FIELD, METHOD })
    @Retention(RUNTIME)
    public @interface MyAutowired {
    
    }

    2. 像使用spring注解一样,使用我们刚才的注解

    @MyService
    public class OrderService {
    
        public void add() {
            System.out.println("Order....add()");
        }
    }
    
    
    @MyService
    public class UserService {
    
        @MyAutowired
        private OrderService orderService;
        
        public void add() {
            orderService.add();
            System.out.println("User....add()");
        }
    }

     3. 演示容器加载核心代码

    /**
     * SpringIOC容器实现过程
     */
    public class MyClassPathXmlApplicationContext {
        // 1. 指定扫描包的范围
        private String packageName ;
        
        // Spring的bean容器 (容器启动时初始化所有bean,默认单例)  key:beanName(默认类名小写)  value:bean对象
        private ConcurrentHashMap<String, Object> beans;
        
        public MyClassPathXmlApplicationContext(String packageName) throws Exception {
            // 类加载时初始化这些参数
            this.packageName = packageName;
            this.beans = new ConcurrentHashMap<String, Object>();
            initBeans();// 初始化所有类
            initEntryField();// 初始化所有类的属性
        }
        
        /**
         * 初始化bean(IOC控制反转)
         */
        public void initBeans() throws Exception {
            // 2. 通过java反射扫描指定包下面所有类的class地址
            List<Class<?>> classes = ClassUtil.getClasses(packageName);
            ConcurrentHashMap<String, Object> classExistAnnotation = this.findClassExistAnnotation(classes);
            if(classExistAnnotation == null || classExistAnnotation.isEmpty()) throw new Exception("cannot find bean");
        }
        
        /**
         * 判断类上面是否存在bean的注解
         */
        private ConcurrentHashMap<String, Object> findClassExistAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
            for(Class<?> classInfo : classes) {
                // 3. 获取指定路径中有ioc注解的类
                MyService annotation = classInfo.getAnnotation(MyService.class);
                if(annotation!=null) {
                    String className = classInfo.getSimpleName();// 获取类名称
                    // 4. 将类名首字母小写,获取bean名称。
                    String beanaName = StringUtil.toLowerCaseFirstOne(className);
                    // 5. 反射初始化对象
                    Object object = classInfo.newInstance();
                    // 6. 将bean对象存入spring容器中
                    beans.put(beanaName, object);
                }
            }
            return beans;// 对象初始化完成
        }    
        /**
         * 初始化属性
         */
        private void initEntryField() throws Exception {
            // 7. 遍历spring容器中所有的bean对象
            for (Entry<String, Object> entry : beans.entrySet()) {
                Object bean = entry.getValue();
                this.attriAssign(bean);
            }
    
        }
        /**
         * 依赖注入实现原理(DI依赖注入)
         */
        public void attriAssign(Object object) throws Exception {
            Class<? extends Object> classInfo = object.getClass();
            // 8. 使用反射机制,获取当前类的所有属性值
            Field[] fields = classInfo.getDeclaredFields();
            for(Field field : fields) {
                // 9. 获取有@MyAutowired注解的属性
                MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
                if(myAutowired != null) {
                    // 10. 获取属性名称
                    String beanName = field.getName();
                    // 11. 默认使用属性名称,查找bean容器对象
                    Object bean = this.getBean(beanName);
                    if(bean != null) {
                        field.setAccessible(true);// 允许访问私有属性
                        // 12. 将得到的bean对象赋值给当前对象的属性上。(bean名称=属性名称)
                        field.set(object, bean);
                    }
                }
            }
        }
        /**
         * 通过bean名称去spring容器里面获取bean对象
         */
        public Object getBean(String beanName) throws Exception {
            if(StringUtils.isEmpty(beanName)) throw new Exception("beans.factory.BeanCreationException");
            Object object = beans.get(beanName);
            return object;
        }
        /**
         * 测试
         */
        public static void main(String[] args) throws Exception {
            MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext("com.wulei.service");
            UserService userService = (UserService)applicationContext.getBean("userService");
            userService.add();
        }
    }

    依赖注入:比如userController里面的userService属性加上bean注解,在类被加载时通过反射获取service的对象,并且赋值给该属性,这就叫做依赖注入。
    控制反转:controller引用service的实例,不需要通过new来创建,将创建对象的责任转移给spring容器,这就叫反转; ioc容器实现对象的创建,以及外部资源的获取(其他类的属性和方法),这就叫控制

  • 相关阅读:
    GrapeCity Documents (服务端文档API组件) V3.0 正式发布
    js 手机号码正则表达式
    springMvc 注解@JsonFormat 日期格式化
    solr java代码
    solr全文检索
    在阿里云服务器CentOS7安装mysql提示“No package mysql-server available上安装mysql遇到的问题
    MockBean 单元测试
    redis缓存
    c3p0连接池
    springboot Transactional事务的使用
  • 原文地址:https://www.cnblogs.com/wlwl/p/10122236.html
Copyright © 2020-2023  润新知