Spring-容器的扩展

本文详细介绍了Spring框架中的BeanPostProcessor接口及其使用,包括前置和后置处理方法,以及如何通过BeanFactoryPostProcessor定制bean的属性和scope。同时探讨了FactoryBean接口,用于自定义bean生成和单例模式。
摘要由CSDN通过智能技术生成

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中

  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值