首页 > 其他分享 >在Spring中如何通过BeanFactoryPostProcessor和BeanPostProcessor来做一点好玩的事情

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

时间:2023-04-03 22:34:13浏览次数:37  
标签:BeanPostProcessor Spring class BeanFactoryPostProcessor public bean 我们

在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的实现都是跟它们密不可分,我们也能够通过实现这两个接口做一些简化我们开发的工作。

标签:BeanPostProcessor,Spring,class,BeanFactoryPostProcessor,public,bean,我们
From: https://www.cnblogs.com/loveletters/p/beanPostProcessAndBeanFactoryPostProcess.html

相关文章

  • springboot请求响应
    springboot请求响应1.什么是请求?响应?请求:获取请求数据响应:设置响应数据2.原始方法获取请求数据Controller方法形参中声明HttpServletRequest对象调用对象的getParameter(参数名)这种方式复杂繁琐//@RequestMapping("/simpleParam")//原始方式//创建请求对......
  • Springboot JSON整合—官方原版
    SpringBoot提供与三个JSON映射库的集成:GsonJacksonJSON-BJackson是首选和默认库。一、Jackson提供了Jackson的自动配置,Jackson是springbootstarterjson的一部分。当Jackson在类路径上时,会自动配置一个ObjectMapperbean。提供了几个配置财产,用于自定义ObjectMapper的配置。1.......
  • 怎么在springboot中配置https证书的详细教程
    前言由于小程序需要https,然后之前申请的域名过期了,用了两年由于忘记续费要将域名赎回居然要1200....想了一下之前还有另一个域名,干脆就用这个域名弄个二级域名出来,所以二级域名建立出来后需要在springboot项目上开启https访问废话不多说,开整在阿里云新建二级域名这个......
  • Spring事件详解,Spring-Event源码详解,一文搞透Spring事件管理
    文章目录一、Java中事件/监听器编程模型1、Java中Observable/Observer事件监听(1)代码实例(2)Java9新的事件监听2、面向接口的事件/监听器设计模式3、面向注解的事件/监听器设计模式二、Spring事件1、Spring标准事件-ApplicationEvent2、基于接口的Spring事件监听器代码实例3、基于注......
  • Spring注解驱动原理及源码,深入理解Spring注解驱动
    文章目录一、Java注解入门大全二、Spring注解驱动编程发展历程1、注解驱动启蒙时代:SpringFramework1.x@Transactional@ManagedResource2、注解驱动过渡时代:SpringFramework2.x@Repository@Component3、注解驱动黄金时代:SpringFramework3.x4、注解驱动完善时代:SpringFramewo......
  • Spring 类型转换详解,SpringBean创建时属性类型转换源码详解
    文章目录一、概述1、Spring类型转换的实现2、使用场景3、源码分析二、基于JavaBeans接口的类型转换1、代码实例2、Spring內建PropertyEditor扩展ByteArrayPropertyEditor3、自定义PropertyEditor扩展整合到springframework代码实例SpringPropertyEditor的设计缺陷三、Spr......
  • SpringBoot启动异常的错误①
    java:无法访问org.springframework.boot.SpringApplication错误的类文件:/D:/maven/repository/org/springframework/boot/spring-boot/3.0.5/spring-boot-3.0.5.jar!/org/springframework/boot/SpringApplication.class类文件具有错误的版本61.0,应为52.0 2023-04......
  • IDEA Spring-boot 使用@Component注解的工具类,用@Autowired注入 @Service或者@Reposit
    IDEASpring-boot使用@Component注解的工具类,用@Autowired注入@Service或者@Repository会空指针(使用@PostContruct)原文链接:https://blog.csdn.net/ld_secret/article/details/104627597/使用idea编译器时,对于spring-boot的项目,大都使用注解,那么:一、现象:@Component标注的U......
  • 设计模式(三十一)----综合应用-自定义Spring框架-自定义Spring IOC-定义解析器、IOC容
    3定义解析器相关类3.1BeanDefinitionReader接口BeanDefinitionReader是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:获取注册表的功能,让外界可以通过该对象获取注册表对象。加载配置文件,并注册bean数据。/***@versionv1.0*@ClassName:BeanDe......
  • springboot 日志
    <loggername="com.sinoservices.chainwork.bms"level="INFO"/><loggername="org.hibernate.orm.deprecation"level="error"/><loggername="druid"additivity="true"><levelval......