手搓Spring

发布于:2025-09-14 ⋅ 阅读:(25) ⋅ 点赞:(0)

目录

两种方法创建Spring容器

自定义Spring容器及前置操作

Spring扫描逻辑实现

createBean()方法

getBean()方法

依赖注入(DI)

BeanNameAware接口

InitializingBean接口

BeanPostProcessor接口 + AOP的实现


Spring 是一个轻量级的 Java 开发框架,最初由 Rod Johnson 在 2002 年提出,主要解决 企业级开发中的复杂性问题。其核心主要是 IoC(控制反转)AOP(面向切面编程)

  • IoC(Inversion of Control)控制反转

    • 对象的创建和依赖的维护由容器(Spring)来管理,而不是由程序员自己 new 出来。

    • 核心实现方式:DI(依赖注入)

  • AOP(Aspect Oriented Programming)面向切面编程

    • 通过代理方式实现横切逻辑的解耦(如事务、日志、权限)。

    • 核心实现方式:动态代理

而我们可以通过学习手搓Spring的实现来快速了解Spring底层源码的实现。


两种方法创建Spring容器


我们想要再启动类上构建Spring容器可以使用下面两种方法:

  1. ClassPathXmlApplicationContext => 在 XML 文件中定义了 <bean> 元素(或者 <context:component-scan>),Spring 会根据这些配置来创建 Bean。
  2. AnnotationConfigApplicationContext  => 传入的是一个 Java 配置类AppConfig)。
public static void main(String[] args) {
    // ① 使用 XML 配置文件创建容器
    ClassPathXmlApplicationContext classPathXmlApplicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");

    // ② 使用 Java 配置类(注解方式)创建容器
    AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(AppConfig.class);

    // ③ 从容器中获取 Bean
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.test();
}

而Java配置类可以这样定义:

通过@ComponentScan注解来指定Spring扫描哪个路径下的文件。也可以通过使用@Bean注解的方式直接定义Bean。

import cn.spring.com.spring.ComponentScan;

@ComponentScan("cn.spring.com.eleven.service")
public class AppConfig {

    @Bean   // 定义一个Bean,方法名默认就是Bean的id
    public UserService userService() {
        return new UserService();
    }
}

自定义Spring容器及前置操作


我们在手搓一个Spring的时候,直接使用Springboot工程创建即可,另外pom.xml不需要导入任何依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.spring.com</groupId>
    <artifactId>spring-eleven</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Spring-Eleven</name>

</project>

大致的结构如下:

我们将Spring该实现的注解、接口等放在Spring目录下,将我们自己的测试类放在eleven目录下。

随后我们先创建一个自定义容器类:

这里我们主要实现的是模拟 AnnotationConfigApplicationContext 使用Java配置类创建Spring容器。

package cn.spring.com.spring;

public class ElevenApplicationContext {

    private Class configClass; // 配置类

    /**
     * 构造Spring容器(需要传递配置类)
     * @param configClass 配置类
     */
    public ElevenApplicationContext(Class configClass) {
        this.configClass = configClass;
    }
}

之后编写配置类AppConfig:

package cn.spring.com.eleven;

public class AppConfig {

}

而为了实现Spring容器可以通过配置类扫描哪个路径下的文件,我们自定义@ComponentScan注解:

value参数代表的是扫描路径。        

package cn.spring.com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 组件扫描注解
 */
@Target(ElementType.TYPE) // 类型注解
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
public @interface ComponentScan {
    String value(); // 扫描的包路径
}

之后在AppConfig上添加@ComponentScan注解:

这里我准备扫描cn.spring.com.eleven.service路径下的文件,所以我将要在该目录下去创建我的Service。

package cn.spring.com.eleven;

import cn.spring.com.spring.ComponentScan;

@ComponentScan("cn.spring.com.eleven.service")
public class AppConfig {

}

创建UserService:

package cn.spring.com.eleven.service;

public class UserService{
    
}

而为了将UserService交给Spring容器去管理,我们需要自定义@Component注解:

package cn.spring.com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 组件注解
 */
@Target(ElementType.TYPE) // 类型注解
@Retention(RetentionPolicy.RUNTIME) // 运行时注解
public @interface Component {
    String value() default ""; // 组件的名称
}

