日常使用到 BeanPostProcessor
的场景不多,因此不是特别熟悉,查阅了一些资料,写了个Demo,记录下来,遇到问题希望能提供一些解决的思路。
简介
接口定义如下:
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;
}
}
其中,postProcessBeforeInitialization
的调用时机为依赖注入之后,postProcessAfterInitialization
将在 initMethod 执行之后被调用。这两个方法的返回值都不能为null
Demo练习
此Demo的目的大致是实现一个 @Autowired
+ @Qualifier
的功能
工程结构
代码
1、业务接口
package com.example.demo.service;
public interface GoOut {
void play(String name);
}
2、实现类
package com.example.demo.service.impl;
import com.example.demo.service.GoOut;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class GoFishing implements GoOut {
@Override
public void play(String name) {
log.info("{}, let's go fishing!!!", name);
}
}
package com.example.demo.service.impl;
import com.example.demo.service.GoOut;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class GoSkating implements GoOut {
@Override
public void play(String name) {
log.info("{}, let's go skating!!!", name);
}
}
3、定义注解(意为精确连接)
package com.example.demo.annotations;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccurateWire {
String value();
}
4、用于测试的CommandLineRunner组件
/**
* 预测的运行结果:
* construstor
* auto wire
* 在BeanPostProcessor中进行赋值
* PostConstruct
* afterPropertiesSet
* init
* 业务
* preDestroy
* destroy
*/
@Getter
@Slf4j
public class DemoCommandLineRunner implements CommandLineRunner, InitializingBean {
// @AccurateWire("goFishing")
@AccurateWire("goSkating")
private GoOut goOut;
private int integer;
public DemoCommandLineRunner() {
log.info("constructor");
}
@Value("${test.value:500}")
public void setInteger(int integer) {
this.integer = integer;
log.info("auto wire");
}
@PostConstruct
public void postConstruct() {
log.info("postConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("afterPropertiesSet");
}
public void init() {
log.info("init");
}
@Override
public void run(String... args) {
goOut.play("James Wood");
}
@PreDestroy
public void preDestroy() {
log.info("preDestroy");
}
public void destroy() {
log.info("destroy");
}
}
5、实现Demo主要功能相关的类
@Component
@Slf4j
public class WireProcessor implements BeanPostProcessor {
@Autowired
private ApplicationContext applicationContext;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 此时的状态是,实例化后,执行了auto wire,但demo中并未使用依赖注入相关注解,因此是没有值的
if ("demoCommandLineRunner".equals(beanName)) {
DemoCommandLineRunner demoCommandLineRunner = (DemoCommandLineRunner) bean;
log.info("此时bean [{}] 中的成员变量值为 [{}]", beanName, demoCommandLineRunner.getGoOut());
// 查看IoC中GoOut子类的Bean
Map<String, GoOut> serviceMap = applicationContext.getBeansOfType(GoOut.class);
serviceMap.entrySet().forEach(entry -> log.info("getBeansOfType entry {}", entry));
Class<?> clazz = bean.getClass();
Field[] fields = clazz.getDeclaredFields();
try {
for (Field field : fields) {
if (field.isAnnotationPresent(AccurateWire.class)) {
AccurateWire accurateWire = field.getAnnotation(AccurateWire.class);
String targetBeanName = accurateWire.value();
field.setAccessible(true);
// version1
// field.set(bean, serviceMap.get(targetBeanName));
// version2
field.set(bean, createProxy(serviceMap.get(targetBeanName)));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
log.info("field set value successful");
}
return bean;
}
public Object createProxy(Object target) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(GoOut.class);
proxyFactory.addAdvice(new MyMethodInterceptor(target));
return proxyFactory.getProxy();
}
}
package com.example.demo.config;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
private final Object target;
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 其他处理
return methodInvocation.getMethod().invoke(target, methodInvocation.getArguments());
}
}
需要注意的是:
- 注入代理对象可以在方法执行前后加入另外的逻辑或对方法的返回值进行处理。本Demo中业务接口的返回值为void,因此方法执行结果为null。
- 也可以在
postProcessAfterInitialization
方法中创建代理对象。
6、Spring Boot启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean(initMethod = "init", destroyMethod = "destroy")
public DemoCommandLineRunner demoCommandLineRunner() {
return new DemoCommandLineRunner();
}
}
运行结果
2021-03-30 11:32:10.055 INFO 9824 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-03-30 11:32:10.383 INFO 9824 --- [ main] c.e.demo.runner.DemoCommandLineRunner : constructor
2021-03-30 11:32:10.383 INFO 9824 --- [ main] c.e.demo.runner.DemoCommandLineRunner : auto wire
2021-03-30 11:32:10.383 INFO 9824 --- [ main] com.example.demo.config.WireProcessor : 此时bean [demoCommandLineRunner] 中的成员变量值为 [null]
2021-03-30 11:32:10.383 INFO 9824 --- [ main] com.example.demo.config.WireProcessor : getBeansOfType entry goFishing=com.example.demo.service.impl.GoFishing@481ba2cf
2021-03-30 11:32:10.383 INFO 9824 --- [ main] com.example.demo.config.WireProcessor : getBeansOfType entry goSkating=com.example.demo.service.impl.GoSkating@46b61c56
2021-03-30 11:32:10.383 INFO 9824 --- [ main] com.example.demo.config.WireProcessor : field set value successful
2021-03-30 11:32:10.383 INFO 9824 --- [ main] c.e.demo.runner.DemoCommandLineRunner : postConstruct
2021-03-30 11:32:10.383 INFO 9824 --- [ main] c.e.demo.runner.DemoCommandLineRunner : afterPropertiesSet
2021-03-30 11:32:10.383 INFO 9824 --- [ main] c.e.demo.runner.DemoCommandLineRunner : init
2021-03-30 11:32:10.461 INFO 9824 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.676 seconds (JVM running for 1.19)
2021-03-30 11:32:10.461 INFO 9824 --- [ main] com.example.demo.service.impl.GoSkating : James Wood, let's go skating!!!
2021-03-30 11:32:10.477 INFO 9824 --- [extShutdownHook] c.e.demo.runner.DemoCommandLineRunner : preDestroy
2021-03-30 11:32:10.477 INFO 9824 --- [extShutdownHook] c.e.demo.runner.DemoCommandLineRunner : destroy
Process finished with exit code 0