• IOC理解


    1. Java反射

      Java反射机制存在于运行时,对于任意一个类,都能获取这个类的所有属性和方法,对于任意一个对象,都能通过反射机制去调用它的属性和方法,这种动态获取类信息及动态调用对象方法的功能称为Java反射机制,即在运行时获取Java类的相关信息。

    2. 反射

    2.1 实体类Student

    public class Student {
    
        private String name;
        private Integer age;
        private String sex;
    
        public Student() {
        }
    
        public Student(String name, Integer age, String sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    2.2 测试类StudentTest

    2.2.1 获取Class对象

    获取Class对象总共有三种方法:

    1. 通过类名.class获取;
    2. 通过实例名.getClass()获取;
    3. 通过Class.forName(“全路径”)进行获取;

    示例代码:

    public void studentTest() throws Exception {
            /**
             * 获取Student类有三种方法
             * 1. 通过类名.class获取   Student.class
             * 2. 通过对象名.getClass()获取 new Student().getClass()
             * 3. 获取Class.forName("全路径")获取
             */
            //1
            Class clazz1 = Student.class;
            //2
            Class clazz2 = new Student().getClass();
            //3
            Class clazz3 = Class.forName("pers.liyongxing.reflect.Student");
    
            //实例化
            Student car = (Student) clazz3.getDeclaredConstructor().newInstance();
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.2.2 获取构造方法

    获取构造方法分为两种,分别为通过getConstructors和getDeclaredConstructors来获取。

    1. getConstructors方法是获取所有公共的构造方法
    2. getDeclaredConstructors方法是获取所有的构造方法

    示例代码:

    public void constructorTest() throws Exception {
            Class clazz = Student.class;
    
            //1. 获取无参数的构造方法
            Constructor[] constructors = clazz.getConstructors();
            Constructor[] constructors1 = clazz.getDeclaredConstructors();
            for(Constructor constructor : constructors){
                System.out.println("构造类型:" + constructor.getName() + ",构造参数个数:" + constructor.getParameterCount());
            }
            //2. 获取有参数的构造方法并实例化 公共构造方法
            Student student = (Student)clazz.getConstructor(String.class, Integer.class, String.class).
                    newInstance("张三", 18, "男");
            System.out.println(student);
            //2. 获取有参数的构造方法并实例化 私有构造方法
            Constructor c2 = clazz.getDeclaredConstructor(String.class, Integer.class);
            c2.setAccessible(true);//允许方法私有构造方法
            Student student1 = (Student) c2.newInstance("张华", 23);
            System.out.println(student1);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.2.3 获取属性和方法

    和上面情况类似,如果是私有方法,需要通过declared*方法进行获取,并对相应的属性或者方法设置允许访问(setAccessible(true));
    获取属性示例代码:

    public void attributeTest() throws Exception{
          Class clazz = Student.class;
    
          Student student = (Student) clazz.getDeclaredConstructor().newInstance();
          //获取属性
          Field[] fields = clazz.getDeclaredFields();
          for(Field field : fields){
              if(field.getName().equals("name")){
                  field.setAccessible(true);
                  field.set(student,"李四");
              }
              System.out.println(field.getName());
              System.out.println(student);
          }
    
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    获取方法并通过执行invoke()方法执行示例代码:

    public void methodTest() throws  Exception{
          Class clazz = Student.class;
    
          Student student = (Student) clazz.getDeclaredConstructor().newInstance();
    
          Method[] methods = clazz.getDeclaredMethods();
          for(Method method : methods){
              if(method.getName().equals("toString")){
                  String invoke = (String)method.invoke(student);
                  System.out.println("toString方法执行了.." + invoke);
              }
          }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3 手写IOC(简单版)

      自定义Bean,主要分为两个:类型注入和属性注入:

    3.1 自定义Bean

    1. 类型Bean
    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 Bean {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 属性Bean
    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 Di {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.2 定义测试类

    1. UserService:
    public interface UserService {
    
        public void add();
    }
    
    • 1
    • 2
    • 3
    • 4
    1. UserServiceImpl
    @Bean
    public class UserServiceImpl implements UserService {
    
        @Di
        private UserDao userDao;
    
    
        @Override
        public void add() {
            System.out.println("service.....");
            userDao.add();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. UserDao
    public interface UserDao {
    
        public void add();
    }
    
    • 1
    • 2
    • 3
    • 4
    1. UserDaoImpl
    @Bean
    public class UserDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("add...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 测试类AnnotationTest
    public void test(){
            ApplicationContext context = new AnnotationApplicationContext("pers.liyongxing");
            UserService userService = (UserService) context.getBean(UserService.class);
            System.out.println(userService);
            userService.add();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.3 自定义ApplicationContext

    自定义ApplicationContext中的AnnotationApplicationContext,实现对路径中的注解进行扫描,如果为类型注入,则将通过反射机制创建该类的对象,并将其存储到Map集合中,最后遍历Map集合,如果当前对象中存在Di属性注入,则将该属性注入其对应的类型。

    3.3.1 自定义ApplicationContext接口:

    public interface ApplicationContext {
    
     Object getBean(Class clazz);
    }
    
    • 1
    • 2
    • 3
    • 4

    3.3.2 创建AnnotationApplicationContext类:

      创建AnnotationApplicationContext类并实现ApplicationContext接口;
    属性:
    beanFactory用于存储类型注入的bean对象;rootPath用于存储文件路径的公共部分。

    private static Map<Class,Object> beanFactory = new HashMap<>();
    private static String rootPath;
    
    • 1
    • 2
    1. 格式化处理包路径,并解析出包路径下的所有文件及文件夹,用于后面的类型注入
    public AnnotationApplicationContext(String basePackage){
            //格式化路径
            //1. 把.换成/
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //2. 获取当前包的绝对路径
            try {
                Enumeration<URL> urls =
                        Thread.currentThread().getContextClassLoader().getResources(packagePath);
                while(urls.hasMoreElements()){
                    URL url = urls.nextElement();
                    String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                    //获取包前面的路径部分
                    rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                    //包扫描
                    loadBean(new File(filePath));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    
            //属性注入
            loadDi();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    注意下面操作的文件路径为target目录下:
    2. 类型注入:

    1. 首先判断当前File对象是否为文件夹,如果是文件夹则进行遍历文件判断,如果不是则返回;
    2. 如果当前文件夹为空,直接返回;
    3. 遍历当前文件夹下的所有File对象,如果当前File仍然为文件夹,则返回第二步进行执行,如果不是,则进行下一步。
    4. 截取掉文件头的路径,获取相对于包的绝对路径。
    5. 判断当前文件是否为.class文件,如果是.class对象,进行下一步
    6. 格式化处理类路径,通过Class.forName(“路径”)获取Class对象,通过isInterface()判断当前对象是否为接口类型,如果当前对象不是接口类型,则进行下一步。
    7. 判断当前对象是否包含Bean注解,如果包含,进行实例化,并将实例化对象存储到beanFactory集合中:
      7.1如果当前类实现接口,则key为接口类型,value为实例化对象。
      7.2如果当前类没有实现接口,则key为class对象,value为实例化对象.
      示例代码:
    private static void loadBean(File file) throws Exception {
            //1. 判断当前内容是否为文件夹
            if(!file.isDirectory()){
                return ;
            }
    
            //2 获取文件夹中的内容
            File[] childrenFiles = file.listFiles();
    
            //3 判断文件夹为空 直接返回
            if(childrenFiles == null || childrenFiles.length == 0){
                return ;
            }
            //4 文件夹不为空,遍历文件夹所有内容
            for(File childFile : childrenFiles){
                //4.1 判断得到每个File对象,继续判断,如果还是文件夹,继续遍历
                if(childFile.isDirectory()){
                    //继续遍历
                    loadBean(childFile);
                }else{
                    //4.2 遍历得到File对象不是文件夹,是文件,
                    //4.3 得到包路径+类名称-字符串截取
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4 判断当前文件类型是否为.class
                    if(pathWithClass.contains(".class")){
                        //4.5 如果是.class类型,把路径中\换成.  把.class去掉
                        String pathName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class","");
                        //4.6 判断类上是否有注解@Bean,如果有,则进行实例化
                        //4.6.1 获取类的class对象
                        Class<?> clazz = Class.forName(pathName);
                        //4.6.2 判断是否为接口
                        if(!clazz.isInterface()){
                            //4.6.3判断是否有注解
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                //4.6.4 进行实例化
                                Object instance = clazz.getDeclaredConstructor().newInstance();
    
                                //4.7 实例化之后,将实例化的bean放到map中
                                //4.7.1 如果当前类有接口,就让接口的class作为key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else{
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
    
                }
    
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    1. 属性注入
      属性注入,主要判断beanFactory中的class对象是否包含Di注解的属性,如果包含则将属性通过字段类型进行注入,
      示例代码:
      private void loadDi(){
          //1. 实例化对象都在beanFactoryMap集合中,
          if(beanFactory.isEmpty()){
              return;
          }
    
          //1. 遍历map集合
          for (Map.Entry<Class,Object> entry: beanFactory.entrySet()){
              //2. 获取map集合中的每个对象(value),每个对象属性获取到
              Object obj = entry.getValue();
              //获取对象的class部分
              Class<?> clazz = obj.getClass();
              //3 遍历得到每个对象属性的数组,得到每个属性
              Field[] fields = clazz.getDeclaredFields();
    
              for(Field field : fields){
                  Di annotation = field.getAnnotation(Di.class);
                  if(annotation != null){
                      //4 判断属性上面是否有Di注解
                      field.setAccessible(true);
                      //5 如果有Di注解 将对象进行注入
                      try {
                          field.set(obj,beanFactory.get(field.getType()));
                      } catch (IllegalAccessException e) {
                          throw new RuntimeException(e);
                      }
                  }
              }
    
          }   
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    总结

    总体来说,理解的比较简单,具体的IOC实现原理还需要看源码多加理解。

  • 相关阅读:
    学长告诉我,大厂MySQL都是通过SSH连接的
    uniapp如何获取IP地址
    22/11/24
    C++中的命令模式
    基于javaweb+mysql的学生成绩管理系统(管理员、教师、学生)
    【MySQL】------数据库连接
    链表经典面试题(四)
    HTTP协议总结
    2022牛客暑期多校训练营6(总结+补题)
    ssm基于web的酒店预订及个性化服务系统 毕业设计源码241822
  • 原文地址:https://blog.csdn.net/BLACKLOVE7/article/details/130904505