通常,应用程序开发人员不需要子类化
ApplicationContext
实现类。相反,可以通过插入特殊集成接口的实现来扩展 Spring IoC 容器。接下来的几节描述了这些集成接口。
自定义 BeanPostProcessor
该BeanPostProcessor
接口定义了您可以实现的回调方法,以提供您自己的(或覆盖容器的默认)实例化逻辑、依赖关系解析逻辑等。如果你想在 Spring 容器完成实例化、配置和初始化 bean 之后实现一些自定义逻辑,你可以插入一个或多个自定义BeanPostProcessor
实现。
您可以配置多个实例,并且可以通过设置属性BeanPostProcessor
来控制这些BeanPostProcessor
实例的运行顺序。order
仅当BeanPostProcessor
实现Ordered
接口时才能设置此属性。如果你自己写BeanPostProcessor
,你也应该考虑实现Ordered
接口。
通过拦截方法,将返回值转换为大写
public class IService {
public String hello() {
return "Hello World";
}
}
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
// 为当前 bean 对象注册监控代理对象,负责增强 bean 对象方法的能力
Class beanClass = bean.getClass();
if (beanClass == IService.class) {
Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String result = (String) method.invoke(bean, args);
return result.toUpperCase();
}
});
return proxy;
}
return bean;
}
}
以下beans
元素使用InstantiationTracingBeanPostProcessor
:
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
https://www.springframework.org/schema/lang/spring-lang.xsd">
<bean id="IService" class="scripting.IService"/>
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
请注意 是如何InstantiationTracingBeanPostProcessor
定义的。它甚至没有名字,而且,因为它是一个 bean,它可以像任何其他 bean 一样被依赖注入。
以下 Java 应用程序运行上述代码和配置:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
BaseService serviceObj = (BaseService) factory.getBean("iService");
System.out.println(serviceObj.hello());
}
}
自定义配置元数据BeanFactoryPostProcessor
我们要看的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor
. 此接口的语义与 的语义相似,但BeanPostProcessor
有一个主要区别:BeanFactoryPostProcessor
对 bean 配置元数据进行操作。也就是说,Spring IoC 容器允许BeanFactoryPostProcessor
读取配置元数据并可能在容器实例化除实例之外的任何 bean之前BeanFactoryPostProcessor
更改它。
您可以使用标准 Java格式PropertySourcesPlaceholderConfigurer
将 bean 定义中的属性值外部化到单独的文件中。Properties
这样做使部署应用程序的人员能够自定义特定于环境的属性,例如数据库 URL 和密码,而无需修改容器的主要 XML 定义文件或文件的复杂性或风险。
考虑以下基于 XML 的配置元数据片段,其中DataSource
定义了带有占位符值的 :
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.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
文件配置的属性。在运行时,将 PropertySourcesPlaceholderConfigurer
应用于替换 DataSource 的某些属性的元数据。要替换的值被指定为表单的占位符${property-name}
,它遵循 Ant 和 log4j 以及 JSP EL 样式。
自定义实例化逻辑FactoryBean
您可以org.springframework.beans.factory.FactoryBean
为本身是工厂的对象实现接口。
该FactoryBean
接口是 Spring IoC 容器的实例化逻辑的可插入点。如果您有复杂的初始化代码,用 Java 更好地表达而不是(可能)冗长的 XML,您可以创建自己的 FactoryBean
,在该类中编写复杂的初始化,然后将您的自定义FactoryBean
插入容器中。
该FactoryBean<T>
接口提供了三种方法:
T getObject()
:返回此工厂创建的对象的实例。该实例可能会被共享,具体取决于该工厂是返回单例还是原型。boolean isSingleton()
:true
如果FactoryBean
返回单例或false
其他,则返回。此方法的默认实现返回true
.Class<?> getObjectType()
:返回getObject()
方法返回的对象类型,或者null
如果事先不知道类型。
FactoryBean
Spring 框架中的许多地方都使用了概念和接口。
@Component
public class HelloFactoryBean implements FactoryBean<String> {
String s = "hello";
@Override
public ZoneId getObject() throws Exception {
return s;
}
@Override
public Class<?> getObjectType() {
return String.class;
}
}
当一个Bean实现了FactoryBean
接口后,Spring会先实例化这个工厂,然后调用getObject()
创建真正的Bean。getObjectType()
可以指定创建的Bean的类型,因为指定类型不一定与实际类型一致,可以是接口或抽象类。
因此,如果定义了一个FactoryBean
,要注意Spring创建的Bean实际上是这个FactoryBean
的getObject()
方法返回的Bean。为了和普通Bean区分,我们通常都以XxxFactoryBean
命名。