spring常用组件扩展点
# 常见用法
# 1. 使用@Configuration
类配合@Bean
通过在配置类中使用@Bean
方法,可以在容器启动时执行特定逻辑。这种方式简单直接,适用于大多数初始化逻辑。
@Configuration
public class JobConfig {
@Bean
public MyJobRegistrar myJobRegistrar(ListableBeanFactory beanFactory) {
MyJobRegistrar registrar = new MyJobRegistrar(beanFactory);
serviceMethod(); // 自定义方法,用于处理注解和注册任务
return registrar;
}
}
2
3
4
5
6
7
8
9
10
# 2. 实现ApplicationListener<ContextRefreshedEvent>
通过监听ContextRefreshedEvent
,你可以在Spring容器加载完毕后执行自定义逻辑。这在确保所有的bean都已经创建完成后执行初始化代码时非常有用。
@Component
public class JobRegistrationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
// 执行逻辑
}
private void registerJobs(ApplicationContext context) {
// 逻辑处理
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 3. 实现CommandLineRunner
或 ApplicationRunner
这两个接口都是在Spring应用程序启动后执行特定代码的方式。CommandLineRunner
和ApplicationRunner
的不同之处在于接受的参数类型不同。
@Component
public class JobStartupRunner implements CommandLineRunner {
@Autowired
private ApplicationContext context;
@Override
public void run(String... args) throws Exception {
// 执行任务注册逻辑
serviceMethod(context);
}
private void registerJobs(ApplicationContext context) {
// 逻辑处理
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4. 使用BeanPostProcessor
如果你需要在bean的初始化阶段介入,BeanPostProcessor
提供了在bean初始化前后执行操作的能力。这可以用来检查是否有特定的注解并据此做一些操作。
@Component
public class JobAnnotationProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 检查bean上的注解
checkForJobAnnotation(bean, beanName);
return bean;
}
private void checkForJobAnnotation(Object bean, String beanName) {
// 注册逻辑
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5. 使用@EventListener
如果你更喜欢使用注解的方式监听应用事件,@EventListener
提供了一种简单的方法来响应不同的事件。
@Component
public class JobEventProcessor {
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
// 执行任务注册逻辑
ApplicationContext context = event.getApplicationContext();
serviceMethod(context);
}
private void registerJobs(ApplicationContext context) {
// 逻辑处理
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
每种方法都有其用武之地,具体使用哪种取决于你的具体需求和偏好。考虑到你的需求是在容器启动阶段注册任务,使用ApplicationListener<ContextRefreshedEvent>
或CommandLineRunner
可能是最直接的方法。
# 高级用法
例如使用 ImportBeanDefinitionRegistrar
和 BeanDefinitionRegistryPostProcessor
。这些技术通常被用于高级配置场景,可以在Spring上下文的生命周期的早期介入,允许我们在容器实例化bean之前修改bean定义或动态注册bean。
# 1. 使用 ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar
可以在配置类处理阶段动态注册Bean,这个接口允许编程式的方式控制Bean的注册过程,非常适合处理自定义注解。
public class JobRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 根据条件动态注册bean或者修改已存在的bean定义
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyJobInitializer.class);
registry.registerBeanDefinition("myJobInitializer", builder.getBeanDefinition());
}
}
@Configuration
@Import(JobRegistrar.class)
public class JobConfig {
// 配置类内容
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2. 使用 BeanDefinitionRegistryPostProcessor
这是一个更为强大的工具,可以在容器标准初始化之后、所有bean定义将要被加载,bean实例还未创建前修改bean定义或增加额外的bean定义。
@Component
public class JobBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyJobInitializer.class);
registry.registerBeanDefinition("myJobInitializer", builder.getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 可用于修改Bean属性等后续处理
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 使用 FactoryBean
动态创建Bean
FactoryBean
是Spring提供的一个接口,通过实现这个接口,可以让你控制bean的实例化过程。这是实现复杂初始化逻辑或者动态代理创建的一种方式。
public class JobFactoryBean implements FactoryBean<JobComponent> {
@Override
public JobComponent getObject() throws Exception {
return new JobComponent(); // 可以实现复杂的初始化逻辑
}
@Override
public Class<?> getObjectType() {
return JobComponent.class;
}
@Override
public boolean isSingleton() {
return true; // 根据需要返回 true 或 false
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4. 结合使用 @Conditional
结合上述技术,使用@Conditional
可以根据环境或其他条件动态激活或禁用某些配置,进一步增加配置的灵活性和动态性。
@Conditional(MyCondition.class)
public class ConditionalJobConfig {
// 根据条件注册Bean
}
2
3
4
# 5. 使用 AspectJ 进行编译时织入
AspectJ 是一种与 Spring AOP 兼容的强大的面向切面的编程工具,它支持在编译时进行织入,而不是运行时。这意味着你可以在编译阶段修改字节码,实现非常复杂和强大的功能,而且性能开销比运行时代理小。
// 定义一个切面处理你的自定义注解
@Aspect
public class JobAspect {
@Before("@annotation(your.custom.annotation.Job)")
public void beforeJob(JoinPoint joinPoint) {
// 实现逻辑
}
}
2
3
4
5
6
7
8
# 6. 使用 Spring Cloud 的功能
如果你的应用是微服务架构,可以利用 Spring Cloud 提供的各种功能,如配置管理、服务发现和断路器。通过 Spring Cloud Config,可以动态地管理和注入配置,而不是静态地在应用中配置。
@RefreshScope
@RestController
public class MyController {
@Value("${some.value}")
private String value;
@RequestMapping("/get")
public String get() {
return this.value;
}
}
2
3
4
5
6
7
8
9
10
11
# 7. 自定义 ClassLoader
在 Java 中,ClassLoader 负责加载类。通过自定义 ClassLoader,可以在运行时动态地修改或生成类。结合 Spring,你可以控制 Spring Bean 的加载过程,甚至实现热替换部分组件。
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("my.special.package")) {
// 自定义加载逻辑
return generateClass(name);
}
return super.loadClass(name, true);
}
private Class<?> generateClass(String name) {
// 生成或修改类的字节码
return defineClass(name, bytecode, 0, bytecode.length);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 8. Spring的 EnvironmentPostProcessor
EnvironmentPostProcessor
允许在应用程序环境准备好之前自定义设置环境。这个处理器可以用来预处理配置数据,为后续的bean创建提供前置条件。
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources sources = environment.getPropertySources();
Map<String, Object> myMap = new HashMap<>();
myMap.put("key", "value");
sources.addFirst(new MapPropertySource("mySource", myMap));
}
}
2
3
4
5
6
7
8
9
# 9. 使用反射技术动态操作Bean
通过反射API,可以在运行时读取或修改类的私有成员,包括私有字段、方法等,从而实现高度灵活的动态特性。
@Component
public class DynamicBeanModifier {
@Autowired
private ApplicationContext context;
public void modifyBean(String beanName) throws Exception {
Object bean = context.getBean(beanName);
Field field = bean.getClass().getDeclaredField("someField");
field.setAccessible(true);
field.set(bean, "newValue");
}
}
2
3
4
5
6
7
8
9
10
11
12
13