BeanPostProcessor的简单使用

日常使用到 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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值