• Spring——原理解析利用反射和注解模拟IoC的自动装配


    解析Spring的IoC容器基于注解实现的自动装配(自动注入依赖)的原理


    1.本文案例
    使用注解和反射机制来模拟Spring中IoC的自动装配功能
    定义两个注解:@Component,用来标注组件;@Autowired,用来标记需要被织入的属性。
    定义一个@Component注解处理器,用来扫描所有组件。
    定义一个bean工厂,用来实例化组件。
    测试:有两个组件,一个组件被设置到另一个组件的属性中。

    2.定义注解
    2.1.定义@Component注解
    这个注解表示被标注的就是一个组件,将会被容器自动扫描并创建实例

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
     
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Component {
        public String id();
    }

    注解的定义有点类似于接口的定义

    注解定义

    public @interface Component {
    }

    接口定义

    public interface Component {
    }

    区别只是在于interface这个标识符前面有没有@符号。

    并且注解的定义,还需要使用到几个原生注解:

    @Target(ElementType.TYPE)

    这个注解表明自定义的注解Component是用来标记谁的,其中ElementType.TYPE表示这个注解使用来标记类型的,也就是可以标记类、接口等。此外还有FIELD、METHOD等,分别表示用来标记字段、方法等。

    @Retention(RetentionPolicy.RUNTIME)

    表示这个自定义的注解需要保留到什么时候,如只保留到源码中,编译之后就没有了;或者保留到运行时,就是在运行的时候也一直有。这里设置为运行时。

    然后这个注解中有这样一行:

    public String id();

    有点类似于接口中方法的声明,不过在注解中,这个表示注解的一个属性,后面用到的时候可以看看是怎么使用的,就明白了。

    2.2.定义 @Autowired注解
    这个注解是一个针对成员变量的注解,使用这个注解则表示,这个字段需要由程序来为其赋值的。

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
     
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Autowire {
        public String id();
    
    }

    3.定义 Beanfactory(也就是注解处理器)
    自定义注解完之后,实际上并没有什么用处。要想让注解发挥用处,重点在于注解处理器。
    首先来明确下这个处理器干了那些事情,首先根据给定的组件的包名,扫描这个包,找出其中所有的被@Component注解标注的类,将类型的信息保存下来。
    然后提供一个getBean()方法,允许根据bean的id来获取bean。
    接下来看看这个BeanFactory是如何编写的。

    3.1.BeanFactory.java

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
     
    public class BeanFactory {
     
        private HashMap<String, Object> beanPool;
        private HashMap<String, String> components;
        
        public BeanFactory(String packageName) {
            beanPool = new HashMap<>();
            
            scanComponents(packageName);
        }
        
        private void scanComponents(String packageName) {
            components = ComponentScanner
                    .getComponentClassName(packageName);
        }
        
        public Object getBean(String id) throws ClassNotFoundException, 
                InstantiationException, IllegalAccessException, 
                NoSuchMethodException, SecurityException, 
                IllegalArgumentException, InvocationTargetException {
            
            if (beanPool.containsKey(id)) {
                return beanPool.get(id);
            }
            
            if (components.containsKey(id)) {
            
                Object bean = Class.forName(components.get(id))
                        .newInstance();
                
                bean = assemblyMember(bean);
                
                beanPool.put(id, bean);
                
                return getBean(id);
            }
            
            throw new ClassNotFoundException();
        }
        
        private Object assemblyMember(Object obj) throws 
                ClassNotFoundException, InstantiationException, 
                IllegalAccessException, NoSuchMethodException, 
                SecurityException, IllegalArgumentException, 
                InvocationTargetException {
            
            Class cl = obj.getClass();
            
            for (Field f : cl.getDeclaredFields()) {
                Autowire at = f.getAnnotation(Autowire.class);
                
                if (at != null) {
     
                    Method setMethod = cl.getMethod("set" 
                            + captureName(f.getName()), f.getType());
                            
                    setMethod.invoke(obj, getBean(at.id()));
                }
            }
            return obj;
        }
        
        public static String captureName(String name) {
            char[] cs=name.toCharArray();
            cs[0]-=32;
            return String.valueOf(cs);
        }
    
    }

    3.2.ComponentScann.java
    这个BeanFactory在构造函数中使用到了一个类,用来扫描出一个包中所有的类的信息。

    import java.io.File;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
     
    public class ComponentScanner {
     
        public static HashMap<String, String> getComponentClassName(
                String packageName) {
            List<String> classes = getClassName(packageName);
            HashMap<String, String> components = new HashMap<String, String>();
            
            try {
                
                for (String cl : classes) {
                    cl = cl.replace("workspace_java.LearningJava.bin.", "");
                    
                    Component comp = Class.forName(cl).getAnnotation(Component.class);
                    
                    if (comp != null) {
                        components.put(comp.id(), cl);
                    }
                }
            
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            return components;
        }
        
        public static List<String> getClassName(String packageName) {
            String filePath = ClassLoader.getSystemResource("").getPath() 
                    + packageName.replace(".", "\\");  
            List<String> fileNames = getClassName(filePath, null);
            return fileNames;
        }
        
        private static List<String> getClassName(String filePath
                , List<String> className) {  
            List<String> myClassName = new ArrayList<String>();  
            File file = new File(filePath);  
            File[] childFiles = file.listFiles();  
            for (File childFile : childFiles) {  
                if (childFile.isDirectory()) {  
                    myClassName.addAll(getClassName(childFile.getPath()
                            , myClassName));  
                } else {  
                    String childFilePath = childFile.getPath();  
                    childFilePath = childFilePath.substring(childFilePath
                            .indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));  
                    childFilePath = childFilePath.replace("\\", ".");  
                    myClassName.add(childFilePath);  
                }  
            }  
      
            return myClassName;  
        }
        
        public static void main(String[] args) {
            getComponentClassName("com.oolong.javase.annotation");
        }
    
    }

    4.测试

    定义一个模拟的数据库访问接口

    @Component(id = "dataAccessInterface")
    public class DataAccessInterface {
     
        public String queryFromTableA() {
            return "query result";
        }
    }

    这个类使用了Component这个注解,并且注意,这里使用了这个注解的id属性。

    定义一个模拟的业务接口

    @Component(id="businessObject")
    public class BusinessObject {
     
        @Autowire(id="dataAccessInterface")
        private DataAccessInterface dai;
        
        public void print() {
            System.out.println(dai.queryFromTableA());
        }
        
        public void setDai(DataAccessInterface dai) {
            this.dai = dai;
        }
    }

    这个接口除了使用@Component这个注解标注之外,还有个成员变量,使用了Autowire这个注解标注。使用这个注解标注,表示这个成员变量的初始化将会交给BeanFactory来进行。

    测试

    public class BeanFactoryTester {
     
        public static void main(String[] args) {
            BeanFactory beanFactory = new BeanFactory("com.oolong.javase.annotation");
            
            BusinessObject obj = (BusinessObject) beanFactory.getBean("businessObject");
            obj.print();
                
        }
    
    }

    这里使用BeanFactory创建了一个BusinessObject的对象之后,调用这个对象的print方法,最终打印出来一个结果。

    而回到这个类的定义中,可以看到:

    public void print() {
        System.out.println(dai.queryFromTableA());
    }

    这个方法调用的是成员变量dai的queryFromTableA方法。而在这个类中,只有这个成员变量的声明,而没有赋值。

    这个赋值又是在哪里进行的呢?

    这个就是有我们编写的这个BeanFactory执行的。通过注解和反射机制,自动为类注入依赖。

  • 相关阅读:
    CDH5.16.1集群企业真正离线部署
    创建node节点上kubeconfig文件
    二进制文件安装安装flannel
    二进制文件安装安装etcd
    二进制文件安装k8s所需要的证书服务
    快速安装k8s,版本为1.13.8
    利用dockerfile 安装一个nginx-1.14.1
    在非spring管理的类中,使用spring管理的类
    远程连接MySQL错误:Can't connect to MySQL server (10060)
    Java继承与多态
  • 原文地址:https://www.cnblogs.com/weilu2/p/spring_ioc_analysis_principle_bsici_on_reflection_annotation.html
Copyright © 2020-2023  润新知