Spring提供了一系列的接口来提供对Spring容器的扩展功能。下面我们一一介绍。

BeanPostProcessor自定义bean

前面一篇文章我们在自定义bean中提到,可以实现Spring的InitializingBean和DisposableBean接口来实现自定义bean的生命周期。如果是容器级别的,Spring提供了更加强大的BeanPostProcessor,来实现在容器级对Bean的扩展。

BeanPostProcessor接口定义了两个方法:

	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

该方法在调用容器初始化方法(如InitializingBean.afterPropertiesSet()或任何声明的init方法)之前,以及在任何bean初始化之后,被调用。

	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

该方法在容器初始化方法之后被调用。

BeanPostProcessor可以配置多个,如果想控制多个BeanPostProcessor的顺序,可以实现Ordered接口,来定义他们的顺序。

虽然BeanPostProcessor是通过ApplicationContext自动检测的,你也可通过ConfigurableBeanFactory的addBeanPostProcessor来手动注册。手动注册则其Ordered失效,以手动注册的先后为准。

还要注意,以编程方式注册的BeanPostProcessor实例总是在注册为自动检测的实例之前进行处理,而不接收任何显式排序。

所有BeanPostProcessor实例和这些实例直接引用的bean都在启动时实例化,因为AOP自动代理是作为BeanPostProcessor本身实现的,所以BeanPostProcessor实例和它们直接引用的bean都不符合自动代理的条件。

下面是一个调用的例子:

    <bean id="beanA" class="com.flydean.beans.BeanA"/>
<bean id="beanB" class="com.flydean.beans.BeanB"/> <bean class="com.flydean.beans.InstantiationTracingBeanPostProcessor"/>

调用实现:

    public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-post-processor.xml");
BeanA beanA = (BeanA) ctx.getBean("beanA");
System.out.println(beanA);
}

BeanFactoryPostProcessor自定义配置元数据

BeanFactoryPostProcessor接口的语义与BeanPostProcessor的语义相似,但有一个主要区别:BeanFactoryPostProcessor对Bean配置元数据进行操作。也就是说,Spring IOC容器允许BeanFactoryPostProcessor读取配置元数据,并可能在容器实例化BeanFactoryPostProcessor实例以外的任何bean之前对其进行更改。

BeanFactoryPostProcessor也可以配置多个,并通过实现Ordered接口来确定执行顺序。BeanFactoryPostProcessor定义了一个方法:

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

通过该方法可以获取到可配置的beanFactory从而对bean定义进行修改。

Spring提供了很多预定义的bean工厂后处理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。下面我们通过例子来说明怎么使用。

PropertyOverrideConfigurer类名替换

PropertyPlaceholderConfigurer主要用于从外部的Property文件读取属性,用来替换定义好的配置,这样做可以使部署应用程序的人员自定义特定于环境的属性,如数据库URL和密码,而不必为容器修改一个或多个XML定义主文件从而增加复杂性或风险。

下面是配置的XML文件:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:jdbc.properties"/>
</bean> <bean id="dataSource" destroy-method="close"
class="com.flydean.beans.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

这个例子展示了属性被配置在外部的Properties文件中。在运行时,使用PropertyPlaceholderConfigurer将元数据替换成DataSource中的某些属性。要替换的值被指定为${property-name}格式的占位符,该格式遵循ant和log4j以及JSP EL样式。

真实的值取自外部的Java Properties格式的文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

PropertyOverrideConfigurer属性覆盖

PropertyOverrideConfigurer可以用来覆盖Bean属性的默认值,或者设置新的值。我们看一个例子:

    <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="classpath:override.properties"/>
<property name="properties">
<value>beanOverride.url=com.something.DefaultStrategy</value>
</property>
</bean>
<bean name="beanOverride" class="com.flydean.beans.BeanOverride"/>

对应的类是:

@Data
public class BeanOverride { private String name="beanA";
private String url="http://www.163.com"; }

它的默认属性会被覆盖。

使用FactoryBean自定义实例化逻辑

FactoryBean接口提供3个方法:

  • Object getObject(): 返回工厂创建的实例,该实例可能是被共享的, 取决于该实例是单例还是多例模式。
  • boolean isSingleton():判断FactoryBean返回的是单例还是多例。
  • Class getObjectType():返回getObject() 方法返回的类型,如果提前不知道类型,那么返回null。

我们可以实现FactoryBean接口来自定义Bean的实现逻辑。

public class BeanFactoryBean implements FactoryBean {

    @Resource
private BeanA beanA; @Override
public Object getObject() throws Exception {
return beanA;
} @Override
public Class<?> getObjectType() {
return BeanA.class;
}
}

下面是其配置:

    <context:annotation-config/>
<bean id="beanA" class="com.flydean.beans.BeanA"/> <bean id="beanFactoryBean" class="com.flydean.beans.BeanFactoryBean"/>

如何使用?

    public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factory.xml");
BeanFactoryBean beanFactoryBean = (BeanFactoryBean) ctx.getBean("&beanFactoryBean");
System.out.println(beanFactoryBean.getObject());
System.out.println(beanFactoryBean.getObjectType()); BeanA beanA=(BeanA)ctx.getBean("beanFactoryBean");
System.out.println(beanA);
}

