在Spring中如何通过BeanFactoryPostProcessor和BeanPostProcessor来做一点好玩的事情

在Spring中如何通过BeanFactoryPostProcessor和BeanPostProcessor来做一点好玩的事情

介绍

BeanFactoryPostProcessor跟BeanPostProcessor是Spring为开发者提供的在Bean加载时候的扩展点。灵活的运用这两个扩展点可以帮助我们做一些好玩的事情,它们为我们提供了无限的扩展能力。

BeanFactoryPostProcessor


public interface BeanFactoryPostProcessor {

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

BeanFactoryPostProcessor是一个函数式接口里面,实现这个接口我们可以获取到BeanFactory,进而我们可以获取到BeanDefinition。熟悉Spring的朋友应该知道Spring在加载Bean的时候首先是封装成为一个BeanDefinition。所以说实现这个接口可以在bean定义对象没有实例化之前,读取bean定义对象配置元数据,并可以对配置元数据的一些属性修改。也就是说:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。在整个过程中,只调用一次这个接口的实现类。我们可以修改bean的定义信息,如是否单例,是否懒加载,以及bean的成员属性。切记不要在我们扩展的方法中调用会触发bean实例化的方法。

@Component
@Data
public class User {
    private String name = "张三";
    private Integer age = 21;
}


@Component
public class BeanFactoryPostProcessDemo  implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        final BeanDefinition user = beanFactory.getBeanDefinition("user");
        final MutablePropertyValues propertyValues = user.getPropertyValues();
        propertyValues.add("name", "lisi");
        propertyValues.add("age", 20);


    }
}

@RestController
public class TestController {
    @Resource
    private User user;

    @GetMapping("/getUser")
    public User getUser(){
        return user;
    }
}

调用方法,结果如下,我们通过改变bean定义的方式改变了User的属性

我们可以看到通过改变bean对象的definition可以改变很多东西,我就不一一举例说明了。

思考:可能你会觉得这些东西我们在定义bean对象的时候就直接考虑好就行了,为什么还要在后续的容器加载bean的过程中再去更改呢?如果仅仅是针对于我们自己定义的一些bean对象当然不用这么大费周章的去做修改,但是如果是我们引入的第三方依赖中定义的一些bean对象呢,比如说Springboot引入的一些starter。这些bean并不是由我们来定义的,但是我们有想对它的一些bean的描述信息做一些修改。比方说我们在做单元测试的时候有些bean对象我们暂时用不到它,那么我们可以设置为懒加载已提高容器启动速度。我们还可以通过它来对于配置文件里面的一些敏感信息进行解密操作,比方说数据库的连接信息,服务器的账户密码等等,我们都可以在BeanFactoryPostProcessor中解密。具体的案例我会在后文讲解Spring的aware相关接口的时候给出。

我们可以看到BeanFactoryPostProcessor是在bean对象创建之前执行的,而且我们禁止调用beanFacotry.getBean相关的操作,因为会导致bean的提前初始化。

BeanPostProcessor

bean的后置处理器,相较于BeanFactoryPostProcessor,BeanPostProcessor可能我们使用的更为常见,它内部有两个default标记的默认实现,都是直接返回bean对象,一个是postProcessBeforeInitialization执行的时机为bean对象创建完成但是还未进行初始化,一个是postProcessAfterInitialization执行的时机为bean对象初始化完成之后。

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;
	}

在下面的例子中我们将通过自定义注解加自定义的BeanPostProcessor在spring 的bean中注入对象,我们的需求是想直接把json数据转换为javabean对象注入到我们需要用到的类中。


// 我们定义的javabean
@Data
public class Human {
    private String name;
    private String  age;
}
// 继承于Human的Person类
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper=true)
public class Person extends Human {
   private int sex;
}

// 我们自定义的注解作用于字段上
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Json2Bean {
    String jsonValue() default "json字符串";
    Class<?> clazz()  default Object.class;

}



// 将json数据转换为实例化javabean对象并且注入bean的成员属性
@Component
public class Json2BeanPostProcess implements  BeanPostProcessor {

    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            // 获取bean对象的所有字段如果被我们自定义的注解所标记那么我们便获取注解中的描述信息通过fastjson来反序列化
            Json2Bean annotation = field.getAnnotation(Json2Bean.class);
            if (null!=annotation){
                String s = annotation.jsonValue();

                Class<?> clazz = annotation.clazz();
                Object o = Object.class.equals(clazz) ? JSONObject.parseObject(s, field.getType()) : JSONObject.parseObject(s, clazz);

                field.set(bean,o);

            }
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}


@Component

public class Mock {
  

    @Json2Bean(jsonValue = "{\"name\":\"王五\",\"age\":22,\"sex\":1}",clazz = Person.class)
    private static Human person;
    @Json2Bean(jsonValue = "{\"name\":\"王五\",\"age\":22,\"sex\":1}")
    private Human human;
    
    }

这样我们就可以直接注入我们想要属性了,相比Spring提供的@Value注解,我们自定义的可以更加灵活的实例化对象,比如我们能够控制实现父类还是子类,我们也可以控制数据的来源,比方说我们自定义注解中提供一个url地址这样我们可以在beanPostprocess中通过http请求的方式来获取数据。相比@Value注解我们可以更加简单的注入静态的成员变量。

结尾

BeanFactoryPostProcessor跟BeanPostProcessor这两个接口我已经简单的讲解了下如何使用它们,其实在spring本身也经常用到这两个接口,比如我们@Value中解析表达式,AOP的实现都是跟它们密不可分,我们也能够通过实现这两个接口做一些简化我们开发的工作。

posted @ 2023-04-03 22:21  loveletters  阅读(55)  评论(0编辑  收藏  举报