手写Mini版的Spring

这段时间有想法去学习手写Spring源码,在此记录一下自己手写的Spring的迭代版本趴。

1. Spring的启动扫描到容器

@Component
@ComponentScan
@Scope // 判断Bean对象是不是单例,值为singleton是单例,

上述注解需要手写出来来实现简单实现Spring中的功能

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {

    /**
     * 容器中类的名称
     * @return
     */
    String value() default "";
}
/**
 * 自定义的注解,默认扫描包的路径
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {

    String value() ; // 扫描的路径
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {


    String value();
}

上述三个注解里面没有什么价值性的东西,因为注解在我理解就是一个标识嘛。

接下来就要实现ApplicationContext,这玩意和我们使用Spring的ApplicationContext一样,主要的核心都在里面
直接上代码。

/**
 * 容器类
 */
public class ZJHApplicationContext {

    /**
     * 配置类
     */
    private Class configClass;

    /**
     * 单例池,里面存储的是单例对象
     */
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 存放BeanDefinition对象
     */
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();



    /**
     * 构造器
     *
     * @param configClass
     * @throws ClassNotFoundException
     */
    public ZJHApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 初始化Bean容器,其实Scan方法并没有将单例Bean对象加入到单例池中,Scan方法的主要作用就是将BeanDefinition对象加入到BeanDefinitionMap中,为了下面的将单例Bean加入到单例池中做准备
        scan(configClass);

