Spring框架核心功能实现

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 概要手写Spring启动以及扫描流程手写getBean流程手写Bean生命周期流程手写依赖注入流程手写BeanPostProcessor机制手写Aop机制Spring启动以及扫描流程实现我们平时都是使用这两种方法获得spring容器,上面的是通过加载类路径上的配置文件来获得容器。下面的方式和上面的原理相同只不过是通过注解的形式去实现,我们传入的也是一个配置类的class文件,我们可以把这个文件类比成第一种方法中的xml文件,然后这个xml文件里的一个个标签都变成了注解。基础环境搭建首先搭建好基础环境:我们的测试类:public class MySpringT

概要
手写Spring启动以及扫描流程
手写getBean流程
手写Bean生命周期流程
手写依赖注入流程
手写BeanPostProcessor机制
手写Aop机制
Spring启动以及扫描流程实现

我们平时都是使用这两种方法获得spring容器,上面的是通过加载类路径上的配置文件来获得容器。下面的方式和上面的原理相同只不过是通过注解的形式去实现,我们传入的也是一个配置类的class文件,我们可以把这个文件类比成第一种方法中的xml文件,然后这个xml文件里的一个个标签都变成了注解。

基础环境搭建
首先搭建好基础环境:

我们的测试类:

public class MySpringTest {
public static void main(String[] args) {
MyApplicationContext applicationContext = new MyApplicationContext(AppConfig.class);

    Object bean = applicationContext.getBean("");

    System.out.println(bean);
}

}
1
2
3
4
5
6
7
8
9
我们的容器类MyApplicationContext :

public class MyApplicationContext {
private Class configClass;

public MyApplicationContext(Class configClass) {
    this.configClass = configClass;
}

public Object getBean(String beanName){
    return null;
}

}
1
2
3
4
5
6
7
8
9
10
11
我们原来在编写Spring的配置文件的时候会使用一个注解@ComponentScan,来定义一个扫描路径。所以我们这里也定义了一个。

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

String value();

}
1
2
3
4
5
6
Retention指定该注解的生命周期,设置成RUNTIME便于反射获取

Target指定注解的使用位置,设置为TYPE表示能在类上使用

我们的AppConfig;

@ComponentScan("com.zyb.service")
public class AppConfig {
}
1
2
3
根据扫描包我们再创建一个业务层类UseService,这个UseService我们一般会使用@Component注解进行标记,这里我们也是如此:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value();
}
1
2
3
4
5
@Component("userService")
public class UserService {

}
1
2
3
4
扫描逻辑实现
我们的思路就是;

解析配置类
拿到ComponentScan注解
得到扫描路径
进行扫描
首先我们可以通过如下代码拿到AppConfig中ComponentScan的内容:

public MyApplicationContext(Class configClass) {
this.configClass = configClass;
//解析配置类
//ComponentScan注解 --》 扫描路径 --》 扫描

    ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScanAnnotation.value();
    System.out.println(path);

}
1
2
3
4
5
6
7
8
9
10
我们测试一下:

拿到路径之后我们就可以开始扫描了。而扫描的目的并不是包下的所有类,而是那些带有@Component注解的类,而Spring会将他们的对象当作Spring中的一个bean。

这里我们扫描的思路如下:

通过类加载器,得到类路径上的class文件
对文件进行筛选
是否以class结尾(判断是否为class文件)
对class文件名进行处理
替换\为.
截取全限定名
然后将类加载进jvm虚拟机
判断运行时类是否有@Component注解,如果有则进行相关的创建bean对象操作
代码如下:

