BeanPostProcessor 接口
简介
public interface BeanPostProcessor {
/**
*前置处理方法,在bean初始化之前被调用(初始化方法调用之前,属性设置之后)
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
*后置处理方法,在bean初始化之后被调用(初始化方法被调用后)
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
BeanPostProcessor 是spring提供的一个用于扩展的接口,spring中的bean都会被应用到BeanPostProcessor 中,可以在实现BeanPostProcessor 来扩展需要的功能。
源码部分
AbstractAutowireCapableBeanFactory.initializeBean
每一个bean都会被应用BeanPostProcessor的两个方法,当返回值为null时,会直接跳出前置或后置方法的应用
例如:有两个后处理器BeanPostProcessorImpl和BeanPostProcessor2Impl,如果在BeanPostProcessorImpl的postProcessBeforeInitialization方法返回null了,那么BeanPostProcessor2Impl的postProcessBeforeInitialization方法也不会执行了。但是不影响BeanPostProcessorImpl和BeanPostProcessor2Impl的postProcessAfterInitialization方法执行。
基本使用
使用BeanPostProcessor可以方便的对容器中的bean做一些定制化操作,比如将容器中的bean替换成相应的代理对象,或者更改属性值等。
/**
*UserService代理
*/
public class UserServiceProxy implements InvocationHandler {
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
public UserService getUserService(){
return (UserService)Proxy.newProxyInstance(getClass().getClassLoader(),userService.getClass().getInterfaces(),this );
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//只代理UserService接口中的方法
Method[] methods = UserService.class.getMethods();
if (methods==null || methods.length<1)
return method.invoke(userService, args);
List<String> methodNames = Arrays.stream(methods).map(m -> m.getName()).collect(Collectors.toList());
if (!methodNames.contains(method.getName()))
return method.invoke(userService, args);
//代理方法逻辑
System.out.println("开始代理方法,方法名:"+method.getName());
Object returnValue = method.invoke(userService, args);
System.out.println("代理方法执行完毕...");
return returnValue;
}
}
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessorImpl.postProcessBeforeInitialization:"+bean+":"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessorImpl.postProcessAfterInitialization:"+bean+":"+beanName);
if (bean instanceof UserService){
//替换成代理对象
return new UserServiceProxy((UserService)bean).getUserService();
}
return bean;
}
}
使用Ordered 接口控制执行顺序
如果有多个BeanPostProcessor 的实例可以使用Ordered接口来控制它们的执行顺序。
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor , Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessorImpl.postProcessBeforeInitialization:"+bean+":"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessorImpl.postProcessAfterInitialization:"+bean+":"+beanName);
if (bean instanceof UserService){
//替换成代理对象
return new UserServiceProxy((UserService)bean).getUserService();
}
return bean;
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE-1;
}
}
关于返回值
通过源码部分可以看到,接口两个方法的返回值会替换原来的bean,并应用到下一个后处理器方法。
当返回值为null时,bean的值不会被替换,并且这个bean不会被应用下面的后处理器方法了。
AutowiredAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor这是spring框架中自带的一个后处理器,用于处理@Autowired注解的依赖注入。它的主要作用是在bean实例化后,在初始化之前或之后,查找并注入被@Autowired注解标记的依赖。
BeanFactoryPostProcessor 接口
源码部分
AbstractApplicationContext
基本使用
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是该接口的实现类被调用时,bean都还没有实例化。可以通过BeanFactoryPostProcessor接口来动态修改bean的元数据,比如scope、属性值、class路径等一些可以在配置文件或配置类中设置的属性状态。
@Component
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessorImpl.postProcessBeanFactory------begin");
//获取bean的定义
BeanDefinition userBeanDefinition = beanFactory.getBeanDefinition("user");
//修改scope
userBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
//设置属性值
MutablePropertyValues propertyValues = userBeanDefinition.getPropertyValues();
propertyValues.addPropertyValue("name","Tom");
propertyValues.addPropertyValue("age",18);
System.out.println("BeanFactoryPostProcessorImpl.postProcessBeanFactory------end");
}
}
注:多个BeanFactoryPostProcessor的实例可以通过Ordered接口控制执行顺序
PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer是Spring框架中的一个特殊的BeanFactoryPostProcessor实现,用于解析属性占位符并替换为相应的属性值。
(1)创建一个外部的配置文件"user.properties"
bean.name=张三
bean.age=18
(2)配置文件或配置类中配置一个PropertySourcesPlaceholderConfigurer ,指定配置文件路径
配置类写法:
@Configuration
@ComponentScan("com.chy")
public class BeanPostProcessorConfig {
@Bean
public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setFileEncoding("UTF-8");
propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource("user.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
配置文件写法:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.chy"/>
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer ">
<property name="fileEncoding" value="UTF-8"></property>
<property name="location" value="user.properties"></property>
</bean>
</beans>
(3)使用"${key}格式读取配置文件中的值
@Component
public class User {
@Value("${bean.name}")
private String name;
@Value("${bean.age}")
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
<bean class="com.chy.pojo.User">
<property name="name" value="${bean.name}"/>
<property name="age" value="${bean.age}"/>
</bean>
@PropertySource && context:property-placeholder/
配置类可以使用@PropertySource简化配置一个PropertySourcesPlaceholderConfigurer
@PropertySource(value = "user.properties",encoding = "UTF-8")
@Configuration
@ComponentScan("com.chy")
public class BeanPostProcessorConfig {
// @Bean
// public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
// PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
// propertySourcesPlaceholderConfigurer.setFileEncoding("UTF-8");
// propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource("user.properties"));
// return propertySourcesPlaceholderConfigurer;
// }
}
配置文件使用property-placeholder简化
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.chy"/>
<context:property-placeholder location="user.properties" file-encoding="UTF-8" />
<!-- <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer ">-->
<!-- <property name="fileEncoding" value="UTF-8"></property>-->
<!-- <property name="location" value="user.properties"></property>-->
<!-- </bean>-->
<bean class="com.chy.pojo.User">
<property name="name" value="${bean.name}"/>
<property name="age" value="${bean.age}"/>
</bean>
</beans>
PropertyOverrideConfigurer
PropertyOverrideConfigurer是Spring框架中的一个特殊的BeanFactoryPostProcessor实现,用于在应用程序上下文启动时覆盖bean定义中的属性值,它和PropertySourcesPlaceholderConfigurer 很相似,都可以用于设置bean的属性值,不过不同的是PropertyOverrideConfigurer加载的外部配置文件需要有特殊的格式,如下:
<beanName>.<beanPropertyName>=<value>
- <beanName>:bean在容器中的名称
- <beanPropertyName>:属性名
- <value>:要设置的值
使用PropertyOverrideConfigurer更加的简单
(1)创建配置文件
user.name=Jack
user.age=20
(2)配置类或配置文件添加一个PropertyOverrideConfigurer指定文件位置
@Configuration
@ComponentScan("com.chy")
public class BeanPostProcessorConfig {
@Bean
public PropertyOverrideConfigurer getPropertyOverrideConfigurer(){
PropertyOverrideConfigurer propertyOverrideConfigurer = new PropertyOverrideConfigurer();
propertyOverrideConfigurer.setFileEncoding("UTF-8");
propertyOverrideConfigurer.setLocation(new ClassPathResource("userOverride.properties"));
return propertyOverrideConfigurer;
}
}
配置文件同等配置
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="fileEncoding" value="UTF-8"></property>
<property name="location" value="user.properties"></property>
</bean>
这样就行了,bean的属性就被自动设置了,不需要再去使用占位符"${}"。
FactoryBean 接口
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 获取bean的实例
*/
@Nullable
T getObject() throws Exception;
/**
* 生产的bean类型
*/
@Nullable
Class<?> getObjectType();
/**
* 生产的bean是否单例
*/
default boolean isSingleton() {
return true;
}
}
FactoryBean是一个工厂bean,用来自定义控制bean的产生,使用时只需将FactoryBean的实现类作为一个普通的bean注入容器就行了,然后就可以正常的获取FactoryBean生产的bean实例了。
基本使用
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
@Component("user")
public class UserFactoryBean implements FactoryBean<User> {
@Value("${bean.name}")
private String name;
@Value("${bean.age}")
private Integer age;
@Override
public User getObject() throws Exception {
User user = new User();
user.setName(name);
user.setAge(age);
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
@Test
public void testFactoryBean(){
//加”&“符表示获取的是对应的FactoryBean实例
System.out.println(app.getBean("&user",UserFactoryBean.class));
//不加”&“符就是获取FactoryBean实例产生的对象
System.out.println(app.getBean("user",User.class));
}
源码部分
FactoryBean生产的bean实例只有在getBean时才会被创建出来,如果是单例,则会将生产的对象放到一个缓存中,下次getBean时会直接从缓存中取了。
AbstractBeanFactory.getObjectForBeanInstance
FactoryBeanRegistrySupport.getObjectFromFactoryBean
FactoryBeanRegistrySupport.doGetObjectFromFactoryBean
由于被FactoryBean创建的bean只有在被getBean时才被创建,所以被工厂创建的bean不能享受到属性注入或者初始化或销毁方法。但是FactoryBean可以作为一个普通的bean使用这些功能,所以应当将被创建的bean的赋值逻辑或初始化逻辑写在FactoryBean中