        // 初始化BeanDefinition容器后,通Beandefinition容器里的元素来一一将扫描路径下的对象的Beandefinition对象取出
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            // 获取Beandefinition对象的key,也就是Bean的name
            String beanName = beanDefinitionEntry.getKey();
            // 获取BeandefinitionEntry的value,也就是BeanDefinition对象
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
            // 接下来就是获取BeanDefinition对象的scope属性来判断对象是单例还是多例
            if ("singleton".equals(beanDefinition.getScope())){
                // 说明是单例Bean,将单例Bean加入到单例池中
                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName,bean);
            }
            // 如果对象是多例,则不需要加入到单例池中
        }

    }

    /**
     * 创建Bean对象的方法,
     * @param beanDefinition
     * @return
     */
    public Object createBean(BeanDefinition beanDefinition){
        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 初始化Bean容器的方法
     * @param configClass
     */
    private void scan(Class configClass){
        // 拿到配置类后会去解析这个配置类

        // 1. 解析@ComponentScan注解的信息 -> 获取扫描路径 -> 扫描

        // 查看配置类里有没有ComponentScan注解
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 获取扫描路径
        String path = componentScanAnnotation.value();
        System.out.println(path); // test.service

        // 扫描路径的类是否加入了Component注解

        // 1. 根据扫描路径来得到路径下的所有类,通过类加载器来获取,这里需要应用类加载器,因为Bootstrap,ext加载器不加载我们自定义的类,通过自己的类来获取对应的类加载器
        ClassLoader classLoader = ZJHApplicationContext.class.getClassLoader();

        path = path.replace(".", "//");
        // 这个resource是一个目录
        URL resource = classLoader.getResource(path);
        // 通过File类来获取目录下的文件
        File file = new File(resource.getFile());
        // 如果文件是一个文件夹,则获取文件夹下的所有的文件
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println(f);

                // 将D:\Tools\Spring_ZJH\target\classes\test\service\UserService.class转为 test.service.UserService
                String fileName = f.getAbsolutePath();

                // 判断文件是不是class文件,如果是的话在开始截取
                if (fileName.endsWith(".class")) {
                    // 从test开始截取,到.class结束
                    String className = fileName.substring(fileName.indexOf("test"), fileName.indexOf(".class"));
                    // 将\转换为.
                    className = className.replace("\\", ".");
                    // 得到了类的路径
                    System.out.println(className);

                    try {
                        // 通过类加载器来加载类
                        Class<?> clazz = classLoader.loadClass(className);
                        // 判断类上是否有Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 到这一步就说明扫描类中已经有了Component注解,即它是一个Bean对象

                            // 到了这一步得判断这个Bean是单例Bean还是Prototype的Bean(即多例Bean)
                            // 如果是单例Bean则将Bean对象加入到singletonObjects单例池中
                            // 在加入容器时,我们需要判断Bean是单例还是多例,在getBean方法时,我们也需要判断,但其实没有必要判断两次,Spring中有BeanDefinition的概念,即Bean的定义
                            // 每解析一个类都会生成一个BeanDefinition对象

                            // 获取Bean的名称
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            // 生成BeanDefinitation对象,注意这个不是Bean对象,而是Bean的定义
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);

                            if (clazz.isAnnotationPresent(Scope.class)){
                                // 设置BeanDefinitation对象的scope属性
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            }else {
                                // Bean中没有scope注解,则将BeanDefinition的scope属性设置为singleton,即单例
                                beanDefinition.setScope("singleton");

                            }

                            // 将BeanDefinition对象加入到BeanDefinitionMap中
                            beanDefinitionMap.put(beanName,beanDefinition);


                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    /**
     * getBean方法
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        if (beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())){
                // 说明这是单例Bean,从单例池子中获取Bean对象返回
                Object o = singletonObjects.get(beanName);
                return o;
            }else {
                // 说明这是多例Bean 手动创建Bean对象并返回
                return createBean(beanDefinition);
            }
        }else {
            throw new RuntimeException("没有"+beanName+"对象");
        }

    }
}

在使用Spring时,我们创建ApplicationContext对象,传入配置类即可使用容器。

上述代码精炼如下:

  1. createBean方法就是用于创建Bean对象
  2. scan方法的作用就是将读取配置类的ComponentScan注解的扫描路径,然后生成扫描路径下的包含@Component注解的类的BeanDefinition对象(即类定义对象),BeanDefinition对象的作用后面叙述。
  3. ZJHApplicationContext这是构造器,在得到所有的BeanDefinition对象后,来判断BeanDefinition所对应的对象是不是单例的,即类上是否加入了@Scope(“singleton”)注解,如果是单例,则加入到单例池
  4. getBean方法就是从容器中获取Bean对象

接下来就要解释一下BeanDefinition对象

package com.spring;


public class BeanDefinition {
    /**
     * Bean的类型
     */
    private Class clazz;
    /**
     * Bean是单例还是多例
     */
    private String scope;

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getScope() {
        return scope;
    }

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

    public BeanDefinition(Class clazz, String scope) {
        this.clazz = clazz;
        this.scope = scope;
    }

    public BeanDefinition() {
    }
}

这里的BeanDefinition对象就只有两个属性:

  1. class:来记录Bean对象的类型,为了createBean方法而做准备
  2. scope:记录Bean对象中Scope注解的值,来判断类是不是单例的

为什么要有这个BeanDefinition对象?

在getBean方法和构造器方法中都需要判断Bean是不是单例,难道每次都要逐一遍历扫描路径的所有类一一判断?这里就用到了BeanDefinition对象来记录Bean对象的class和scope,方便判断Bean对象是不是单例的,也方便了创建Bean对象。

上述代码的流程图:
请添加图片描述
代码的详细注释在代码中,结合流程图更清晰。

手动测试,看看是否能获得Bean对象

配置类

@ComponentScan("test.service")
public class AppConfig {

}

待加入容器的类

@Component("userService")
@Scope("singleton")
public class UserService {
}

测试类

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
        Object userService = zjhApplicationContext.getBean("userService");
        Object userService2 = zjhApplicationContext.getBean("userService");
        System.out.println(userService.toString());
        System.out.println(userService2.toString());
    }
}

这里的Bean是单例的,结果如图

在这里插入图片描述

2. 依赖注入

Spring的依赖注入:先ByType后ByName。上述代码中没有实现依赖注入的功能,因为createBean方法里就直接通过BeanDefinition对象的class属性通过反射直接调用无参构造器进行创建了。那么如何实现简单的依赖注入呢?
在createBean方法通过反射创建一个Bean对象时,里面的属性没有被赋值,我们通过反射查看其所有属性,判断属性上是否含有@Autowired注解,如果有的话,我们就从容器里寻找注入。所以我们只需要修改createBean方法的逻辑来实现依赖注入。


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface Autowired {


}

那么如何实现先ByType后ByName呢?先上代码,后解释。

