[spring6: @EnableSpringConfigured]-编译时织入
推荐阅读:[spring6: @EnableLoadTimeWeaving]-运行时织入
源码
EnableSpringConfigured
@EnableSpringConfigured
用于启用 Spring 对通过 new
创建的、标注了 @Configurable
的非 Spring 管理对象的依赖注入支持(基于 AspectJ 织入)。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}
1. SpringConfiguredConfiguration
SpringConfiguredConfiguration
注册了一个 AnnotationBeanConfigurerAspect
切面 Bean,使 Spring 能对使用 @Configurable
标注的非 Spring 管理对象进行依赖注入。
@Configuration
public class SpringConfiguredConfiguration {public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME ="org.springframework.context.config.internalBeanConfigurerAspect";@Bean(name = BEAN_CONFIGURER_ASPECT_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public AnnotationBeanConfigurerAspect beanConfigurerAspect() {return AnnotationBeanConfigurerAspect.aspectOf();}}
2. AnnotationBeanConfigurerAspect
AnnotationBeanConfigurerAspect
是一个基于 @Configurable
注解的 AspectJ 切面,用于在非 Spring 管理对象上执行自动依赖注入。
public aspect AnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect implements BeanFactoryAware, InitializingBean, DisposableBean {// `BeanConfigurerSupport` 是一个支持使用 `BeanFactory` 和 `BeanWiringInfoResolver` 为任意对象执行依赖注入的基类,通常被 AspectJ 切面类继承,用于在对象创建后注入 Spring 容器中的依赖。private final BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport();// 设置注入解析器为注解驱动,同时保存 BeanFactory 供后续注入使用@Overridepublic void setBeanFactory(BeanFactory beanFactory) {// `AnnotationBeanWiringInfoResolver` 是一个根据 `@Configurable` 注解生成依赖注入信息的解析器this.beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());this.beanConfigurerSupport.setBeanFactory(beanFactory);}// 初始化内部支持类,准备注入工作@Overridepublic void afterPropertiesSet() {this.beanConfigurerSupport.afterPropertiesSet();}// 委托 beanConfigurerSupport 对非 Spring 管理对象执行依赖注入@Overridepublic void configureBean(Object bean) {this.beanConfigurerSupport.configureBean(bean);}// 销毁资源,清理注入支持对象@Overridepublic void destroy() {this.beanConfigurerSupport.destroy();}// 匹配所有运行时对象上被 @Configurable 注解标记的实例,以便对这些非 Spring 管理的对象执行依赖注入。public pointcut inConfigurableBean() : @this(Configurable);// 暴露一个通用切点 preConstructionConfiguration(),它会匹配内部的 preConstructionConfigurationSupport(*)public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*);// 匹配运行时被 @Configurable 注解的对象,并绑定注解对象 c// 运行时判断:只有当 @Configurable(preConstruction = true) 时,才匹配这个切点private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if (c.preConstruction());// public interface ConfigurableObject {}// 让所有被 @Configurable 注解的类自动实现 ConfigurableObject 接口,以统一切点匹配方式@CodeGenerationHint(ifNameSuffix="bb0")declare parents: @Configurable * implements ConfigurableObject;
}
Configurable
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Configurable {/*** The name of the bean definition that serves as the configuration template.*/String value() default "";// 是否启用自动注入。推荐使用 NO,由 Aspect 决定注入方式Autowire autowire() default Autowire.NO;/*** Is dependency checking to be performed for configured objects?*/boolean dependencyCheck() default false;// 是否在构造方法之前就注入依赖,必须配置 AspectJ LTW + constructor joinpoint 才生效boolean preConstruction() default false;}
案例
配置
<?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><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>xyz.idoly</groupId><artifactId>ctw-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>ctw-demo</name><properties><java.version>24</java.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies><build><plugins><plugin><groupId>dev.aspectj</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.14.1</version><dependencies><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>1.9.24</version></dependency></dependencies><configuration><complianceLevel>24</complianceLevel><aspectLibraries><aspectLibrary><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></aspectLibrary></aspectLibraries></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
代码
@Component // 将 A 注册为 Spring Bean
public class A {public String a() {return "A.a() called!";}
}
@Configurable // 标记此类型,使其在通过 new 关键字创建时也能被 Spring 注入依赖
public class B {@Resource // 希望 Spring 注入 A 的实例private A a;public B() {System.out.println("B instance created (constructor called).");}public void b2a() {if (a != null) {System.out.println("B.b2a() successfully called A: " + a.a());} else {System.out.println("B.b2a(): A was NOT injected!");}}
}
@EnableSpringConfigured
@SpringBootApplication
public class Application {@Beanpublic CommandLineRunner commandLineRunner(A a) {return args -> new B().b2a();}public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
结果
PS D:\workspace\java\ctw-demo> d:; cd 'd:\workspace\java\ctw-demo'; & 'D:\plugins\jdk\bin\java.exe' '@C:\Users\idoly\AppData\Local\Temp\cp_5i5g1kftfuzrrfyldazo9jwpu.argfile' 'xyz.idoly.demo.Application' . ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v3.5.3)2025-07-13T12:04:19.640+08:00 INFO 5988 --- [ctw-demo] [ main] xyz.idoly.demo.Application : Starting Application using Java 24.0.1 with PID 5988 (D:\workspace\java\ctw-demo\target\classes started by idoly in D:\workspace\java\ctw-demo)
2025-07-13T12:04:19.643+08:00 INFO 5988 --- [ctw-demo] [ main] xyz.idoly.demo.Application : No active profile set, falling back to 1 default profile: "default"
2025-07-13T12:04:20.153+08:00 INFO 5988 --- [ctw-demo] [ main] xyz.idoly.demo.Application : Started Application in 0.848 seconds (process running for 1.077)
B instance created (constructor called).
B.b2a() successfully called A: A.a() called!