public MyApplicationContext(Class configClass) {
    this.configClass = configClass;
    //解析配置类
    //ComponentScan注解 --》 扫描路径 --》 扫描
    ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScanAnnotation.value();
    String searchPath = path.replace(".", "/");
    ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(searchPath);
    File file = new File(resource.getFile());
    if (file.isDirectory()) {
        //首先筛选末尾是.class文件的
        File[] files = file.listFiles();
        for (File classFile: files) {
            String absolutePath = classFile.getAbsolutePath();
            if (absolutePath.endsWith(".class")) {
                //拼接全限定名
                String fullyQualifiedClassName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                fullyQualifiedClassName = fullyQualifiedClassName.replace("\\", ".");
                //使用app类加载器加载这个类
                Class<?> aClass = null;
                try {
                    aClass = classLoader.loadClass(fullyQualifiedClassName);
                    if (aClass.isAnnotationPresent(Component.class)) {
                        //然后加载bean

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

            }
        }
    }
}

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
注意;

classLoader.loadClass接收的是类的全限定名
当我们确认当前类有@Component注解的时候并不是急着去给其创建bean,我们在使用spring的时候是可以决定该bean是否为单例的。我们在这里还是创建一个同名注解@Scope:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value() default "single";
}
1
2
3
4
5
默认是single单例的,如果是原型bean则传入prototype。

而我们实现单例bean是通过一个Map单例池,不考虑懒加载。

现在我们的大致思路就是在通过@Scope注解确定bean是单例模式还是原型模式之后,在进行相应的bean的创建,但是这里我们考虑到一个问题,我们在getBean的时候如果每次都去解析这个类获得@Scope是很麻烦的。所以我们在这里引入一个新的概念BeanDefinition。

BeanDefinition中包含了当前bean的所有定义,例如bean的类型、作用域、是否懒加载等等。注意bean的name不在BeanDefinition中:

public class BeanDefinition {
private Class beanClass;
private String scope;

public Class getBeanClass() {
    return beanClass;
}

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

public String getScope() {
    return scope;
}

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

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在Spring中每扫描到一个bean都会对其进行解析然后生成各自的BeanDefinition实例,随后将这个BeanDefinition对象放在map中,key为bean的name,BeanDefinition对象作为value。以后当我们要获取一个bean时先会去map中查看,如果查找不到bean的BeanDefinition才会去解析类,以此来减少解析类的次数提高效率。

接下来我们完善一下代码,思路如下:

判断运行时类是否有Component注解
如果有,再获取该类的@Scope
如果没有注解则为默认的单例模式
如果有此注解则为原型模式
然后再把对应的模式添加到BeanDefinition
将此BeanDefinition和对应的bean name添加到beanDefinitionMap中
getBean方法思路:

判断要获取的bean的BeanDefinition是否存在于beanDefinitionMap
如果不存在,说明容器中不存在该bean则直接报错
如果存在则在BeanDefinition中拿到该bean的scope,根据scope再去具体的创建bean
代码如下:

public class MyApplicationContext {
private Class configClass;
private ConcurrentHashMap singleBeanHashMap = new ConcurrentHashMap<>();
private HashMap beanDefinitionHashMap = new HashMap<>();

public MyApplicationContext(Class configClass) {
    this.configClass = configClass;
    //解析配置类
    //ComponentScan注解 --》 扫描路径 --》 扫描
    ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScanAnnotation.value();
    String searchPath = path.replace(".", "/");
    ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(searchPath);
    File file = new File(resource.getFile());
    if (file.isDirectory()) {
        //首先筛选末尾是.class文件的
        File[] files = file.listFiles();
        for (File classFile: files) {
            String absolutePath = classFile.getAbsolutePath();
            if (absolutePath.endsWith(".class")) {
                //拼接全限定名
                System.out.println(classFile);
                String fullyQualifiedClassName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                fullyQualifiedClassName = fullyQualifiedClassName.replace("\\", ".");
                //使用app类加载器加载这个类
                Class<?> aClass = null;
                try {
                    aClass = classLoader.loadClass(fullyQualifiedClassName);
                    if (aClass.isAnnotationPresent(Component.class)) {
                        BeanDefinition beanDefinition = new BeanDefinition();
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
                            String scope = scopeAnnotation.value();
                            beanDefinition.setScope(scope);
                        }else{
                            beanDefinition.setScope("single");
                        }

                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        beanDefinitionHashMap.put(beanName,beanDefinition);

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

            }
        }
    }
}

public Object getBean(String beanName){
    //首先判断是否存在该bean的BeanDefinition
    if (beanDefinitionHashMap.containsKey(beanName)) {
        BeanDefinition beanDefinition = beanDefinitionHashMap.get(beanName);
        if (beanDefinition.getScope().equals("single")) {
            //单例模式创建bean
        }else {
            //原型模式创建bean
        }
    }else {
        throw new NullPointerException("不存在该bean");
    }
}

}

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
56
57
58
59
60
61
62
63
64
65
66
67
68
最后我们可以把扫描这部分逻辑提取出来重新建立一个scan方法:

public class MyApplicationContext {
private Class configClass;
private ConcurrentHashMap singleBeanHashMap = new ConcurrentHashMap<>();
private HashMap beanDefinitionHashMap = new HashMap<>();

public MyApplicationContext(Class configClass) {
    this.configClass = configClass;
    scan(configClass);
}

private void scan(Class configClass) {
    //解析配置类
    //ComponentScan注解 --》 扫描路径 --》 扫描
    ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScanAnnotation.value();
    String searchPath = path.replace(".", "/");
    ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(searchPath);
    File file = new File(resource.getFile());
    if (file.isDirectory()) {
        //首先筛选末尾是.class文件的
        File[] files = file.listFiles();
        for (File classFile: files) {
            String absolutePath = classFile.getAbsolutePath();
            if (absolutePath.endsWith(".class")) {
                //拼接全限定名
                System.out.println(classFile);
                String fullyQualifiedClassName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                fullyQualifiedClassName = fullyQualifiedClassName.replace("\\", ".");
                //使用app类加载器加载这个类
                Class<?> aClass = null;
                try {
                    aClass = classLoader.loadClass(fullyQualifiedClassName);
                    if (aClass.isAnnotationPresent(Component.class)) {
                        BeanDefinition beanDefinition = new BeanDefinition();
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
                            String scope = scopeAnnotation.value();
                            beanDefinition.setScope(scope);
                        }else{
                            beanDefinition.setScope("single");
                        }

                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        beanDefinitionHashMap.put(beanName,beanDefinition);

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

            }
        }
    }
}

public Object getBean(String beanName){
    //首先判断是否存在该bean的BeanDefinition
    if (beanDefinitionHashMap.containsKey(beanName)) {
        BeanDefinition beanDefinition = beanDefinitionHashMap.get(beanName);
        if (beanDefinition.getScope().equals("single")) {
            //单例模式创建bean
        }else {
            //原型模式创建bean
        }
    }else {
        throw new NullPointerException("不存在该bean");
    }
}

}

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
bean创建的简单实现
spring容器启动之后,先进行扫描步骤,而这个扫描的主要作用就是得到BeanDefinition,这样我们就有了BeanDefinitionMap,然后我们就可以根据BeanDefinitionMap去创建单例池singleBeanHashMap为我们的getBean方法提供支持。

在getBean方法中:

如果是单例模式的bean就直接在singleBeanHashMap中去拿
如果是原型模式的bean就直接使用creatBean方法创建bean
我们这里的creatBean方法就是用来创建bean的方法,我们这里暂时只对此方法进行一个简单的实现。

整体代码如下:

public class MyApplicationContext {
private Class configClass;
private ConcurrentHashMap singleBeanHashMap = new ConcurrentHashMap<>();
private HashMap beanDefinitionHashMap = new HashMap<>();

public MyApplicationContext(Class configClass) {
    this.configClass = configClass;
    scan(configClass);
    //scan之后就得到了beanDefinitionHashMap,然后我们根据此来构建singleBeanHashMap
    for (String beanName:beanDefinitionHashMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionHashMap.get(beanName);
        if (beanDefinition.getScope().equals("single")) {
            singleBeanHashMap.put(beanName,createBean(beanName));
        }
    }
}

public Object createBean(String name){
    BeanDefinition beanDefinition = beanDefinitionHashMap.get(name);
    Class beanClass = beanDefinition.getBeanClass();
    Object instance = null;
    try {
        instance = beanClass.getConstructor().newInstance();
    } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }
    return instance;
}

private void scan(Class configClass) {
    //解析配置类
    //ComponentScan注解 --》 扫描路径 --》 扫描
    ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScanAnnotation.value();
    String searchPath = path.replace(".", "/");
    ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(searchPath);
    File file = new File(resource.getFile());
    if (file.isDirectory()) {
        //首先筛选末尾是.class文件的
        File[] files = file.listFiles();
        for (File classFile: files) {
            String absolutePath = classFile.getAbsolutePath();
            if (absolutePath.endsWith(".class")) {
                //拼接全限定名
                System.out.println(classFile);
                String fullyQualifiedClassName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                fullyQualifiedClassName = fullyQualifiedClassName.replace("\\", ".");
                //使用app类加载器加载这个类
                Class<?> aClass = null;
                try {
                    aClass = classLoader.loadClass(fullyQualifiedClassName);
                    if (aClass.isAnnotationPresent(Component.class)) {
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setBeanClass(aClass);
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
                            String scope = scopeAnnotation.value();
                            beanDefinition.setScope(scope);
                        }else{
                            beanDefinition.setScope("single");
                        }

                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        beanDefinitionHashMap.put(beanName,beanDefinition);

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

            }
        }
    }
}

public Object getBean(String beanName){
    //首先判断是否存在该bean的BeanDefinition
    if (beanDefinitionHashMap.containsKey(beanName)) {
        BeanDefinition beanDefinition = beanDefinitionHashMap.get(beanName);
        if (beanDefinition.getScope().equals("single")) {
            //单例模式创建bean
            return singleBeanHashMap.get(beanName);
        }else {
            //原型模式创建bean
            return createBean(beanName);
        }
    }else {
        throw new NullPointerException("不存在该bean");
    }
}

}

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
我们测试一下效果:

当UserService类上的Scope Vablue值为prototype

当UserService类上的Scope Vablue值为single或者去掉Scope注解

依赖注入实现
前面我们对createBean方法进行了一个简单地实现,这里依赖注入的实现就是将createBean写完整。我们知道不管是单例bean还是原型bean他们的作用范围可能不同,但是他们在创建某一个对象的时候步骤是一样的,这也就是我们为什么要单独抽取一个方法出来专门用来创建bean。

这里我们只考虑使用@Autowired注解进行注入,createBean方法思路:

首先通过beanName在BeanDefinition中拿到Class对象
通过空参构造器创建instance对象
遍历当前Class所有成员变量,检查是否有@Autowired注解
如果没有则直接调用空参构造器返回bean
如果有,那么我们此时只能根据当前属性的属性名以及类型进行依赖注入,这里我们选择简单一点的按照名称注入,思路就是将属性名传入到getBean方法中尝试去获取bean,在getBean方法我们会对当前依赖进行一个判断
如果是单例模式,则将依赖直接从单例池中拿出来对依赖进行赋值
如果不是单例模式,则递归调用createBean方法,创建依赖,再对依赖进行赋值
代码如下:

public Object createBean(String name){
    BeanDefinition beanDefinition = beanDefinitionHashMap.get(name);
    Class beanClass = beanDefinition.getBeanClass();

    Object instance = null;
    try {
        instance = beanClass.getConstructor().newInstance();
        for (Field field:beanClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                //拿到属性名
                String fieldName = field.getName();
                field.set(instance,getBean(fieldName));
            }
        }
    } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }
    return instance;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
我们测试一下:

结果:

BeanNameAware回调实现
我们思考一个场景:

如果我们想获得当前的beanName,这里是不能使用Autowired的。Spring为我们提供了一种接口BeanNameAware,将当前的beanName返回给我们。

我们将这个功能实现一下:

首先写一个BeanNameAware接口:

public interface BeanNameAware {
void setBeanNameAware(String beanName);
}
1
2
3
然后我们在createBean方法中调用接口方法,传入bean的name:

public Object createBean(String name){
    ···

        //进行依赖注入
        ···

        //BeanNameAware返回bean name
        if (instance instanceof BeanNameAware) {
            BeanNameAware beanNameAware = (BeanNameAware) instance;
            beanNameAware.setBeanNameAware(name);
        }

    ···
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
初始化机制模拟实现
在bean的属性默认初始化设置完之后,我们程序员可以自定义进行业务方面的初始化,例如bean中的属性是否为空或者说是否符合业务方面的要求,或者你想对某一个属性赋值。那么这个功能如何完成呢?

在Spring中我们可以去实现InitializingBean接口来完成这一个功能,Spring在检测到当前类实现了InitializingBean接口之后,就会帮我们调用接口方法完成我们自定义的初始化。

InitializingBean接口定义如下:

public interface InitializingBean {
void afterPropertiesSet();
}
1
2
3
然后我们在createBean方法中调用接口方法:

public Object createBean(String name){
    ···

        //进行依赖注入
        ···

        //BeanNameAware返回bean name
        if (instance instanceof BeanNameAware) {
            BeanNameAware beanNameAware = (BeanNameAware) instance;
            beanNameAware.setBeanNameAware(name);
        }
        //自定义初始化
        if (instance instanceof InitializingBean) {
            InitializingBean initializingBean = (InitializingBean) instance;
            initializingBean.afterPropertiesSet();
        }

    ···
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BeanPostProcessor模拟实现
BeanPostProcessor:bean的后置处理器

我们在前文中提到的初始化机制、Aware回调机制都是Spring提供给我们的拓展功能,我们只需要在需求类上实现相应接口,Spring即可帮我们完成需求。

那么Spring能否给我们提供帮助:在实例化之前或之后给我们提供额外的功能追加,或者说在初始化之前或初始化之后给我们提供额外的功能追加。

为了实现这一需求Spring为我们提供了BeanPostProcessor。

我们来看一下Spring的源码:

public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return bean;
}

}
1
2
3
4
5
6
7
8
9
10
11
postProcessBeforeInitialization:初始化前追加功能
postProcessAfterInitialization:初始化后追加功能

BeanPostProcessor有很多子接口,其中InstantiationAwareBeanPostProcessor的源码如下:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    return true;
}

@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
    return null;
}

/** @deprecated */
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
    return pvs;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
postProcessBeforeInstantiation:实例化之前追加功能
postProcessAfterInstantiation:实例化之后追加功能

我们在Spring框架中要想使用这一个功能,一般是新创建一个类然后去实现相应的接口,而不会去在业务层的类中去实现。因为初始化、实例化都是相对于每一个bean来说的,我们理应将他们单独抽离出来,而且这样不会污染业务代码。

接下来我们开始BeanPostProcessor的代码实现:

实现思路如下:

扫描类的时候(也就是scan方法中) 看是否有类实现了BeanPostProcessor接口
如果实现了该接口,则创建该类的实例
将实例放入beanPostProcessorList中
在createBean方法的相应的位置将列表中的实例一个个取出来,然后调用他们的方法
代码实现:

public interface BeanPostProcessor {

Object postProcessBeforeInitialization(Object bean, String beanName);


Object postProcessAfterInitialization(Object bean, String beanName);

}

1
2
3
4
5
6
7
8
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化之前");
if (beanName.equals("userService")) {
System.out.println("为userService初始化之前追加的功能");
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    System.out.println("初始化之后");
    return bean;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在MyApplicationContext中新建一个成员属性beanPostProcessorList:

scan方法:

注意:

这里的isAssignableFrom方法和instanceof要区别开
instanceof是判断某一个对象与父类或者接口的关系
isAssignableFrom是判断某一个类与父类或者接口的关系
同时isAssignableFrom方法与instanceof的用法也不一样,例如
判断a对象是否实现b接口:a instanceof b
判断a类是否实现b接口:b.isAssignableFrom(a)
这里的代码逻辑与Spring源码并不相同,在Spring源码中这里创建BeanPostProcessor的实例对象时使用的getBean方法,这样走的是Spring内部的逻辑可以处理当BeanPostProcessor的实现类里面存在使用@Autowired进行依赖注入的情况。但是我们这里并没有考虑这样的情况,所以我们直接得到类的构造器去创建BeanPostProcessor的实例对象
createBean方法:

我们测试一下:

public class MySpringTest {
public static void main(String[] args) {
MyApplicationContext applicationContext = new MyApplicationContext(AppConfig.class);

// UserService userService = (UserService) applicationContext.getBean("userService");
// System.out.println(userService.getOrderService());
// System.out.println(userService.getBeanName());

}

}
1
2
3
4
5
6
7
8
9
10
结果:

我们的扫描范围时service包下,而我们的service包下有三个类,与结果相吻合。

AOP模拟实现
在spring框架中,使用代理模式比较典型的场景就是AOP的实现了,代理逻辑核心要点如下:

默认使用 JDK 动态代理,这样可以代理所有的接口类型;
如果目标对象没有实现任何接口,则默认采用CGLIB代理;
可强制使用CGLIB,指定proxy-target-class = “true” 或者 基于注解@EnableAspectJAutoProxy(proxyTargetClass = true)
AOP的工作流程可以直接看我的另一篇文章:
[Spring Framework]AOP工作流程

除了动态代理技术之外,AOP在Spring中的实现还需要借助BeanPostProcessor。我们知道BeanPostProcessor是bean的后置处理器,其可以在bean初始化的前后对bean造成额外的影响。

这里我们可以借助BeanPostProcessor接口中的postProcessAfterInitialization方法,返回需要增强类的代理对象。

接下来我们进行代码实现:

这里我们实现JDK动态代理的情况,所以先要为委托类编写接口方法,方便根据接口实现代理对象:

public interface ProxyInterface {
void show();
}
1
2
3
@Component("userService")
@Scope("single")
public class UserService implements BeanNameAware, InitializingBean,ProxyInterface {

@Autowired
private  OrderService orderService;

private String beanName;

public String getBeanName() {
    return beanName;
}
@Override
public void afterPropertiesSet() {
    System.out.println("自定义初始化");
}


@Override
public void setBeanNameAware(String beanName) {
    this.beanName = beanName;
}

public OrderService getOrderService() {
    return orderService;
}

public void show(){
    System.out.println("这里是UserService的实例");
}

}

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
然后我们重写一下postProcessAfterInitialization方法:

@Component("myBeanPostProcessor")
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化之前");
if (beanName.equals("userService")) {
System.out.println("为userService初始化之前追加的功能");
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    System.out.println("初始化之后");
    if (beanName.equals("userService")) {
        return Proxy.newProxyInstance(
                MyBeanPostProcessor.class.getClassLoader(),
                bean.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("show")) {
                            System.out.println("这里是对show方法的前置增强");
                        }
                        return method.invoke(bean, args);
                    }
                }
        );
    }
    return bean;
}

}

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
我们这里省略了是否进行AOP的判断以及找切点的逻辑

测试:

public class MySpringTest {
public static void main(String[] args) {
MyApplicationContext applicationContext = new MyApplicationContext(AppConfig.class);
ProxyInterface proxyInterface = (ProxyInterface) applicationContext.getBean("userService");
proxyInterface.show();
}
}
1
2
3
4
5
6
7
结果:

我们在Spring中使用AOP时会在配置类上使用一个注解@EnableAspectJAutoProxy。这个注解会向Spring的容器中注册一个bean:

而这个bean你往上一直寻找会发现,他其实就是一个BeanPostProcessor!

————————————————
版权声明:本文为CSDN博主「十八岁讨厌编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyb18507175502/article/details/129333353

目录
相关文章
|
6天前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
21 3
|
21天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
6天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
23 0
|
30天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
36 0
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
25天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
35 2
|
17天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
67 1
|
25天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
1月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。