/**
     * 创建Bean对象的方法,
     * @param beanDefinition
     * @return
     */
    public Object createBean(BeanDefinition beanDefinition){
        // 方法内部的存储BeanDefinition的Map
        ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();

        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            // 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
            // 通过反射来获取class的所有的属性
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 判断属性上是否包含了Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    // 进入到这一步就说明已经包含了AutoWired注解
                    // 接下来获取包含@Autowired注解的属性的属性
                    Class<?> type = declaredField.getType();
                    System.out.println("aclass"+type.toString());
                    // 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        // 进行比较
                        if (entry.getValue().getClazz().equals(type)){
                            System.out.println("有的哦");
                            internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
                        }
                    }


                    // 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
                    if (internalBeanDefinitionMap.size()>1){
                        // 先获取有@AutoWired注解的属性的名称
                        String beanName = declaredField.getName();
                        // 不断从internalBeanDefinitionMap中进行比较
                        for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
                            if (beanName.equals(entry.getKey())){
                                // 到这一步就说明Name有相同的了,通过name来调用getBean方法
                                Object bean = getBean(beanName);
                                // 属性注入
                                declaredField.setAccessible(true);
                                declaredField.set(instance,bean);
                                // 将内部的internalBeanDefinitionMap置空
                                internalBeanDefinitionMap.clear();
                                break;
                            }
                        }

                    }else {
                        // 这一步就说明容器中类型相同的只有1个
                        Object bean = getBean(declaredField.getName());
                        declaredField.setAccessible(true);
                        declaredField.set(instance, bean);
                        // 将内部的internalBeanDefinitionMap置空
                        internalBeanDefinitionMap.clear();
                    }
                }
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
  1. 首先方法内,我定义了一个内部的internalBeanDefinitionMap,这个是用于存储当容器中有多个BeanDefinition的class属性能够匹配的上含@Autowired注解属性的类型,这个是我用于判断是否需要接下来的ByName。
  2. 首先通过反射创建出instance对象,此时这个对象还没有任何的属性注入
  3. 通过反射来获取instance对象中包含@Autowired注解的属性
  4. 获取属性的Type来和BeanDefinitionMap元素里的class进行比较,如果相同,就将这个BeanDefinition加入到自己方法内定义的internalBeanDefinitionMap
  5. 属性一一比较完毕后,就需要判断internalBeanDefinitionMap里的元素是否>1,如果>1,则说明需要ByName,就获取属性的name,来和BeanDefinitonMap的元素的key进行比较即可,只要有相同就注入

接下来手动测试一下趴

public class UserService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private NameService nameService;
}

userService里依赖注入了OrderService和NameService,get,set方法就不贴出来了(占空间)

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
        UserService userService = (UserService) zjhApplicationContext.getBean("userService");
        System.out.println(userService.getOrderService().toString());
        System.out.println(userService.getNameService().toString());
    }
}

结果
在这里插入图片描述

2.2 这里实现一个小功能,如果类有name属性,我们自动将beanName给赋值到这个name属性上

说一下实现思路:

  1. 创建一个接口,其包含了一个给name赋值的方法
  2. 类实现这个接口
  3. 在createBean方法内,依赖注入完毕后判断通过反射创建出来的对象是否实现了这个接口,如果实现了就通过对象调用接口的方法,在这里将Bean的名称赋值到类中
public interface BeanNameAware {
    public void setBeanName(String beanName);
}

接口代码如上

@Component("userService")
@Scope("singleton")
public class UserService implements BeanNameAware {

    @Autowired
    private OrderService orderService;

    @Autowired
    private NameService nameService;

    private String name;
    }

