通过一个简单的模拟来看Spring中依赖注入的基本实现原理

类的说明:

  1. Address、Customer、Order类是普通的bean类
  2. Config类可以理解为Spring中添加了@Configuration的类
  3. Container类可以理解为Spring中的容器
  4. 重点在于Container类,其中的属性和方法在代码块中添加了注释

在Spring中,对bean的注入基本方式有两种,一是我们自己项目中的类,添加了如@Component注解的方式;二是添加了@Configuration搭配@Bean的方式

这个小示例就演示了这两种方式注入的基本原理。其中Container类中的getServiceInstanceByClass()演示了方法二的基本实现;createInstance()演示了方法一的基本实现,且有一个Autowired注入的演示。

很多反射的使用,注解本质上就是一个标记,除此之外没有任何意义,有意义的在于处理该标记的方法。

bilibili视频地址

java 复制代码
public class Address {

    private String street;

    private String postCode;

    public void printStreet() {
        System.out.println("Address street: " + street);
    }

    public void printPostCode() {
        System.out.println("Address postCode: " + postCode);
    }
}
java 复制代码
public class Customer {

    private String name;

    private String email;

    public void printName() {
        System.out.println("Address street: " + name);
    }

    public void printEmail() {
        System.out.println("Address postCode: " + email);
    }
}
java 复制代码
public class Order {
    private Customer customer;

    private Address address;

    public Order() {
    }

    @Autowired
    public Order(Customer customer, Address address) {
        this.customer = customer;
        this.address = address;
    }
}
java 复制代码
/**
 * 配置类,类比Spring中添加了Configuration注解的类
 */
public class Config {
    @Bean
    public Customer customer(){
        return new Customer();
    }

    @Bean
    public Address address(){
        return new Address();
    }

    public Message message(){
        return new Message();
    }
}
java 复制代码
/**
 * 模拟Spring的容器
 */
public class Container {

    //该map存储的是配置类中的所有方法,作用是通过method创建实例对象,见getServiceInstanceByClass方法
    private Map<Class<?>, Method> methods;
    private Object config;

    // 该map是存储已经实例化成功的bean
    private Map<Class<?>, Object> service;

    /**
    该方法的作用是初始化配置类,以及将配置类中的所有method存储到容器的methods map中
     */
    public void init() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        this.methods = new HashMap<>();
        this.service = new HashMap<>();
        Class<?> clazz = Class.forName("Reflection.Config");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Bean.class)) {
                this.methods.put(method.getReturnType(), method);
            }
        }
        this.config = clazz.getConstructor().newInstance();
    }

    /**
     * 该方法的作用是通过class获得容器中已经实例化成功的对象
     * 双重锁检测
     */
    public Object getServiceInstanceByClass(Class<?> clazz) throws InvocationTargetException, IllegalAccessException {
        if (this.service.containsKey(clazz)) {
            return this.service.get(clazz);
        }
        synchronized (Container.class){
            if (this.service.containsKey(clazz)) {
                return this.service.get(clazz);
            }
            if (this.methods.containsKey(clazz)) {
                Method method = this.methods.get(clazz);
                Object obj = method.invoke(this.config);
                this.service.put(clazz,obj);
                return obj;
            }
        }
        return null;
    }

    /**
     *该方法的作用是通过class参数创建一个该类型的对象
     */
    public Object createInstance(Class<?> clazz) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for(Constructor<?> constructor : constructors){
            if(constructor.getDeclaredAnnotation(Autowired.class) != null){
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                Object[] arguments = new Object[parameterTypes.length];
                for(int i = 0; i < parameterTypes.length; i++){
                    arguments[i] = getServiceInstanceByClass(parameterTypes[i]);
                }
                return constructor.newInstance(arguments);
            }
        }
        // 如果没有@Autowired注解,就创建一个无参构造函数的实例
        return clazz.getConstructor().newInstance();
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) throws Exception {
        Container container = new Container();
        container.init();
        Class<?> clazz = Class.forName("Reflection.Order");
        Object obj = container.createInstance(clazz);
        Field filed = clazz.getDeclaredField("customer");
        filed.setAccessible(true);
        Object fieldValue = filed.get(obj);
        Method[] declaredMethods = fieldValue.getClass().getDeclaredMethods();
        for (Method method : declaredMethods){
            System.out.println(method.getName());
            method.invoke(fieldValue);
        }
    }
}
相关推荐
lybugproducer2 分钟前
创建型设计模式之:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式
java·设计模式·建造者模式·简单工厂模式·工厂方法模式·抽象工厂模式·面向对象
南客先生6 分钟前
马架构的Netty、MQTT、CoAP面试之旅
java·mqtt·面试·netty·coap
Minyy1110 分钟前
SpringBoot程序的创建以及特点,配置文件,LogBack记录日志,配置过滤器、拦截器、全局异常
xml·java·spring boot·后端·spring·mybatis·logback
百锦再10 分钟前
Java与Kotlin在Android开发中的全面对比分析
android·java·google·kotlin·app·效率·趋势
武昌库里写JAVA1 小时前
39.剖析无处不在的数据结构
java·vue.js·spring boot·课程设计·宠物管理
画个大饼2 小时前
Go语言实战:快速搭建完整的用户认证系统
开发语言·后端·golang
Nelson_hehe4 小时前
Java基础第四章、面向对象
java·语法基础·面向对象程序设计
Thomas_YXQ4 小时前
Unity3D Lua集成技术指南
java·开发语言·驱动开发·junit·全文检索·lua·unity3d
ShiinaMashirol5 小时前
代码随想录打卡|Day27(合并区间、单调递增的数字、监控二叉树)
java·算法
东阳马生架构6 小时前
Nacos简介—3.Nacos的配置简介
java