之后在UserService上添加@Component注解:

@Component("userService")
public class UserService {

}

最后将自定义的Java配置类交给自定义容器:

package cn.spring.com.eleven;

import cn.spring.com.eleven.service.UserService;
import cn.spring.com.spring.ElevenApplicationContext;

public class Test {

    public static void main(String[] args) {
        // 传一个Spring容器的配置文件
        ElevenApplicationContext applicationContext = new ElevenApplicationContext(AppConfig.class);
    }
}

而我们创建 ElevenApplicationContext 肯定会调用其构造方法,所以为了实现实现扫描指定路径下的文件,我们需要拿到@ComponentScan内的value值,根据value值进行扫描那个目录下的文件。


Spring扫描逻辑实现


在 ElevenApplicationContext 构造方法中编写扫描逻辑,我们可以将扫描逻辑抽出成一个方法void Scan(),之后在构造方法内调用即可。

首先通过传递的 configClass,使用 getDeclaredAnnotation(ComponentScan.class) 方法来拿到ComponentScan注解,随后.value()拿到内部值并转换路径格式:

ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value(); // 扫描路径
path = path.replace(".", "/");

现在已经拿到了扫描路径,所以我们需要根据扫描路径拿到路径下的所有类。那么可以使用类加载器ClassLoader去拿刚刚获得的扫描路径下的资源,随后转换成目录:

// 扫描(使用应用类加载器ClassLoader)
ClassLoader classLoader = ElevenApplicationContext.class.getClassLoader();
// 因为是应用类加载器,会去 F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service 下拿到资源
// 这里的resource可以是资源也可以是目录,这里拿到的是目录
URL resource = classLoader.getResource(path);
// resource转换成file目录,方便调用
File file = new File(resource.getFile());

之后判断file是否为目录,如果是则通过 file.listFiles() 方法拿到目录下的所有文件:

if (file.isDirectory()) {
    File[] files = file.listFiles();
    for (File f : files) {
        System.out.println(f.getAbsolutePath());
    }
}

之后在循环file文件,首先根据目录来拿到类名className,随后根据类名拿到类对象Class<?> clazz = classLoader.loadClass(className),随后使用 clazz.isAnnotationPresent(Component.class) 判断当前类上是否有@Component注解。

// file是否为目录
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f.getAbsolutePath());
if (f.getAbsolutePath().endsWith(".class")) {
    // 转换可用的路径格式
    // F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service\UserService.class
    // cn.spring.com.eleven.service.UserService
    String className = f.getAbsolutePath().replace("\\", "/");
    className = className.substring(className.indexOf("cn"), className.indexOf(".class"));  // 截取类名
    className = className.replace("/", ".");  // 替换成点号
    System.out.println(className);
    // 加载类
    try {
        Class<?> clazz = classLoader.loadClass(className);
        // 判断是否有@Component注解
        if (clazz.isAnnotationPresent(Component.class)) {
            System.out.println("有@Component注解,表示当前类是一个Bean对象");
        }
    }
}

在上面我们已经可以判断该类上是否有@Component注解,由于有@Component注解,代表我们想要将该类生成一个Bean对象,所以需要构建 getBean() 方法。

在这里需要讲述Bean的作用域!!!

单例Bean与原型Bean的区别:

单例bean(singleton):在整个 Spring 容器中,同一个 Bean 定义只有一个实例

原型bean(prototype):每次调用 getBean() 时,都会创建一个新的实例

在Spring中是通过@Scope注解去解释当前Bean的作用域,所以我们创建该注解:

package cn.spring.com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 作用域注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    /**
     * 单例Bean还是原型Bean
     */
    String value() default "singleton";
}

之后我们可以在UserService加入注解让它暂时代表原型Bean:

package cn.spring.com.eleven.service;
import cn.spring.com.spring.*;

@Component("userService")
@Scope("prototype")
public class UserService {

}

现在UserService要创建的是原型Bean,所以我们在Test下拿到三次不同的UserService的Bean对象。

package cn.spring.com.eleven;

import cn.spring.com.eleven.service.UserService;
import cn.spring.com.spring.ElevenApplicationContext;