测试的类的代码如上

 /**
     * 创建Bean对象的方法,
     * @param beanDefinition
     * @return
     */
    public Object createBean(String beanNames,BeanDefinition beanDefinition){
        // 方法内部的存储BeanDefinition的Map
        ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();

        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            // 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
            // 通过反射来获取class的所有的属性
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 判断属性上是否包含了Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    // 进入到这一步就说明已经包含了AutoWired注解
                    // 接下来获取包含@Autowired注解的属性的属性
                    Class<?> type = declaredField.getType();
                    System.out.println("aclass"+type.toString());
                    // 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        // 进行比较
                        if (entry.getValue().getClazz().equals(type)){
                            System.out.println("有的哦");
                            internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
                        }
                    }


                    // 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
                    if (internalBeanDefinitionMap.size()>1){
                        // 先获取有@AutoWired注解的属性的名称
                        String beanName = declaredField.getName();
                        // 不断从internalBeanDefinitionMap中进行比较
                        for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
                            if (beanName.equals(entry.getKey())){
                                // 到这一步就说明Name有相同的了,通过name来调用getBean方法
                                Object bean = getBean(beanName);
                                // 属性注入
                                declaredField.setAccessible(true);
                                declaredField.set(instance,bean);
                                // 将内部的internalBeanDefinitionMap置空
                                internalBeanDefinitionMap.clear();
                                break;
                            }
                        }

                    }else {
                        // 这一步就说明容器中类型相同的只有1个
                        Object bean = getBean(declaredField.getName());
                        declaredField.setAccessible(true);
                        declaredField.set(instance, bean);
                        // 将内部的internalBeanDefinitionMap置空
                        internalBeanDefinitionMap.clear();
                    }
                }
            }
            if (instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanNames);
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

createBean方法如上

2.3 InitializingBean

其实在Spring中,我们知道有一个接口InitializingBean,其有一个方法afterPropertiesSet方法,我们可以自定义类实现这个接口里的这个方法,在方法里写自己的逻辑,Spring就可以在初始化Bean的时候给我们调用这个方法,那么这个应该如何实现呢?其实和上面的那个小功能一样。

public interface InitializingBean {
    void afterPropertiesSet();
}

