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

目录
相关文章
|
21天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
21天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
4天前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
|
11天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
554 6
|
9天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
9天前
|
存储 NoSQL Java
Spring Session框架
Spring Session 是一个用于在分布式环境中管理会话的框架,旨在解决传统基于 Servlet 容器的会话管理在集群和云环境中的局限性。它通过将用户会话数据存储在外部介质(如数据库或 Redis)中,实现了会话数据的跨服务器共享,提高了应用的可扩展性和性能。Spring Session 提供了无缝集成 Spring 框架的 API,支持会话过期策略、并发控制等功能,使开发者能够轻松实现高可用的会话管理。
Spring Session框架
|
16天前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
27 2
|
16天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
25天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
104 1
|
29天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
58 2
下一篇
无影云桌面