手写模拟spring

目录

spring扫描底层实现

BeanDefinition的生成

getBean方法的底层实现

Bean创建流程

依赖注入

Aware回调

spring初始化

BeanPostProcessor

springAOP机制


spring扫描底层实现

spring容器启动时需要根据配置类提供的信息去扫描,因为扫描路径定义在了配置类,所以需要判断有没有配置类上有没有ComponentScan注解,如果有就先获取到这个注解对象,接着就需要根据注解对象去获取注解的属性值,也就是获得扫描路径。获取的扫描路径只是包名,另外,扫描的时候需要扫描的是编译好的.class文件。

public PnjgApplicationContext(Class configClass) {
        // 扫描类 得到 BeanDefinition
        scan(configClass);

如何获得准确的扫描文件路径呢?通过类加载器的getResource方法,获取相对路径对应的资源,就能获得存放.class文件的路径并获得文件夹,判断文件里的.class文件并筛选出来。

ClassLoader classLoader = PnjgApplicationContext.class.getClassLoader();
        packagePath = packagePath.replace(".", "/");
        URL resource = classLoader.getResource(packagePath);
        File file = new File(resource.getFile());

        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
//                System.out.println(fileName);
                if (fileName.endsWith(".class")) {
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    className = className.replace("\\", ".");
//                    System.out.println(className);

                    try {
                        Class<?> clazz = classLoader.loadClass(className);
                        beanClasses.add(clazz);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

获取到所有的.class文件之后需要判断是不是bean,也就是判断有没有@Component注解,这里用反射,拿到对应类的Class对象,然后去判断这个类上面有没有提示这是个bean的注解。

BeanDefinition的生成

如果知道某个类并且这个类上有@Component注解,这时候不是直接把bean创建出来而是生成beanDefinition,这个beanDefinition类里面会有一些属性,比如Bean的类型、单例多例等。

package com.spring;

public class BeanDefinition {

    private Class beanClass;
    private ScopeEnum scope;

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }


    public ScopeEnum getScope() {
        return scope;
    }

    public void setScope(ScopeEnum scope) {
        this.scope = scope;
    }
}

生成完beanDefinition类的对象中之后,把扫描得到的信息(此时可以知道bean的类型,而单例多例也可以在扫描的时候判断是否有@Scope注解,如果有取出里面的值)写入给beanDefinition类的对象。

if (clazz.isAnnotationPresent(Component.class)) {
    BeanDefinition beanDefinition = new BeanDefinition();
    beanDefinition.setBeanClass(clazz);
        // 解析scope
          if (clazz.isAnnotationPresent(Scope.class)) {
            Scope scope = (Scope) clazz.getAnnotation(Scope.class);
             String scopeValue = scope.value();
            if (ScopeEnum.singleton.name().equals(scopeValue)) {
                 beanDefinition.setScope(ScopeEnum.singleton);
              } else {
                 beanDefinition.setScope(ScopeEnum.prototype);
                }
           } else {
             beanDefinition.setScope(ScopeEnum.singleton);
          }

此时已经生成好了一个beanDefinition类的对象,需要把它存起来,放在一个MAP里面,key值bean的名字,value是beanDefinition类的对象。bean的名字在判断是否有@Component注解后获取该注解的对象并取出里面的value值,即bean的名字

private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
Component component = (Component) clazz.getAnnotation(Component.class);
String beanName = component.value();
beanDefinitionMap.put(beanName, beanDefinition);

getBean方法的底层实现

getBean()方法传入的参数是beanName,可以根据这个参数在beanDefinitionMap中获取对应的beanDefinition对象。

BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

如果获取到的beanDefinition对象不为空,根据beanDefinition对象内的属性值去创建bean对象,比如scope的值是单例还是多例,如果是单例,那么可以在spring容器启动的时候就把单例bean创建出来,并放入单例池。

private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private void instanceSingletonBean() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if (beanDefinition.getScope().equals(ScopeEnum.singleton)) {
                Object bean = doCreateBean(beanName, beanDefinition);

                singletonObjects.put(beanName, bean);
            }
        }
    }

所以后续getbean是单例bean的时候先尝试从单例池中获取,如果没有就创建一个单例bean并放入单例池

if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        } else {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            return doCreateBean(beanName, beanDefinition);
        }

Bean创建流程

在创建bean的方法里面拿到了beanName, beanDefinition这两个参数,beanDefinition有当前要创建的bean的类型,并获得这个类,利用反射可以直接获得构造方法获取该类的对象

Class beanClass = beanDefinition.getBeanClass();
Object instance = declaredConstructor.newInstance();

依赖注入

在创建bean的那个步骤去做依赖注入,在上一步拿到对应Bean的对象之后给加了@Autowired的属性赋值。

第一步先拿到所有属性,然后判断当前属性是否有@Autowired注解,如果有就赋值,通过getBean()注入

Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String fieldName = field.getName();
                    Object bean = getBean(fieldName);

                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }

Aware回调

比如如果需要查看bean的名字,这时候就需要使用到Aware回调,spring提供了beanNameAware接口来实现beanName回调

public interface BeanNameAware {

    public void setBeanName(String name);
}

需要用到aware回到的时候这个bean类就需要去实现这个beanNameAware接口,并实现其中的setBeanName方法,这个方法是由spring调用。

在spring创建bean方法的依赖注入完之后,就回去判断这个bean是否实现了BeanNameAware接口,如果是实现了这个接口,就去调用setBeanName方法,并把bean的名字传入

// Aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

spring初始化

spring会提供一个InitializingBean接口,里面提供一个afterPropertiesSet方法。bean类实现这个接口并实现afterPropertiesSet方法,在该方法内可以写需要实现功能的代码

public class OrderService implements BeanNameAware, InitializingBean {

    @Autowired
    private UserService userService;

    private String beanName;

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println(name);
        this.beanName = name;
    }

初始化方法类似于aware回调方法,会被spring在创建bean判断是否实现该接口并判断是否调用。

// 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

与aware实现机制相同,不同点在于aware的调用是spring会告诉你一些东西,而初始化是实现你想要的一部分功能

BeanPostProcessor

利用BeanPostProcessor机制可以处理bean,spring会提供BeanPostProcessor接口,接口里会有两个方法postProcessBeforeInitialization(),postProcessAfterInitialization()。程序员可以实现这个接口,可以在这两个方法里做事情,比如获取具体的bean去做处理。

在spring容器中会创建一个List来记录实现了BeanPostProcessor接口的类

 private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

在spring容器中扫面阶段,判断当前的类是否实现了BeanPostProcessor接口类型,如果是就生成一个该类的对象,然后把该对象加入上面创建好的list里面。

在创建bean的时候,取出beanPostProcessorList里面的内容并且遍历,就可以在初始化前、初始化后调用对应的两个方法

            for (BeanPostProcessor beanPostProcessor: beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }

springAOP机制

如果是AOP机制,那么从spring容器中拿到的是动态代理之后代理对象。在postProcessAfterInitialization方法中判断beanName是否为需要动态代理的bean,如果是,创建一个新的代理对象,先执行切面逻辑,再执行bean本身的方法,最后返回新的代理对象,这样在spring容器调用postProcessAfterInitialization方法时就能拿到代理对象并返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值