InitializingBean 接口


    /**
     * 创建Bean对象的方法,
     * @param beanDefinition
     * @return
     */
    public Object createBean(String beanNames,BeanDefinition beanDefinition){
        // 方法内部的存储BeanDefinition的Map
        ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();

        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            // 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
            // 通过反射来获取class的所有的属性
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 判断属性上是否包含了Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    // 进入到这一步就说明已经包含了AutoWired注解
                    // 接下来获取包含@Autowired注解的属性的属性
                    Class<?> type = declaredField.getType();
                    System.out.println("aclass"+type.toString());
                    // 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        // 进行比较
                        if (entry.getValue().getClazz().equals(type)){
                            System.out.println("有的哦");
                            internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
                        }
                    }


                    // 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
                    if (internalBeanDefinitionMap.size()>1){
                        // 先获取有@AutoWired注解的属性的名称
                        String beanName = declaredField.getName();
                        // 不断从internalBeanDefinitionMap中进行比较
                        for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
                            if (beanName.equals(entry.getKey())){
                                // 到这一步就说明Name有相同的了,通过name来调用getBean方法
                                Object bean = getBean(beanName);
                                // 属性注入
                                declaredField.setAccessible(true);
                                declaredField.set(instance,bean);
                                // 将内部的internalBeanDefinitionMap置空
                                internalBeanDefinitionMap.clear();
                                break;
                            }
                        }

                    }else {
                        // 这一步就说明容器中类型相同的只有1个
                        Object bean = getBean(declaredField.getName());
                        declaredField.setAccessible(true);
                        declaredField.set(instance, bean);
                        // 将内部的internalBeanDefinitionMap置空
                        internalBeanDefinitionMap.clear();
                    }
                }
            }
            // Aware回调
            if (instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanNames);
            }
            // Bean初始化
            if (instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

实现Bean初始化

@Component("userService")
@Scope("singleton")
public class UserService implements InitializingBean {

    @Autowired
    private OrderService orderService;

    @Autowired
    private NameService nameService;

    @Override
    public void afterPropertiesSet() {
        System.out.println("棒棒哒");
    }
    }

测试
在这里插入图片描述

3. BeanPostProcessor

BeanPostProcessor,bean的后置处理器,其实在Spring中Bean的生命周期是有许多步骤,实例化前后,初始化前后等等,在这些环节,Spring能够给你提供方法,你自己编写逻辑,Spring可以帮你把你的逻辑在对应的阶段进行调用。

实现思路:

  1. 定义接口
  2. 类实现接口
  3. 在scan方法,判断类包含@Conpoment注解后判断类是否实现了BeanPostProcessor接口,如果实现了,就实例化,并加入到list中
  4. 在createBean方法中,调用InitializingBean方法的前后分别调用BeanPostProcessor的postProcessBeforeInitialization方法和postProcessAfterInitialization方法
public interface BeanPostProcessor {
	// 初始化前
    Object postProcessBeforeInitialization(Object bean, String beanName);
	// 初始化后
    Object postProcessAfterInitialization(Object bean, String beanName);
}

先定义这个接口


    /**
     * 初始化Bean容器的方法
     * @param configClass
     */
    private void scan(Class configClass){
        // 拿到配置类后会去解析这个配置类

        // 1. 解析@ComponentScan注解的信息 -> 获取扫描路径 -> 扫描

        // 查看配置类里有没有ComponentScan注解
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 获取扫描路径
        String path = componentScanAnnotation.value();
        System.out.println(path); // test.service

        // 扫描路径的类是否加入了Component注解

        // 1. 根据扫描路径来得到路径下的所有类,通过类加载器来获取,这里需要应用类加载器,因为Bootstrap,ext加载器不加载我们自定义的类,通过自己的类来获取对应的类加载器
        ClassLoader classLoader = ZJHApplicationContext.class.getClassLoader();

        path = path.replace(".", "//");
        // 这个resource是一个目录
        URL resource = classLoader.getResource(path);
        // 通过File类来获取目录下的文件
        File file = new File(resource.getFile());
        // 如果文件是一个文件夹,则获取文件夹下的所有的文件
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println(f);

                // 将D:\Tools\Spring_ZJH\target\classes\test\service\UserService.class转为 test.service.UserService
                String fileName = f.getAbsolutePath();

                // 判断文件是不是class文件,如果是的话在开始截取
                if (fileName.endsWith(".class")) {
                    // 从test开始截取,到.class结束
                    String className = fileName.substring(fileName.indexOf("test"), fileName.indexOf(".class"));
                    // 将\转换为.
                    className = className.replace("\\", ".");
                    // 得到了类的路径
                    System.out.println(className);

                    try {
                        // 通过类加载器来加载类
                        Class<?> clazz = classLoader.loadClass(className);
                        // 判断类上是否有Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 判断类是否继承了BeanPostProcessor
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    // 如果继承了,就创建这个BeanPostProcessor对象,并添加到list中
                                
                                try {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                    beanPostProcessorList.add(instance);
                                } catch (InstantiationException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                } catch (NoSuchMethodException e) {
                                    e.printStackTrace();
                                }

                                
                            }

                            // 到这一步就说明扫描类中已经有了Component注解,即它是一个Bean对象

                            // 到了这一步得判断这个Bean是单例Bean还是Prototype的Bean(即多例Bean)
                            // 如果是单例Bean则将Bean对象加入到singletonObjects单例池中
                            // 在加入容器时,我们需要判断Bean是单例还是多例,在getBean方法时,我们也需要判断,但其实没有必要判断两次,Spring中有BeanDefinition的概念,即Bean的定义
                            // 每解析一个类都会生成一个BeanDefinition对象

                            // 获取Bean的名称
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            // 生成BeanDefinitation对象,注意这个不是Bean对象,而是Bean的定义
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);

                            if (clazz.isAnnotationPresent(Scope.class)){
                                // 设置BeanDefinitation对象的scope属性
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            }else {
                                // Bean中没有scope注解,则将BeanDefinition的scope属性设置为singleton,即单例
                                beanDefinition.setScope("singleton");

                            }

                            // 将BeanDefinition对象加入到BeanDefinitionMap中
                            beanDefinitionMap.put(beanName,beanDefinition);


                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

scan方法

/**
     * 创建Bean对象的方法,
     * @param beanDefinition
     * @return
     */
    public Object createBean(String beanNames,BeanDefinition beanDefinition){
        // 方法内部的存储BeanDefinition的Map
        ConcurrentHashMap<String,BeanDefinition> internalBeanDefinitionMap = new ConcurrentHashMap();

        // 通过BeanDefinition的class属性来通过反射创建出对象
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            // 依赖注入,对象里面有很多属性,那么哪些属性需要赋值?
            // 通过反射来获取class的所有的属性
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 判断属性上是否包含了Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    // 进入到这一步就说明已经包含了AutoWired注解
                    // 接下来获取包含@Autowired注解的属性的属性
                    Class<?> type = declaredField.getType();
                    System.out.println("aclass"+type.toString());
                    // 从BeanDefinitionMap中遍历,找到和包含@Autowired注解的属性的属性相同的BeanDefinition,并把BeanDefinition存入到internalBeanDefinitionMap
                    for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                        // 进行比较
                        if (entry.getValue().getClazz().equals(type)){
                            System.out.println("有的哦");
                            internalBeanDefinitionMap.put(entry.getKey(), entry.getValue());
                        }
                    }


                    // 判断internalBeanDefinitionMap里有几个元素,如果大于1,就说明相同类型有两个,我们就通过ByName来进行注入
                    if (internalBeanDefinitionMap.size()>1){
                        // 先获取有@AutoWired注解的属性的名称
                        String beanName = declaredField.getName();
                        // 不断从internalBeanDefinitionMap中进行比较
                        for (Map.Entry<String, BeanDefinition> entry : internalBeanDefinitionMap.entrySet()) {
                            if (beanName.equals(entry.getKey())){
                                // 到这一步就说明Name有相同的了,通过name来调用getBean方法
                                Object bean = getBean(beanName);
                                // 属性注入
                                declaredField.setAccessible(true);
                                declaredField.set(instance,bean);
                                // 将内部的internalBeanDefinitionMap置空
                                internalBeanDefinitionMap.clear();
                                break;
                            }
                        }

                    }else {
                        // 这一步就说明容器中类型相同的只有1个
                        Object bean = getBean(declaredField.getName());
                        declaredField.setAccessible(true);
                        declaredField.set(instance, bean);
                        // 将内部的internalBeanDefinitionMap置空
                        internalBeanDefinitionMap.clear();
                    }
                }
            }
            // Aware回调
            if (instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanNames);
            }

            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanNames);
            }

            // Bean初始化
            if (instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }
            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanNames);
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

