项目中常常会使用到spring提供的aop技术,那么它的大概实现原理是什么?
省略spring扫描bean段落,当spring扫描完所有的bean后,开始对这些bean进行实例化和初始化,这样bean就准备好了。
首先有两个单词要注意:
Instantiation: 实例化(该对象还没生成)
Initialization:初始化 (该对象已经生成了,等待初始化)
下面以TestController bean创建为案例,一步步分析该bean的创建过程:
其中@MethodLog是一个注解用来标识AOP,主要目的是在具体方法调用的时候,打印方法的参数。
@RestController
public class TestController {
@MethodLog
@GetMapping(value = "test")
public String test(String name) {
System.out.println("i come here");
return "hello";
}
}
接下来先看创建bean方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
....
// 让BeanPostProcessors有机会返回一个代理,而不是目标bean实例
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
// 否则就去创建这个bean
Object beanInstance = doCreateBean(beanName,mbdToUse, args);
...
return beanInstance;
}
在上面的方法中,我们注意到resolveBeforeInstantiation(),当看到这里的时候,我以为项目中的aop技术就是在这里运用的,可是实际debug下来并不是。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
spring在创建这个TestController bean的时候,为什么不在resolveBeforeInstantiation返回这个代理对象呢?
这是因为对于TestController aop的目的是增强其功能,而不是随意返回一个TestController类就行了。
resolveBeforeInstantiation中有一个很重要的方法,那就是applyBeanPostProcessorsBeforeInstantiation(targetType, beanName),它的目的是在实例化之前返回一个bean来代替当前bean。
要注意的是下面这段代码判断。
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
不为空,才会调用,而applyBeanPostProcessorsAfterInitialization()方法是在实例化后进行返回的,主要目的是返回包装器,spring把实例化和初始化分开。
因此必须要先对TestController进行实例化,然后对TestController进行包装。
下面看doCreateBean():
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
// 填充bean
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
省略了很多代码,主要分析关键部分,首先spring会选择构造器实例化一个具体的bean,然后利用@value, @Autowired等注解来进行注入。
最后进行初始化。在实例化和填充阶段都不应该处理代理的逻辑。
下面分析初始化逻辑
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
Object wrappedBean = bean;
// 执行初始化之前的方法
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 执行初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
// 执行初始化之后的方法
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
当我debug TestController创建过程,我发现是从applyBeanPostProcessorsAfterInitialization()后返回了一个代理对象。
applyBeanPostProcessorsAfterInitialization()实际了是调用了所有的
BeanPostProcessor的postProcessAfterInitialization方法。
在这方法的注释上可以看出,它允许返回一个对象来代替当前对象,或者一个包装器。
在这些BeanPostProcessor中,有一个AbstractAutoProxyCreator,它就是用来产生代理对象的。
下面分析一下AbstractAutoProxyCreator的postProcessAfterInitialization():
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
核心是调用了wrapIfNecessary(),通过这个方法名称也可以看到出就是要判断是否对目的对象进行包装,如果要的话,就返回一个包装后的对象。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
在wrapIfNecessary()中有一句很重要的注释“Create proxy if we have advice.”,如果我们对某个类使用了aop技术,specificInterceptors不会空,然后根据这些specificInterceptors,调用createProxy()产生一个代理对象。现在大概知道了spring aop实现的初步入口点在哪些地方。
现在回过头来,resolveBeforeInstantiation到底在什么时候会返回一个不一样的实例代理对象?
先看怎么做:自定义一个InstantiationAwareBeanPostProcessor,返回该bean。
假设我研发了一个出租车系统,提供了标准的Car接口,并且提供了多种颜色的车来供用户选择。我开始使用下面的方式暴露服务:
public interface Car {
/**
* 根据用户喜欢的的颜色,来判断是否匹配使用该汽车载客
*
* @param color 客户喜欢的颜色
* @return
*/
boolean support(String color);
/**
* 载客到目的地
*
* @param user
* @param address
*/
void run(String user, String address);
}
@Component
public class BlueCar implements Car {
@Override
public boolean support(String color) {
return "Blue".equals(color);
}
@Override
public void run(String user, String address) {
System.out.println("BlueCar:" + "正在搭载客户:" + user + "到目的地:" + address);
}
}
@Component
public class RedCar implements Car {
@Override
public boolean support(String color) {
return "Red".equals(color);
}
@Override
public void run(String user, String address) {
System.out.println("RedCar:" + "正在搭载客户:" + user + "到目的地:" + address);
}
}
@RestController
public class TestController {
@Resource(name = "redCar")
private Car redCar;
@Resource(name = "blueCar")
private Car blueCar;
@GetMapping(value = "redCar")
public String redCar(String name, String color) {
boolean support = redCar.support(color);
if (support) {
redCar.run(name, "上海");
return "success";
}
return "fail";
}
@GetMapping(value = "blueCar")
public String blueCar(String name, String color) {
boolean support = blueCar.support(color);
if (support) {
blueCar.run(name, "上海");
return "success";
}
return "fail";
}
}
开始项目工作的很好,突然有一天公司经营不善,不在提供各种颜色的车了。而是使用一种默认颜色的车,来代替所有的车,假设叫CarProxy,那么代码怎么办呢?一种办法是把所有的@Resource(name = "blueCar"), @Resource(name = "redCar") 替换成@Resource(name = "carProxy")。如果项目中有很多这样的代码,那么风险会很高。
可以像下面这样处理:
@Component
public class CarInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
boolean isCar = false;
for (Class<?> anInterface : beanClass.getInterfaces()) {
if (anInterface == Car.class) {
isCar = true;
}
}
if (isCar) {
return new CarProxy();
}
return null;
}
}
这样当发现所有要注入为Car类型的车,会全部被替换为CarProxy。
这是我能想到的一种用法,可能更多的是在框架上使用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。