手写IOC

IOC原理(手写IOC)

Spring框架的IOC是基于反射机制实现的。

手写IOC_第1张图片

反射回顾

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制,简单来说,反射机制就是程序在运行时能够获取自身的信息。

示例

实体类Car

package com.louis.reflect;

/**
 * @author XRY
 * @date 2023年06月26日8:59
 */
public class Car {

    private String bind;
    private int lifeTime;
    private String color;

    public Car() {
    }

    public Car(String bind, int lifeTime, String color) {
        this.bind = bind;
        this.lifeTime = lifeTime;
        this.color = color;
    }

    //普通方法
    private void use(){
        System.out.println("私有方法..........");
    }

    public String getBind() {
        return bind;
    }

    public void setBind(String bind) {
        this.bind = bind;
    }

    public int getLifeTime() {
        return lifeTime;
    }

    public void setLifeTime(int lifeTime) {
        this.lifeTime = lifeTime;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Car{" +
                "bind='" + bind + '\'' +
                ", lifeTime=" + lifeTime +
                ", color='" + color + '\'' +
                '}';
    }
}

1、获取class对象

@Test
public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //1、类名.class
    Class<Car> clazz01 = Car.class;
    //2、对象.getClass()
    Class<? extends Car> clazz02 = new Car().getClass();
    //3、Class.forName("全路径")
    Class<?> clazz03 = Class.forName("com.louis.reflect.Car");
    //实例化
    Car car = (Car)clazz03.getDeclaredConstructor().newInstance();
    logger.info("car" + car);
}

/*[2023-06-26 09:16:13:036] [INFO] - com.louis.reflect.TestCar.testGetClass(TestCar.java:28) - carcom.louis.reflect.Car@2445445a*/

2、获取构造方法

@Test
public void testCacheConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Class<Car> clazz = Car.class;
    //获取所有构造
    //getConstructors针对public方法,如果是private则不能够使用这种方法获取,如果构造方法中包含私有的方法,则需要使用getDeclaredConstructors
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        logger.info("constructor" + constructor.getName() + "参数个数" + constructor.getParameterCount());
    /*
    * [2023-06-26 09:57:16:855] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数0
      [2023-06-26 09:57:16:858] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数3
    * */
    }
    //指定有参数的构造去创建对象
    //1、构造是public,如果目标对象是private则会报错
    /*Constructor haveParameterPub = clazz.getConstructor(String.class, int.class, String.class);
    Car car = haveParameterPub.newInstance("野马", 1, "blue");
    /*[2023-06-26 10:07:47:947] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
      [2023-06-26 10:07:47:950] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
      汽车Car{bind='野马', lifeTime=1, color='blue'}*/
    //2、构造是private
    Constructor<Car> haveParameterPri = clazz.getDeclaredConstructor(String.class, int.class, String.class);
    haveParameterPri.setAccessible(true);//设置访问权限,如果为false不能够访问
    Car car1 = haveParameterPri.newInstance("悍马", 2, "yellow");
    System.out.println("car1 = " + car1);
    /*
    [2023-06-26 10:13:58:492] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
    [2023-06-26 10:13:58:496] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
    car1 = Car{bind='悍马', lifeTime=2, color='yellow'}
    * */
}

3、获取属性

@Test
public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获得类的字节码文件,类、对象和class.forName
    Class<?> carClass = Class.forName("com.louis.reflect.Car");
    //实例化
    Car car = (Car)carClass.getDeclaredConstructor().newInstance();
    //获取其中所有的public方法
    Field[] fields = carClass.getFields();
    //获取所有的属性,包括私有的属性
    Field[] declaredFields = carClass.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println("declaredField.getName() = " + declaredField.getName());
        //给属性赋值
        if(declaredField.getName().equals("bind")){
            //设置允许访问
            declaredField.setAccessible(true);
            //传入对象和属性值
            declaredField.set(car,"野马");
        }
        System.out.println("car" + car);
    }
    /*
    * declaredField.getName() = bind
      carCar{bind='野马', lifeTime=0, color='null'}
      declaredField.getName() = lifeTime
      carCar{bind='野马', lifeTime=0, color='null'}
      declaredField.getName() = color
      carCar{bind='野马', lifeTime=0, color='null'}

    * */
}

4、获取方法