public class Test {

    public static void main(String[] args) {
        // 传一个Spring容器的配置文件
        ElevenApplicationContext applicationContext = new ElevenApplicationContext(AppConfig.class);
        Object userService =  applicationContext.getBean("userService");
        Object userService =  applicationContext.getBean("userService");
        Object userService =  applicationContext.getBean("userService");
    }
}

而现在如何让单例Bean能够确保getBean()可以得到同一个Bean对象呢?

这个时候我们可以使用单例池来存储创建出来的单例Bean对象:

// 单例池
// map存储的都是单例bean =》<bean名字,bean对象>
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

随后我们创建getBean方法,在方法内传入BeanName:

/**
 * 获取Bean对象
 * @param beanName Bean的名字
 * @return Bean对象
 */
public Object getBean(String beanName) {
    
}

之后我们需要研究如何使用beanName来判断是单例Bean还是原型Bean呢?

这个时候Spring底层是使用了 BeanDefinition 定义类:

用来定义Bean对象,内部有Bean的类型以及Bean的作用域,其他的也可以自定义。

package cn.spring.com.spring;

/**
 * Bean定义类
 */
public class BeanDefinition {

    /**
     * Bean的类型
     */
    private Class aClass;

    /**
     * Bean的作用域
     */
    private String scope;

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

    public BeanDefinition() {}

    public Class getaClass() {return aClass;}

    public String getScope() {return scope;}

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

    public void setaClass(Class<?> clazz) {this.aClass = clazz;}
}

所以现在我们需要再类上有@Component注解的基础上先通过@Component注解拿到beanName类名字,随后去判断类上是否有@Scope注解,一但有@Scope注解则代表该类是原型Bean,之后使用BeanDefinition定义类的类构造方法将BeanName与Scope传递。而为了方便以后再任意方法内可轻易的拿到Bean的属性我们可以定义一个 beanDefinitionMap 对象池:

// BeanDefinition对象池
// map存储的都是BeanDefinition对象 =》<bean名字,bean定义对象>
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

随后将BeanDefinition存到 beanDefinitionMap 对象池内,下面是实现代码:

/**
 * 在容器内通过配置类扫描Bean
 * @param configClass 配置类
 */
