1.引言
Spring是一个开源的应用程序框架,主要在于简化企业级应用的开发。Spring Boot是Spring的一个子项目,它通过自动配置、起步依赖(starter dependencies)特性,进一步简化了Spring应用的开发和部署。在Spring应用中,Bean的生命周期管理至关重要。通过深入理解并利用各种扩展点,可以在Bean的不同生命周期阶段进行自定义的处理,从而实现更灵活、更强大的功能。
2.Spring Bean生命周期
源码解析见上一篇文章springboot bean生命周期和作用域
- BeanDefinition 注册
发生在bean实例化之前,Spring通过注解扫描或显式配置类发现需要管理的bean,并将被发现的bean生成一个BeanDefinition对象,将其注册到BeanDefinitionRegistry中,这个阶段可以通过可以通过实现ImportBeanDefinitionRegistrar接口来自定义,从而实现动态地向容器中添加新的BeanDefinition,源码详见springboot启动时自动装配过程 章节 2.源码解析 - BeanFactoryPostProcessor 处理
在所有BeanDefinition注册完成后,在bean实例化之前,Spring会调用所有实现了BeanFactoryPostProcessor接口的bean。这里可以在bean定义被加载到容器中后但在bean实例化之前对其进行修改 - 实例化
通过反射创建bean实例。在此阶段,可以使用FactoryBean来创建复杂对象,源码详见springboot bean生命周期和作用域。 - 属性填充
设置bean的属性值(依赖注入)。可以使用BeanFactoryPostProcessor进行属性值处理。 - 初始化前处理
执行BeanPostProcessor的postProcessBeforeInitialization方法。可以在此阶段对bean进行预处理。 - 初始化
执行自定义的初始化方法(可以通过**@PostConstruct注解的方法、InitializingBean接口的afterPropertiesSet方法**、自定义的初始化方法)。SmartLifecycle接口提供了启动顺序控制。 - 初始化后处理
执行BeanPostProcessor的postProcessAfterInitialization方法。可以在此阶段对bean进行后处理。 - 使用
这里可以通过依赖注入(Dependency Injection, DI)“@Autowired” 或者 直接从应用上下文中获取Bean(通过ApplicationContext.getBean()方法 获取bean。 - 销毁前处理
执行DisposableBean接口的destroy方法或其他自定义的销毁方法(如@PreDestroy注解的方法)。
SmartLifecycle接口提供了关闭顺序控制。 - 销毁
bean被容器销毁并释放资源
3.bean生命周期演示
示例来自bean的生命周期。
- 定义bean
//bean1
@Component
public class MyDependency {
public MyDependency() {
System.out.println("MyDependency created");
}
}
//bean2
@Component
public class MyBean implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware {
private MyDependency myDependency;
private String someProperty = "TEST";
private String beanName;
private BeanFactory beanFactory;
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
@Autowired
public MyBean(MyDependency myDependency) {
this.myDependency = myDependency;
System.out.println("属性填充: " + myDependency);
}
@PostConstruct
public void init() {
System.out.println("PostConstruct 自定义初始化方法调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean.afterPropertiesSet() 调用");
}
//DisposableBean接口:如果实现了DisposableBean接口,则会调用其destroy()方法
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean.destroy() 调用");
}
//BeanNameAware:设置Bean名称。
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("BeanNameAware Bean名称设置为: " + beanName);
}
//BeanFactoryAware:提供对创建该Bean的BeanFactory的引用
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactoryAware BeanFactory设置为" );
}
}
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyBean) {
System.out.println("MyBeanPostProcessor#postProcessBeforeInitialization在初始化之前执行的逻辑");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyBean) {
System.out.println("MyBeanPostProcessor#postProcessAfterInitialization在初始化之后执行的逻辑");
}
return bean;
}
}
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取某个Bean的定义
if (beanFactory.containsBeanDefinition("myBean")) {
// 修改Bean定义的某个属性
beanFactory.getBeanDefinition("myBean").getPropertyValues().add("someProperty", "modifiedValue");
System.out.println("MyBeanFactoryPostProcessor: 在Bean实例化之前修改了myBean的someProperty属性");
}
}
}
4.最佳实践
4.1.创建Bean
在springboot中,如何创建一个bean呢?很多同学大概都知道吧。就是在上一篇文章中写到的如下:
- @Controller通常用于标识Web控制器类;
- @Service用于业务逻辑层;
- @Repository用于数据访问层。
上面三个注解都包含@Component注解,这意味着它们都可以被组件扫描自动检测并注册为Spring容器中的Bean。 - @Component:这是一个通用的注解,用于标记任何Spring管理的组件。允许Spring通过组件扫描机制自动发现并注册到应用上下文中。
- @Configuration:这个注解用于定义一个配置类,其中可以使用@Bean注解的方法来定义Spring管理的bean。配置类本身也是一个Spring bean。
- @Bean:在配置类内部使用,用来显式声明一个或多个方法返回的对象应由Spring容器管理。通常与@Configuration一起使用。
但是假如我们的bean不符合Bean的定义,举个例子,我们的bean没有public构造方法,怎么处理,有些同学说,我可以在@configuration中添加@bean注入,这样也没问题。那假如我们的的bean只是个接口,使用上面的方式可以注入么?,接下来将要写的是,怎么将一个自定义对象,放入springboot。
4.1.1.FactoryBean的简单使用示例
通过工厂方法来创建和管理复杂的对象。FactoryBean 提供了一种灵活的方式来控制对象的创建过程,特别是当对象的构造函数不可访问或需要复杂的初始化逻辑时。上代码直接测试
//测试接口,使用FactoryBean创建
public class DemoClass {
public void say() {
System.out.println("DemoClass say Hello World");
}
}
//测试类,注入DemoClass,并通过构造函数由springboot自动注入DemoClass
@Component
public class DemoJava {
private DemoClass demoClass;
public DemoJava(DemoClass demoClass) {
this.demoClass = demoClass;
System.out.println("create DemoJava");
}
public void say() {
System.out.println("DemoClass say Hello World");
demoClass.say();
}
}
//FactoryBean 测试类,创建DemoClass对象,注意,这里需要配置@Component注解,标注它是一个springboot的Bean
@Component
public class AntFactoryBean implements FactoryBean {
@Override
public DemoClass getObject() throws Exception {
return new DemoClass();
}
@Override
public Class<DemoClass> getObjectType() {
return DemoClass.class;
}
}
//注入Bean 并执行say方法
@Component
public class AntTestRunner implements CommandLineRunner {
@Autowired
private DemoJava demoJava;
@Override
public void run(String... args) throws Exception {
demoJava.say();
}
}
测试结果:
从测试结果可以看出来,是不是DemoClass 已经在springboot容器中了?
- 关键点解释:
getObject() 方法:
FactoryBean 的核心方法,用于返回实际的对象实例。在上面的例子中,我们通过 new 一个对象返回单例实例,当然我们可以通过其他任何方式,返回一个对象。
getObjectType() 方法:
返回由工厂创建的对象类型。这对于Spring容器来说非常重要,因为Spring要知道工厂生成的对象类型以便进行类型检查和依赖注入。
isSingleton() 方法:
指示由工厂创建的对象是否是单例的。如果返回 true,则表示该对象在整个应用程序上下文中只有一个实例;如果返回 false,则每次请求都会创建一个新的实例。
接下来,我们想一下,为什么mybatis可以将对象进行自动注入?很多同学是不是很迷茫?当然对java很熟悉的人是不是会想到一个方式,jdk动态代理。
4.1.2.FactoryBean通过动态代理将接口的对象注册到spring容器
//测试接口
public interface OrderMapper {
void getOrder();
}
public class AntInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("AntMapperProxy invoke-"+method.getName());
return "AntMapperProxy invoke Test……";
}
}
// 使用代理类,返回OrderMapper的对象。
@Component
public class AntProxyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return Proxy.newProxyInstance(OrderMapper.class.getClassLoader(), new Class[]{OrderMapper.class},new AntInvocationHandler());
}
@Override
public Class getObjectType() {
return OrderMapper.class;
}
}
@Component
public class AntTestRunner implements CommandLineRunner {
@Autowired
private DemoJava demoJava;
@Autowired
private OrderMapper orderMapper;
@Override
public void run(String... args) throws Exception {
String order = orderMapper.getOrder();
System.out.println("AntTestRunner run-"+order);
}
}
从上面可以看出,是不是我们的接口也注册到springboot容器,并可以通过 @Autowired注解进行注入,调用方法。
那么,假如我们有很多个接口需要注册到springboot容器怎么处理呢?难道每个接口都写一个FactoryBean 的实现方法?
有没有一种方式可以批量注入,而不是每次都要写一个类?回顾上面的Bean的生命周期,是不是有很多地方可以修改Bean或者对Bean进行处理。但是我们现在要将Bean注册到Springboot容器,很容易想到的ImportBeanDefinitionRegistrar。
4.2.批量复杂的bean添加到容器
4.2.1.ImportBeanDefinitionRegistrar
动态向 Spring 容器启动时动态地注册 Bean 定义,也就是BeanDefinition。接口只有一个实现方法
void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
AnnotationMetadata:提供导入该注册器的类的元数据信息(如注解、类名等)。
BeanDefinitionRegistry:用于注册新的 Bean 定义
- 测试例子:
public class DemoClass {
public void say() {
System.out.println("DemoClass say Hello World");
}
}
//创建一个自定义的 ImportBeanDefinitionRegistrar 实现类,并在其中动态注册 DemoClass
public class AntImportBeanDefinitionResgistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(DemoClass.class);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("demoClass", beanDefinition);
}
}
@Component
public class DemoJava {
private DemoClass demoClass;
public DemoJava(DemoClass demoClass) {
this.demoClass = demoClass;
System.out.println("create DemoJava");
}
public void say() {
System.out.println("DemoJava say Hello World");
demoClass.say();
}
}
//使用 @Import 注解导入 ImportBeanDefinitionRegistrar
@Configuration
@Import({AntImportBeanDefinitionResgistrar.class})
public class AntConfig {
}
@Component
public class AntTestRunner implements CommandLineRunner {
@Autowired
private DemoJava demoJava;
@Autowired
private OrderMapper orderMapper;
@Override
public void run(String... args) throws Exception {
demoJava.say();
}
}
输出:
代码解释:
- BeanDefinitionBuilder:
BeanDefinitionBuilder 是一个辅助类,简化 BeanDefinition 的创建过程。 - genericBeanDefinition(Class<?> beanClass) 方法用于创建一个新的 BeanDefinition
- BeanDefinitionRegistry 提供了注册和移除 BeanDefinition 的方法。
- registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法用于将一个 BeanDefinition 注册到容器中。
- @Import 注解用于导入配置类或实现 ImportBeanDefinitionRegistrar 接口的类。告诉 Spring 容器在启动时调用 AntImportBeanDefinitionResgistrar 的 registerBeanDefinitions 方法。
那么,怎么将接口注册到容器中呢?
4.2.2.将接口注册到容器中
主要思想是,BeanDefinition在生成是需要设置Class类型,因此需要对代理对做修改,修改为Class由构造函数传入。代码如下
//一个接口对象
public interface OrderMapper {
String getOrder();
}
//InvocationHandler 实现
public class AntInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("AntMapperProxy invoke-"+method.getName());
return "AntMapperProxy invoke Test……";
}
}
//这里只是将单个接口OrderMapper注册到容器中,如果有多个,是不是可以通过for循环,依次注入,大家下来实验一下。
public class AntProxyImportBeanDefinitionResgistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(AntProxyFactoryBean.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("orderMapper", beanDefinition);
}
}
@Component
public class AntTestRunner implements CommandLineRunner {
@Autowired
private DemoJava demoJava;
@Autowired
private OrderMapper orderMapper;
@Override
public void run(String... args) throws Exception {
String order = orderMapper.getOrder();
System.out.println("AntTestRunner run-"+order);
// demoJava.say();
}
}
测试结果:
截止到这里,我们已经知道,怎么将接口注册到springboot 的容器中。看到这里大家想到了什么呢?是不是想到了mybatis的实现方式?接下来咱们模拟实现一个mybatis。
4.3.模拟实现mybatis将接口注册springboot Bean容器中。
整个代码逻辑为,创建一个ImportBeanDefinitionRegistrar,在ImportBeanDefinitionRegistrar类中,需要有以下步骤
- 获取Mapper类
- 定义BeanDefinition
- 注册bean
4.3.1.定义FactoryBean
- 从上面我们知道,将接口注册到springboot中,可以使用jdk动态代理+FactoryBean实现,为了实现查询功能,我们需要将DataSource 传入,
public class AntMapperFactoryBean<T> implements FactoryBean {
private Class<T> clazz;
private DataSource dataSource;
public AntMapperFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new AntMapperInvocationHandler(this.clazz, this.dataSource));
}
@Override
public Class<?> getObjectType() {
return this.clazz;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
4.3.2.定义动态代理类,
主要逻辑是通过@Sql获取注解上的sql,并替换替换方法参数。映射成方法返回
public class AntMapperInvocationHandler<T> implements InvocationHandler {
private Class<T> clazz;
private DataSource dataSource;
public AntMapperInvocationHandler(Class<T> clazz, DataSource dataSource) {
this.clazz = clazz;
this.dataSource = dataSource;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
// 假设方法上有 @Sql 注解
Sql sqlAnnotation = method.getAnnotation(Sql.class);
if (sqlAnnotation != null) {
String sql = sqlAnnotation.value();
Parameter[] parameters = method.getParameters();
sql = replaceParameters(sql, parameters, args);
try (Connection connection = DataSourceUtils.getConnection(dataSource);
PreparedStatement ps = connection.prepareStatement(sql)) {
if (args != null) {
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
}
ResultSet rs = ps.executeQuery();
List<Map<String, Object>> results = new ArrayList<>();
while (rs.next()) {
Map<String, Object> row = mapRowToMap(rs);
results.add(row);
}
return results;
} catch (SQLException e) {
throw new RuntimeException("Error executing SQL", e);
}
}
return null;
}
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
private String replaceParameters(String sql, Parameter[] parameters, Object[] args) {
if (parameters == null || args == null || parameters.length != args.length) {
return sql;
}
for (int i = 0; i < parameters.length; i++) {
String placeholder = "#{" + parameters[i].getName() + "}";
sql = sql.replace(placeholder, "?");
}
return sql;
}
private Map<String, Object> mapRowToMap(ResultSet rs) throws SQLException {
Map<String, Object> row = new HashMap<>();
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i).toLowerCase();
Object value = rs.getObject(i);
row.put(columnName, value);
}
return row;
}
}
4.3.3.定义ImportBeanDefinitionRegistrar,
主要逻辑,通过读取注解AntMapperScan,获取扫描包,使用Spring的ClassPathScanningCandidateComponentProvider包扫描接口(注意,ClassPathScanningCandidateComponentProvider包中需要自定义筛选逻辑,并添加包含过滤器,例子中只扫描带有 @AntMapper 注解的类。),处理完成后scanner.findCandidateComponents(basePackage)生成BeanDefinition的集合。这里代码是还可以优化的,大家自己优化一下。
public class AmtMapperImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取注解的属性值
Map<String, Object> basePackages = importingClassMetadata.getAnnotationAttributes(AntMapperScan.class.getName(), true);
String[] packagesToScan;
if (basePackages == null || !basePackages.containsKey("basePackages") || ((String[]) basePackages.get("basePackages")).length == 0) {
// 设置默认值为配置类所在的包及其子包
String className = importingClassMetadata.getClassName();
packagesToScan = new String[]{className.substring(0, className.lastIndexOf('.'))};
} else {
packagesToScan = (String[]) basePackages.get("basePackages");
}
Set<Class<?>> classes = findMapperInterfaces(packagesToScan);
for (Class<?> clazz : classes) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
// 这里可以通过设置必要属性,如从注解中获取的属性值
builder.setLazyInit(true);
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// 添加具体的属性值
builder.addPropertyReference("dataSource", "dataSource"); // 引用Spring容器中的dataSource Bean
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setBeanClass(AntMapperFactoryBean.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
registry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);
}
}
private Set<Class<?>> findMapperInterfaces(String[] basePackages) {
// 创建自定义的 ClassPathScanningCandidateComponentProvider
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 自定义筛选逻辑:只选择接口且带有 @AntMapper 注解的类
return beanDefinition.getMetadata().isInterface() &&
beanDefinition.getMetadata().hasAnnotation(AntMapper.class.getName());
}
};
// 添加包含过滤器,指定只扫描带有 @AntMapper 注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(AntMapper.class));
Set<Class<?>> candidates = new HashSet<>();
for (String basePackage : basePackages) {
System.out.println("Scanning package: " + basePackage);
Set<BeanDefinition> components = scanner.findCandidateComponents(basePackage);
if (components.isEmpty()) {
System.out.println("No candidate components found in package: " + basePackage);
} else {
for (BeanDefinition reader : components) {
try {
candidates.add(Class.forName(reader.getBeanClassName()));
System.out.println("Found candidate component: " + reader.getBeanClassName());
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + reader.getBeanClassName());
throw new RuntimeException(e);
}
}
}
}
return candidates;
}
}
4.3.4. 接下来就是定义一些注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AntMapper {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface AntMapperScan {
String[] basePackages() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Sql {
String value() default "";
}
// 配置扫描包和Import加载
@Configuration
@Import(AmtMapperImportBeanDefinitionRegistrar.class)
@AntMapperScan(basePackages = "com.ant.spring.antbaties.mapper")
public class AntMapperConfiguration {
}
4.3.5.定义测试类
@AntMapper
public interface OrderMapper {
@Sql("select * from orders where id = #{id}")
List<Map> getOrder(int id);
}
@AntMapper
public interface UserMapper<T> {
@Sql("select * from employees where name = #{name}")
List<Map> getUser(String name);
@Sql("select * from employees")
List<Map> getUser();
}
@Component
public class AntTestRunner implements CommandLineRunner {
@Autowired
private UserMapper<List> userMapper;
@Override
public void run(String... args) throws Exception {
List<Map> user = userMapper.getUser("小明");
System.out.println("user-"+user);
List<Map> allUser = userMapper.getUser();
System.out.println("allUser-"+allUser);
}
}
5.总结
该 文章基于Spring Boot 扩展开发的关键点,详细描述了 Spring Bean 生命周期及其核心扩展机制。通过解析 Bean 生命周期的各个阶段,结合实际案例展示如何利用 BeanPostProcessor、BeanFactoryPostProcessor 和 ApplicationListener 等扩展点进行灵活的定制和增强功能,同时提供了最佳实践,希望能够在项目中能够用到,该文章主要适用于希望深入了解和应用 Spring Boot 扩展机制进行开发的人