createBean方法

主要就是在扫描的时候,将BeanPostProcessor的实现类加入到list,在createBean方法中的调用BeanPostProcessor的方法。

4. AOP

如果了解SpringBean生命周期的话,就知道AOP操作是初始化后这个阶段进行的AOP,上面叙述BeanPostProcessor就是为了AOP做准备。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EnableAspectJAutoProxy {
}

上述是AOP的注解

@Component("zJHBeanPostProcessor")
@Scope("singleton")
public class ZJHBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // Spring把Bean对象和BeanName给你,你自己进行逻辑编写

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean.getClass().isAnnotationPresent(EnableAspectJAutoProxy.class)) {
            // 类上有AOP的注解
            System.out.println("初始化后");
            Object proxyInstance = Proxy.newProxyInstance(ZJHBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("这是代理逻辑");
                    // 调用原对象的方法
                    return method.invoke(bean,args);

                }
            });
            return proxyInstance;
        }
        return bean;
    }
}

在具体的PostProcessor的实现类里进行实现后置处理来实现AOP

这样子因为PostProcessor实现类是进入容器的且实现了PostProcesser,所以在scan方法中就会将它加入到beanPostProcessorList集合中,在createBean中因为beanPostProcessorList里有元素了,所以每一个bean都会执行PostProcess实现类的前置后置处理器的逻辑,AOP的逻辑就在后置处理器中,首先判断类是否含有AOP注解,如果有就通过JDK的方式来生成代理对象,这里就没有实现方法的切入,只是手动模拟代理逻辑。

@Component("zJHBeanPostProcessor")
@Scope("singleton")
public class ZJHBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // Spring把Bean对象和BeanName给你,你自己进行逻辑编写

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean.getClass().isAnnotationPresent(EnableAspectJAutoProxy.class)) {
            // 类上有AOP的注解
            System.out.println("初始化后");
            Object proxyInstance = Proxy.newProxyInstance(ZJHBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("这是代理逻辑");
                    // 调用原对象的方法
                    return method.invoke(bean,args);

                }
            });
            return proxyInstance;
        }
        return bean;
    }
}

PostProcessor实现类如上,最终返回的是代理对象,测试一下结果

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        ZJHApplicationContext zjhApplicationContext = new ZJHApplicationContext(AppConfig.class);
        Object userService = zjhApplicationContext.getBean("userService");

        System.out.println("userService的类型"+userService.getClass());
    }
}

在这里插入图片描述
可以看到最后返回的是代理对象

以上的都只是模拟Spring,真正的Spring核心代码远远不止这些。

代码地址: https://gitee.com/Nov_Zsir/zjh_-spring

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值