private void scan(Class configClass) {
    // 解析配置类
    // 解析@ComponentScan注解 --》扫描路径 --》扫描包下所有的类
    ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
    String path = componentScan.value(); // 扫描路径
    path = path.replace(".", "/");

    // 扫描(使用类加载器ClassLoader)
    ClassLoader classLoader = ElevenApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(path);
    // 转换成目录
    File file = new File(resource.getFile());
    // file是否为目录
    if (file.isDirectory()) {
        File[] files = file.listFiles();
        for (File f : files) {
            System.out.println(f.getAbsolutePath());
            if (f.getAbsolutePath().endsWith(".class")) {
                // 转换可用的路径格式
                // F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service\UserService.class
                // cn.spring.com.eleven.service.UserService
                String className = f.getAbsolutePath().replace("\\", "/");
                className = className.substring(className.indexOf("cn"), className.indexOf(".class"));  // 截取类名
                className = className.replace("/", ".");  // 替换成点号
                System.out.println(className);
                // 加载类
                try {
                    Class<?> clazz = classLoader.loadClass(className);
                    // 判断是否有@Component注解
                    if (clazz.isAnnotationPresent(Component.class)) {
                        System.out.println("有@Component注解,表示当前类是一个Bean对象");

                        // 是单例bean还是原型bean
                        // 解析类,-》BeanDefinition对象

                        // 首先通过Component拿到Bean的名字
                        Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();

                        // 创建BeanDefinition对象
                        BeanDefinition beanDefinition = new BeanDefinition();
                        // 设置BeanDefinition的类型
                        beanDefinition.setaClass(clazz);
                        // 是否有Scope注解
                        if(clazz.isAnnotationPresent(Scope.class)){
                            // 然后通过Scope注解拿到Bean的作用域
                            Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                            String scope = scopeAnnotation.value(); // 拿到Scope注解的值
                            beanDefinition.setScope(scope); // 设置BeanDefinition的作用域
                        } else {
                            beanDefinition.setScope("singleton"); // 默认是单例
                        }

                        // 存到BeanDefinition对象池
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (InstantiationException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

createBean()方法


在上面,我们已经在容器扫描指定路径下的文件,并将需要创建Bean对象的类名 + Bean作用域封装成BeanDefinition定义类存入 beanDefinitionMap 对象池,那么接下来我们需要通过遍历 beanDefinitionMap 对象池来拿出单例bean,并且通过 createBean(beanName,beanDefinition) 方法来拿到创建完成的Bean对象,随后存入 singletonObjects 单例池中:

/**
 * 构造Spring容器(需要传递配置类)
 * @param configClass 配置类
 */
public ElevenApplicationContext(Class configClass) {
    this.configClass = configClass;

    // 解析配置类
    // 解析@ComponentScan注解 --》扫描路径 --》扫描包下所有的类 -> BeanDefination -> BeanDefinationMap
    scan(configClass);

    // 在BeanDefinitionMap中拿出单例Bean
    for (String beanName : beanDefinitionMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 找出单例Bean
        if ("singleton".equals(beanDefinition.getScope())){
            // 单例Bean
            Object o = createBean(beanName,beanDefinition);
            singletonObjects.put(beanName, o);
        }
    }
}

所以接下来我们需要创建createBean()方法来创建Bean对象并返回:

我们现在有BeanName以及BeanDefinition,所以先通过 beanDefinition.getaClass() 方法获取        需要创建的Bean类型,随后通过 aClass.getDeclaredConstructor().newInstance() 无参构造方法反射得到实例对象,最后返回该对象:

/**
 * 创建Bean对象
 * @param beanName Bean的名称
 * @param beanDefinition Bean的定义对象
 * @return Bean对象
 */
private Object createBean(String beanName,BeanDefinition beanDefinition) {
    // 获取需要创建的Bean类型
    Class aClass = beanDefinition.getaClass();
    // 通过无参构造方法反射得到实例对象
    Object instance = aClass.getDeclaredConstructor().newInstance();
    // 返回实例对象
    return instance;
}

getBean()方法


在获取Bean对象时,我们需先去判断在BeanDefinition对象池里是否有Bean对象?如果有就可以通过BeanName来拿到该Bean的BeanDefinition定义类,随后根据定义类内部的scope属性判断该Bean是单例还是原型,如果是单例可去单例池获取,如果是原型Bean则需要创建新的对象并返回:

/**
 * 获取Bean对象
 * @param beanName Bean的名字
 * @return Bean对象
 */
public Object getBean(String beanName) {
    // 在BeanDefinition对象池里是否有Bean对象
    if(beanDefinitionMap.containsKey(beanName)){
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 判断是单例Bean还是原型Bean
        if("singleton".equals(beanDefinition.getScope())){
            // 单例Bean,在单例池获取
            return singletonObjects.get(beanName);
        } else {
            // 原型Bean,每次都创建新的对象
            return createBean(beanName,beanDefinition);
        }
    } else {
        throw new NullPointerException("Bean不存在");
    }
}

依赖注入(DI)


依赖:一个类使用到另一个类的功能。例如我们将要实现 UserService 依赖 OrderService。

注入:不需要自己 new,而是由 Spring 容器自动把依赖对象赋给我们

➡️ 简单说:以前我们需要自己去 new 一个 OrderS二vice 对象,这样做代码强耦合。但是现在 Spring 容器帮助我们创建 OrderS二vice 并注入到 UserService ,以此来达到解耦的效果。

控制反转(IoC)的核心思想:如果在 Bean 内部手动 new 一个对象,那这个对象就不在 Spring 的 IoC 容器中管理了。

在Spring中,一般是通过@Autowired注解来实现的,所以我们在UserService来依赖注入一个OrderService:

package cn.spring.com.eleven.service;

import cn.spring.com.spring.*;

@Component("userService")
@Scope("prototype")
public class UserService {

    @Autowired
    private OrderService orderService;   
}

因为我们需要再UserService依赖注入一个OrderService,所以需要在createBean()方法内加入DI:

我们可以通过使用 aClass.getDeclaredFields() 方法获取一个 Field 数组,其内部包含当前类中声明的所有字段(成员变量),之后需要遍历字段上是否有@Autowired注解,如果有那么就可以通过属性名字找Bean对象,随后setAccessible(true) 来关闭 Java 的访问检查机制,允许通过反射来操作 private/protected/default 修饰的字段,最后使用 field.set(instance, bean) 方法把容器里准备好的 bean 塞进 instance 对象的某个字段里:

/**
 * 创建Bean对象
 * @param beanName Bean的名称
 * @param beanDefinition Bean的定义对象
 * @return Bean对象
 */
private Object createBean(String beanName, BeanDefinition beanDefinition) {
    // 1. 获取需要创建的Bean类型
    Class aClass = beanDefinition.getaClass();
    try {
        // 2. 通过无参构造方法反射得到实例对象
        Object instance = aClass.getDeclaredConstructor().newInstance();

        // ===================== 依赖注入开始 =====================
        // 3. 获取 Bean 内所有的属性(成员变量)
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            // 4. 判断属性上是否有 @Autowired 注解
            if (field.isAnnotationPresent(Autowired.class)) {
                // 5. 根据属性名字来找容器中的 Bean 对象
                Object bean = getBean(field.getName());
                if (bean == null) {
                    throw new RuntimeException("依赖注入失败,没有找到对应的Bean对象");
                }

                // 6. 打破封装,让私有属性可以被赋值
                field.setAccessible(true);

                // 7. 把依赖对象注入到当前正在创建的 Bean
                // instance -> 当前 Bean(例如 UserService)
                // bean     -> 依赖对象(例如 UserRepository)
                field.set(instance, bean);
            }
        }
        // ===================== 依赖注入结束 =====================

        return instance;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

BeanNameAware接口


BeanNameAware 是 Spring 容器中 Aware 系列接口 之一,用来让 Bean 感知到自己在容器中的名字

  • 获取 Bean 的名字:有些时候,一个类可能被容器加载多次(比如配置了多个别名),实现这个接口后,它就能知道自己被容器叫做啥。

  • 区分不同的 Bean 实例:当同一个类被注册成多个 Bean 时,可以通过名字来区别。

  • 调试/日志:在日志打印时,能更直观地输出 Bean 的名字,方便定位问题。

  • 框架扩展:在某些自定义框架或工具中,可以根据 Bean 名称实现一些动态逻辑。

package cn.spring.com.spring;


/**
 * 实现该接口的Bean可以感知自己的BeanName
 * @Author: Eleven
 */
public interface BeanNameAware {

    void setBeanName(String beanName);
}

当一个 Bean 实现了这个接口,Spring 容器在实例化并注入依赖完成后,会调用它的 setBeanName() 方法,把当前 Bean 在容器中的 name 传进去。所以接下来我们就要实现这个逻辑。

我们先让UserService去引入该接口:

package cn.spring.com.eleven.service;


import cn.spring.com.spring.*;

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

    @Autowired
    private OrderService orderService;

    private String beanName;

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

随后在createBean()方法中去调用:

首先去判断当前的Bean对象是不是实现了BeanNameAware接口,如果是的话就将该Bean对象强制转型为 BeanNameAware 类型,之后调用内部的setBeanName()方法,把这个 Bean 在容器里的名字注入进去。

👉 这是典型的 Aware 回调机制
容器检查 -> 确认实现接口 -> 调用接口方法 -> 注入容器上下文信息。

/**
 * 创建Bean对象
 * @param beanName Bean的名称
 * @param beanDefinition Bean的定义对象
 * @return Bean对象
 */
private Object createBean(String beanName,BeanDefinition beanDefinition) {
    // 获取需要创建的Bean类型
    Class aClass = beanDefinition.getaClass();
    try {
        // 通过无参构造方法反射得到实例对象
        Object instance = aClass.getDeclaredConstructor().newInstance();

        // 依赖注入DI
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            // 2. 判断属性上是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                // 根据属性名字来找Bean对象
                Object bean = getBean(field.getName());
                if (bean == null){
                    throw new RuntimeException("依赖注入失败,没有找到对应的Bean对象");
                }
                // 依赖注入
                // 设置属性为可访问
                field.setAccessible(true);
                // 设置属性值
                // instance 是当前正在创建的 Bean 对象
                // bean 是从容器中拿到的依赖对象
                field.set(instance, bean);
            }
        }

        // 判断是否实现了BeanNameAware接口(感知自己的BeanName)
        if (instance instanceof BeanNameAware){
            // 强转并调用setBeanName方法拿到自己的BeanName
           ((BeanNameAware) instance).setBeanName(beanName);
        }
        // 返回实例对象
        return instance;
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

InitializingBean接口


InitializingBean 接口是 Spring 里面 Bean 生命周期 的一个关键接口,跟我们前面说的 BeanNameAware 接口类似,它也是一种 回调接口,不过用途不同。

package cn.spring.com.spring;

/**
 * 实现该接口的Bean可以在Bean初始化后调用afterPropertiesSet方法
 * @Author: Eleven
 */
public interface InitializingBean {
    public void afterPropertiesSet() throws Exception;
}

调用 afterPropertiesSet() 方法作用是当 Bean 的所有属性都注入完成之后,Spring 容器会回调这个方法,让 Bean 有机会执行一些“初始化逻辑”,适合做那些 依赖注入完成后,必须再进行一次额外配置的场景

我们在UserSerivce内引入该接口:

package cn.spring.com.eleven.service;


import cn.spring.com.spring.*;

@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware, InitializingBean {

    @Autowired
    private OrderService orderService;

    private String beanName;

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

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

跟上面的 BeanNameAware 接口实现类似,同样先去判断当前的Bean对象是不是实现了InitializingBean 接口,如果是的话就将该Bean对象强制转型为 InitializingBean 类型,之后调用内部的 afterPropertiesSet() 方法。

/**
 * 创建Bean对象
 * @param beanName Bean的名称
 * @param beanDefinition Bean的定义对象
 * @return Bean对象
 */
private Object createBean(String beanName,BeanDefinition beanDefinition) {
    // 获取需要创建的Bean类型
    Class aClass = beanDefinition.getaClass();
    try {
        // 通过无参构造方法反射得到实例对象
        Object instance = aClass.getDeclaredConstructor().newInstance();

        // 依赖注入DI
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            // 2. 判断属性上是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                // 根据属性名字来找Bean对象
                Object bean = getBean(field.getName());
                if (bean == null){
                    throw new RuntimeException("依赖注入失败,没有找到对应的Bean对象");
                }
                // 依赖注入
                // 设置属性为可访问
                field.setAccessible(true);
                // 设置属性值
                // instance 是当前正在创建的 Bean 对象
                // bean 是从容器中拿到的依赖对象
                field.set(instance, bean);
            }
        }

        // 判断是否实现了BeanNameAware接口(感知自己的BeanName)
        if (instance instanceof BeanNameAware){
            // 强转并调用setBeanName方法拿到自己的BeanName
           ((BeanNameAware) instance).setBeanName(beanName);
        }

        // 判断是否实现了InitializingBean接口(初始化)
        if (instance instanceof InitializingBean){
            // 强转并调用afterPropertiesSet方法(初始化)
            ((InitializingBean) instance).afterPropertiesSet();
        }

        // 返回实例对象
        return instance;
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

BeanPostProcessor接口 + AOP的实现


BeanPostProcessor 是 Spring 框架里最核心、最常考的扩展接口之一。它的作用就是:在 Bean 初始化前后,做一些“增强”或“加工”

package cn.spring.com.spring;

/*
 * 定义一个Bean的后置处理器
 */
public interface BeanPostProcessor {

    /*
     * 初始化之前调用
     */
    Object postProcessBeforeInitialization(Object bean, String beanName);

    /*
     * 初始化之后调用
     */
    Object postProcessAfterInitialization(Object bean, String beanName);
}

Spring 给了开发者一个“切入点”,让我们能在 Bean 初始化的前后,统一做一些额外处理。

常见用途:

  1. 修改 Bean 属性(比如给某些字段设置默认值)

  2. 包装 Bean(比如 AOP 代理就是在这一步完成的)

  3. 检查或替换 Bean(如果发现不合规,可以替换成另一个对象)

  4. 实现通用逻辑(比如自动注入日志、监控、事务代理等)

假设我们想要再UserService中设置一个name属性,然后想要再Bean初始化前给name赋值:

package cn.spring.com.eleven.service;


import cn.spring.com.spring.*;

@Component("userService")
@Scope("prototype")
public class UserService UserInterface {

    @Autowired
    private OrderService orderService;

    private String beanName;

    private String name;

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

    public void setName(String newUserService) {
        this.name = newUserService;
    }

}

那我们就可以使用创建新的类去继承这个 BeanPostProcessor 接口:

package cn.spring.com.eleven.service;

import cn.spring.com.spring.BeanPostProcessor;
import cn.spring.com.spring.Component;
import cn.spring.com.spring.ElevenApplicationContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Component("ElevenBeanPostProcessor")
public class ElevenBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("初始化之前调用");
        if(beanName.equals("userService")){
            ((UserService)bean).setName("eleven");
        }
        return bean;
    }

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

之后我们在扫描类的过程就需要去判断这个类加了@Component并且还引入了 BeanPostProcessor,之后在外面自定义一个BeanPostProcessor对象池存入,方便后序直接查找:

// BeanPostProcessor对象池
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

// 判断clazz这个类是否实现了BeanPostProcessor接口
if (BeanPostProcessor.class.isAssignableFrom(clazz)){
    BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
    // 把BeanPostProcessor对象添加到BeanPostProcessor对象池
    beanPostProcessorList.add(beanPostProcessor);
}

之后在createBean()方法内去遍历BeanPostProcessor对象池找到当前自定义的两个方法去调用:

/**
 * 创建Bean对象
 * @param beanName Bean的名称
 * @param beanDefinition Bean的定义对象
 * @return Bean对象
 */
private Object createBean(String beanName,BeanDefinition beanDefinition) {
    // 获取需要创建的Bean类型
    Class aClass = beanDefinition.getaClass();
    try {
        // 通过无参构造方法反射得到实例对象
        Object instance = aClass.getDeclaredConstructor().newInstance();

        // 依赖注入DI
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            // 2. 判断属性上是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                // 根据属性名字来找Bean对象
                Object bean = getBean(field.getName());
                if (bean == null){
                    throw new RuntimeException("依赖注入失败,没有找到对应的Bean对象");
                }
                // 依赖注入
                // 设置属性为可访问
                field.setAccessible(true);
                // 设置属性值
                // instance 是当前正在创建的 Bean 对象
                // bean 是从容器中拿到的依赖对象
                field.set(instance, bean);
            }
        }

        // 判断是否实现了BeanNameAware接口(感知自己的BeanName)
        if (instance instanceof BeanNameAware){
            // 强转并调用setBeanName方法拿到自己的BeanName
           ((BeanNameAware) instance).setBeanName(beanName);
        }

        // 调用BeanPostProcessor的postProcessBeforeInitialization方法
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }

        // 判断是否实现了InitializingBean接口(初始化)
        if (instance instanceof InitializingBean){
            // 强转并调用afterPropertiesSet方法(初始化)
            ((InitializingBean) instance).afterPropertiesSet();
        }

        // 调用BeanPostProcessor的postProcessAfterInitialization方法
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }

        // 返回实例对象
        return instance;
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

接下来就需要实现AOP,AOP一般都是实现在初始化后,所以我们要改写postProcessAfterInitialization()方法:

package cn.spring.com.eleven.service;

import cn.spring.com.spring.BeanPostProcessor;
import cn.spring.com.spring.Component;
import cn.spring.com.spring.ElevenApplicationContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Component("ElevenBeanPostProcessor")
public class ElevenBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("初始化之前调用");
        return bean;
    }

    // 在 Bean 创建完成后,换掉原来的 Bean 对象,返回一个代理对象。
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("初始化之后调用");
        if(beanName.equals("userService")){
            // JDK的动态代理
            Object proxyInstance = Proxy.newProxyInstance(ElevenBeanPostProcessor.class.getClassLoader()
                    , bean.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("JDK的动态代理");
                            return method.invoke(bean, args);
                        }
                    });
            return proxyInstance;
        }
        return bean;
    }
}