当需要向容器请求实际的FactoryBean实例本身而不是它生成的bean时,在调用ApplicationContext的getbean()方法时,在bean的ID前面加上符号(&)。因此,对于ID为beanFactoryBean的给定FactoryBean,在容器上调用getBean(“beanFactoryBean”)返回FactoryBean生成的bean,而调用getBean(“&beanFactoryBean”)则返回FactoryBean实例本身。

本节的例子可以参考:ioc-extend

更多教程请参考flydean的博客

Spring5参考指南:容器扩展的更多相关文章

  1. Spring5参考指南:IOC容器

    文章目录 为什么使用Spring5 什么是IOC容器 配置元数据 实例化容器 XML嵌套 groovy bean定义DSL 使用容器 最近在翻译Spring Framework Documentati ...

  2. Spring5参考指南:基于注解的容器配置

    文章目录 @Required @Autowired @primary @Qualifier 泛型 @Resource @PostConstruct和@PreDestroy Spring的容器配置可以有 ...

  3. Spring5参考指南:事件Event

    文章目录 基于继承的Event 基于注解的Event 异步侦听器 Spring提供了很方便的事件的处理机制,包括事件类ApplicationEvent和事件监听类ApplicationListener ...

  4. Spring5参考指南:组件扫描

    文章目录 组件扫描 @Component 元注解和组合注解 组件内部定义Bean元数据 为自动检测组件命名 为自动检测的组件提供作用域 生成候选组件的索引 组件扫描 上一篇文章我们讲到了annotat ...

  5. Spring5参考指南:Bean的生命周期管理

    文章目录 Spring Bean 的生命周期回调 总结生命周期机制 startup和Shutdown回调 优雅的关闭Spring IoC容器 Spring Bean 的生命周期回调 Spring中的B ...

  6. Spring5参考指南:Bean作用域

    文章目录 Bean作用域简介 Singleton作用域 Prototype作用域 Singleton Beans 中依赖 Prototype-bean web 作用域 Request scope Se ...

  7. Spring5参考指南:依赖注入

    文章目录 依赖注入 依赖注入的配置详解 depends-on lazy-init 自动装载 方法注入 依赖注入 依赖注入就是在Spring创建Bean的时候,去实例化该Bean构造函数所需的参数,或者 ...

  8. Spring5参考指南:Bean的创建

    文章目录 Spring容器中的Bean Bean的命名 Bean的实例化 Spring容器中的Bean Bean在Spring中就是一个业务组件,我们通过创建各种Bean来完成最终的业务逻辑功能. 在 ...

  9. Spring5参考指南:AspectJ注解

    文章目录 什么是AspectJ注解 启用AOP 定义Aspect 定义Pointcut 切入点指示符(PCD) 切入点组合 Advice 访问JoinPoint Advice参数 Advice参数和泛 ...

随机推荐

  1. PTA数据结构与算法题目集(中文) 7-11

    PTA数据结构与算法题目集(中文)  7-11 7-11 关键活动 (30 分)   假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行.“任务调度 ...

  2. python编程笔记整理(2)

    1.向字典中添加元素     字典名[键名] = 键值     my["姓名"] = "许嘉祺" (使用此代码可以把键值对添加到名为name的字典里.) (由于 ...

  3. Redis 练习(二)

    需求: 为购物网站实现登录状态及浏览记录的维护 进入时检查 token 是否已登录 每次进入更新 token 最新进入时间 记录用户浏览的商品信息(最多 25 个) 定时检查 token 数量,如果超 ...

  4. Linux/Unix下pid文件的作用

    主要有三点: (1) pid文件的内容:pid文件为文本文件,内容只有一行, 记录了该进程的ID. 用cat命令可以看到. (2) pid文件的作用:防止进程启动多个副本.只有获得pid文件(固定路径 ...

  5. 一键创建以太坊ERC20代币教程

    30秒极速创建以太坊ERC20代币 傻瓜式创建,界面化创建,自动创建代币,简单好用 合约采用新版 5.10 新版合约编译器,合约代码100%安全无任何漏洞 下面是详细的使用和创建教程 1.点击创建ER ...

  6. django生成验证码

    django生成验证码 # 制作验证码 def verify_code(): # 1,定义变量,用于画面的背景色.宽.高 # random.randrange(20, 100)意思是在20到100之间 ...

  7. 文本表格文件指定分隔符分列转Excel(java实现)

    我的需求: 嗯,实习中遇到,需要过滤数据然后以指定的列名输出为excel 我是这样解决的: 写出到一个文本或者表格文件然后指定分隔符分列的输出excel,因为要设计去重处理. 我需要做的: 写一个文本 ...

  8. matplotlib BlendedGenericTransform(混合变换)和CompositeGenericTransform(复合变换)

    2020-04-10 23:31:13 -- Edit by yangrayBlendedGenericTransform是Transform的子类,支持在x / y方向上使用不同的变换.(博主翻译为 ...

  9. SQL基础系列(4)-性能优化建议

    10.1 连接查询表的顺序问题 SQLSERVER的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表driving table)将被最先处理,在FROM子句中包 ...

  10. JAVA—线程(Thread)

    1.线程的状态有哪些 我记得在操作系统原理的书上有一张具体的图,暂时找不到书... new:新建状态,被创建出来后未启动时的线程状态. runnable:就绪状态,表示可以运行. blocked:阻塞 ...