@Test
public void getMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Car car = new Car("Benz", 10, "black");
    Class<? extends Car> clazz = car.getClass();
    //1、public方法,不会取到私有
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
//            System.out.println(method.getName());
        //执行方法toString
        if(method.getName().equals("toString")){
            String invoke = (String)method.invoke(car);
            System.out.println("toString执行了" + invoke);
            /*toString执行了Car{bind='Benz', lifeTime=10, color='black'}*/
        }
    }
    //2、private方法
    Method[] methodsAll = clazz.getDeclaredMethods();
    for (Method methodA : methodsAll) {
        //执行私有方法
        if(methodA.getName().equals("use")){
            methodA.setAccessible(true);
            methodA.invoke(car);
        }
    }
    /*私有方法..........*/
}

实现Spring的IoC

实现过程

1、创建模块spring-ioc

手写IOC_第2张图片

2、创建测试类service、dao

手写IOC_第3张图片

接口实现

@Bean
public class UserServiceImpl implements UserService {
    @Di
    private UserDao userDao;
    public void add(){
        System.out.println("service......");
        //调用dao的方法
        userDao.show();
    }
}

3、创建两个注解@Bean(创建对象)、 @Di(属性注入)

@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 {
    //用于创建对象
}

@Di

package com.louis.annotation;

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 {
    //用于注入属性
}

4、创建bean容器接口ApplicationContext,定义方法、返回对象

public interface ApplicationContext {
    //在IOC中BeanFactory返回的是一个工厂
    Object getBean(Class clazz);
}

5、实现bean容器接口,创建实现类

(1)返回对象
(2)根据包规则加载bean(扫描路径下包含@Bean注解的类,并将这些类通过反射实例化)

package com.louis.bean.impl;

import com.louis.annotation.Bean;
import com.louis.annotation.Di;
import com.louis.bean.ApplicationContext;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author XRY
 * @date 2023年06月26日16:39
 */
public class AnnotationApplicationContext implements ApplicationContext {
    //模拟IOC,创建map集合,放置bean对象
    private Map<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描规则
    //当前包及其子包,将带有@Bean注解的类通过反射实现实例化
    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());
//                System.out.println("filePath = " + filePath);
                //根据获得的路径进行包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //属性注入
        loadDi();
    }

    /**
     * 包扫描过程,进行实例化
     * @param file
     */
    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1、判断当前路径下的东西是否是文件夹,如果不是就不需要继续往下查找
        if(file.isDirectory()){
            //2、获取文件夹里面所有内容
            File[] childFiles = file.listFiles();
            //3、判断文件夹里面为空,直接返回
            if(childFiles == null || childFiles.length == 0){
                return;
            }
            //4、如果文件夹里面不为空,遍历文件夹中所有内容
            for(File child:childFiles){
                //4.1遍历得到每个File对象,继续判断,如果是文件夹,递归
                if(child.isDirectory()){
                    //递归
                    loadBean(child);
                }else{
                    //4.2遍历得到的File对象不是文件夹
                    //4.3得到包路径+类名称部分
                    String packagePath = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4判断当前文件类型是否为.class,如果是,将路径中的\替换为.并将.class文件去掉
                    if(packagePath.contains(".class")){
                        String allName = packagePath.replaceAll("\\\\", "\\.").replaceAll(".class", "");
                        //4.5判断类上面是否有注解@Bean,如果有进行实例化过程
                        //4.5.1获取类的class
                        Class<?> clazz = Class.forName(allName);
                        //4.5.2判断不是interface
                        if(!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                //4.5.3实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7把对象实例化之后,放到map集合beanFactory
                                //4.7.1判断当前类如果有接口,让接口class作为map的key,如果没有接口将自己的class作为key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                }else{
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

//    public static void main(String[] args) {
//        new AnnotationApplicationContext("com.louis");
//    }

    //属性注入
    private void loadDi(){
        //实例化的对象都在beanFactory的map集合里
        //1、遍历beanFactory的map集合,entrySet()用来获取到对象的集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2、获取map集合中每个对象(value),每个对象获取到属性
            Object value = entry.getValue();
            //获取对象Class
            Class<?> clazz = value.getClass();
            //获取每个对象中的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            //3、遍历得到的每个对象属性的数组,得到每个属性
            for (Field field : declaredFields) {
                //4、判断属性上是否有@Di注解,
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    //如果是私有属性可以设置一个值
                    field.setAccessible(true);
                    //有Di注解就把对象进行设置(注入)
                    try {
                        field.set(value, beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

6、测试

public class TestBean {
  public static void main(String[] args){
      ApplicationContext context = new AnnotationApplicationContext("com.louis");
      UserService userService = (UserService) context.getBean(UserService.class);
//      UserService userService1 = new UserServiceImpl();
      userService.add();
  }
}

/*
* service......
UserDao................
* */

你可能感兴趣的:(Spring,开发语言,java,intellij-idea)