一、前言
BeanPostProcessor 接口是 Spring 提供的众多接口之一,他的作用主要是如果我们需要在Spring 容器完成 Bean 的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个 BeanPostProcessor 接口的实现,然后注册到容器中。本节中会对以下两部分进行讲解:
- Spring 中如何使用 BeanPostProcessor 处理实例化对象
- 手动实现 BeanPostProcessor(此处还是根据前面章节的代码进行拓展,模仿实现一个简易的 BeanPostProcessor)
二、Spring 中如何使用 BeanPostProcessor 处理实例化对象
1、Spring中Bean的实例化过程图示:
由图可以看出,Spring 中的 BeanPostProcessor 在实例化过程处于的位置分为两部分--前置处理和后置处理,而 BeanPostProcessor 接口也提供了两个可实现的方法,下面我们看一下源码:
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;
}
}
由方法名字也可以看出,前者在实例化及依赖注入完成后、在任何初始化代码(比如配置文件中的init-method)调用之前调用;后者在初始化代码调用之后调用。此处需要注意的是:接口中的两个方法都要将传入的 bean 返回,而不能返回 null,如果返回的是 null 那么我们通过 getBean() 方法将得不到目标。
2、自定义类来实现 BeanPostProcessor 接口
MyBeanPostProcessor.java
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean 对象初始化之前······");
return bean;
// return bean对象监控代理对象
}
public Object postProcessAfterInitialization(final Object beanInstance, String beanName) throws BeansException {
// 为当前 bean 对象注册监控代理对象,负责增强 bean 对象方法的能力
Class beanClass = beanInstance.getClass();
if (beanClass == ISomeService.class) {
Object proxy = Proxy.newProxyInstance(beanInstance.getClass().getClassLoader(),
beanInstance.getClass().getInterfaces(),
new InvocationHandler() {
/**
* @param proxy 代理监控对象
* @param method doSome()方法
* @param args doSome()方法执行时接收的实参
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("ISomeService 中的 doSome() 被拦截了···");
String result = (String) method.invoke(beanInstance, args);
return result.toUpperCase();
}
});
return proxy;
}
return beanInstance;
}
}
我们自定义的类实现了 BeanPostProcessor 接口,主要对 postProcessAfterInitialization() 方法进行了实现,用于增强 bean 对象的能力,这里我们使用了一下小例子,就是将当前 bean 对象返回的结果全部改为大写,因此这里我们使用到了一个代理对象,对 ISomeService 中的 doSome() 方法进行拦截。在 invoke() 方法中,对结果进行转换,下面我们给出 ISomeService 对象的相关代码:
(1)BaseService.java
public interface BaseService {
String doSomething();
String eat();
}
(2)ISomeService.java
public class ISomeService implements BaseService {
public String doSomething() {
return "Hello AlanShelby"; // 增强效果:返回内容全部大写
}
public String eat() {
return "eat food";
}
}
doSomething() 方法返回的是"Hello AlanShelby",我们想要的到的是"HELLO ALANSHELBY"。
3、在配置文件中注册被监控的实现类以及注册代理实现类
<!-- 注册 bean:被监控的实现类 -->
<bean id="iSomeService" class="top.alanshelby.service.impl.ISomeService"></bean>
<!-- 注册代理实现类 -->
<bean class="top.alanshelby.utils.MyBeanPostProcessor"></bean>
这就是对 BeanPostProcessor 的简单使用,测试时,直接使用 ApplicationContext 的方式调用 getBean() 方法即可,测试代码如下:
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext("spring_config.xml");
BaseService serviceObj = (BaseService) factory.getBean("iSomeService");
System.out.println(serviceObj.doSomething());
}
这样我们就得到了想要的结果。
三、手动实现 BeanPostProcessor
使用 Spring 提供的 BeanPostProcessor 后,是不是也想自己动手实现一下,如果我们只是照着 Spring 模仿着做一个,其实也没有多复杂,只是我们考虑的东西紧紧实在这一功能点上,对于拓展的东西不进行实现,这样就简单了很多,这里的 BaseService 和 ISomeService 和上面相同,就不再给出代码,下面我们自己动手实现一下吧。
(1)首先我们先“抄袭”一下源码中的接口,来当做我们自己的 BeanPostProcessor :
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return bean;
}
}
这个代码和源码中提供的几乎是一模一样,不再多做解释。
(2)自定义实现类 MyBeanPostProcessor1 :
public class MyBeanPostProcessor1 implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
System.out.println("bean 对象初始化之前······");
return bean;
// return bean对象监控代理对象
}
public Object postProcessAfterInitialization(final Object beanInstance, String beanName) throws Exception {
// 为当前 bean 对象注册监控代理对象,负责增强 bean 对象方法的能力
Class beanClass = beanInstance.getClass();
if (beanClass == ISomeService.class) {
Object proxy = Proxy.newProxyInstance(beanInstance.getClass().getClassLoader(),
beanInstance.getClass().getInterfaces(),
new InvocationHandler() {
/**
* @param proxy 代理监控对象
* @param method doSome()方法
* @param args doSome()方法执行时接收的实参
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("ISomeService 中的 doSome() 被拦截了···");
String result = (String) method.invoke(beanInstance, args);
return result.toUpperCase();
}
});
return proxy;
}
return beanInstance;
}
}
此代码和上一部分中的唯一区别就在于这里实现的是我们自己写的接口。
(3)对 BeanFactory 的拓展:
public class BeanFactory {
private List<BeanDefined> beanDefinedList; // 存放bean的集合
private Map<String, Object> springIoc; // 存放已经创建好的实例对象(用于单例模式)
private BeanPostProcessor processorObj; // 后置对象
public BeanFactory(List<BeanDefined> beanDefinedList) throws Exception {
this.beanDefinedList = beanDefinedList;
springIoc = new HashMap<String, Object>(); // 所有scope="singleton"采用单例模式管理bean对象
for (BeanDefined bean : beanDefinedList) {
if ("singleton".equals(bean.getScope())) {
String classPath = bean.getClassPath();
Class classFile = Class.forName(classPath);
/********** BeanPostProcessor 处理 begin **********/
Object instance = classFile.newInstance();
// 判断当前对象是一个 bean 对象还是一个后置处理对象
isProcessor(instance, classFile);
/********** BeanPostProcessor 处理 end **********/
springIoc.put(bean.getBeanId(), instance);
}
}
}
/**
* 判断当前对象是一个 bean 对象还是一个后置处理对象(根据接口进行判断)
*
* @param instance 当前的实例对象
* @param classFile 当前的实例对象关联的类路径
*/
private void isProcessor(Object instance, Class classFile) {
Class[] interfaceArray = classFile.getInterfaces();
if (interfaceArray == null) {
return;
}
for (int i = 0; i < interfaceArray.length; i++) {
Class interfaceType = interfaceArray[i];
if (interfaceType == BeanPostProcessor.class) {
// 证明当前的实例对象是后置处理器
this.processorObj = (BeanPostProcessor) instance;
}
}
}
/**
* 获取bean实例
*
* @param beanId
* @return
* @throws Exception
*/
public Object getBean(String beanId) throws Exception {
Object instance = null;
Object proxyObj = null; // 当前实例对象的代理监控对象
for (BeanDefined bean : beanDefinedList) {
if (beanId.equals(bean.getBeanId())) {
String classPath = bean.getClassPath();
Class classFile = Class.forName(classPath);
String scope = bean.getScope();
String factoryBean = bean.getFactoryBean();
String factoryMethod = bean.getFactoryMethod();
if ("prototype".equals(scope)) {
if (factoryBean != null && factoryMethod != null) {
// 用户希望指定工厂来创建实例对象
Object factoryObj = springIoc.get(factoryBean);
Class factoryClass = factoryObj.getClass();
Method methodObj = factoryClass.getDeclaredMethod(factoryMethod, null);
methodObj.setAccessible(true);
instance = methodObj.invoke(factoryObj, null);
} else {
// 如果scope是prototype(原型模式),每一次都创建一个新的实例对象
instance = classFile.newInstance();
}
} else {
// 如果scope是singleton(单例模式),返回同一个实例对象
instance = springIoc.get(beanId);
}
if (this.processorObj != null) {
proxyObj = this.processorObj.postProcessBeforeInitialization(instance, beanId);
// 实例对象初始化,Spring 依赖注入
proxyObj = this.processorObj.postProcessAfterInitialization(instance, beanId);
// 此时的 proxyObj 可能是原始 bean 对象,也可能是代理对象
return proxyObj;
} else {
return instance;
}
}
}
return null;
}
}
对 BeanFactory 的拓展是最为重要的,我们最主要的是对 isProcessor() 方法,此方法是判断当前对象是一个 bean 对象还是一个后置处理对象(根据接口进行判断),在 getBean() 方法中,调用了接口中定义的方法。
(4)测试代码:
public static void main(String[] args) throws Exception {
// 1、声明注册bean
BeanDefined beanObj = new BeanDefined();
beanObj.setBeanId("iSomeService");
beanObj.setClassPath("top.alanshelby.service.impl.ISomeService");
// 获取工厂类
BeanDefined beanObj1 = new BeanDefined();
beanObj1.setClassPath("top.alanshelby.utils.MyBeanPostProcessor1");
List<BeanDefined> configuration = new ArrayList<BeanDefined>();
configuration.add(beanObj);
configuration.add(beanObj1);
// 2、声明一个BeanFactory,类似于Spring中的ApplicationContext
BeanFactory factory = new BeanFactory(configuration);
// 3、开发人员向BeanFactory索要实例对象
BaseService b = (BaseService) factory.getBean("iSomeService");
System.out.println("b =" + b);
System.out.println("b方法 =" + b.doSomething());
}
(5)测试结果: