spring aop 简介 & JDK 动态代理

aop 简介

aop,全称:aspect-oriented programming,即是面向切面编程。
aop和Ioc并称为spring的两大特性,但aop有什么用呢?

场景一:

对功能重复的代码,并被不同地方使用,我们都会把它简单的封装成util,以供调用。但是,我们并不能很好的灵活控制这个util的被调用。
假如我们需要在每次数据库操作之前或之后使用某个util记录日志,使用直接调用的话,要是有100个操作,你就需要在这100个操作前后调用log util。
又有可能,后来你不需要这个util了,你又需要在100个地方取消它。是不是很痛苦呢~~
当然,可以通过面向对象和设计模式的方法来解决一部分问题,但仍显得不够优雅和敏捷。

当然应用场景还不仅这些,还有如session管理,验证,事务,权限等等方面都有类似的问题。

为了解决这些痛点,于是便有了AOP。那AOP是如何解决这个问题的呢?首先了解基本概念。

基本概念


以上两张图,很好的展示了AOP的工作方式,以及为什么叫切面编程。
这里主要涉及到几个概念:

Advice(通知):

如图,主要描述要增强的行为,如场景一中的log行为,但不仅如此,Advice还分BeforeAdvice、AfterAdvice、ThrowsAdvice等类型。

Pointcut(切点):

这个描述了Advice或切面要嵌入的位置,比如场景一中的数据库操作,其实就是一个Pointcut。

现在,有了Advice和Pointcut,总感觉差点什么。没错,需要一个配置器(通知器),把两者关联起来。因此定义为:

Advisor(通知器):

通知器建立了Advice和Pointcut的关联,因此我们终于可以知道在某个切点要用哪个通知了。


因此,场景一的AOP解决方案,以Spring Aspect(注解版)为例:

1, 使用spring注解,如下代码, 定义Advice,下面我们使用@Before注解,被标注的方法相当与一个Advice。
2, @Before注解中有个参数,这个参数定义了切点的描述,这里可以使用一些通配符来简化配置。

@Aspect
@Component
public class LogAspect {

@Before("execution(* foo.FooDao.op(..))")
public void log(JoinPoint joinPoint) {
LOG.info("before ...");
}
}

使用注解,好处就是省去了Advisor的配置,偷个懒~~ 当然在applicationContext.xml中要启用<aop:aspectj-autoproxy/><context:component-scan才行。
XML 声明文档传送门

看完这个,你一定对spring aop的实现很好奇,接下来就简单看一下spring aop的工作flow吧。

实现过程


也许你已经猜到,要实现AOP,必须使用反射和动态代理。没错,Java的这个特性可以实现AOP。
spring环境下,再结合Ioc的统一对象管理,功能无比强大啊。
接下来我们就一起结合源码看一下aop是怎么工作的。

生成 aop proxy


这里Ioc部分不介绍了(请参阅另一博文spring Ioc),直接以aop proxy生成为入口。
以下是主要的类,接下来让我们慢慢体会这样设计的原因:

具体的aop代理对象的生成,分别是由AspectJProxyFactory、ProxyFactoryBean和ProxyFactory来完成的。

以ProxyFactoryBean.getObject()为例:

public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
return getSingletonInstance();
}
else {
...
return newPrototypeInstance();
}
}

initializeAdvisorChain()代码比较长,主要流程是 : 根据内置成员interceptorNames : String[]生成bean,然后namedBeanToAdvisor()转换成Advisor,
再调用父类AdvisedSupport.addAdvisor(Advisor)增加到通知器链LinkedList<Advisor>中,后面也会对这部分进行更详细介绍。

结合上面的class hierarchy图,可知AdvisedSupport主要封装了对通知和通知器的相关操作(从命名也可以知道),这些操作对不同的代理对象应该是一致的,
而代理对象的生成,是交给子类去实现的,ProxyCreatorSupport则协助子类进行proxy的生成。

而生成的proxy, 可以是singleton或者prototype,这个功能都是由AopProxy完成的。
而这个AopProxy是由AopProxyFactory创建,这部分代码在ProxyCreatorSupport中:

// ProxyCreatorSupport.java
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}

其中getAopProxyFactory(),即this.aopProxyFactory,是spring封装的DefaultAopProxyFactory:

public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}

DefaultAopProxyFactory createAopProxy()会根据target class是否接口来选择不同的生成方式(下图)。
如果是接口,则使用jdk动态代理;否则使用cglib,如下所示:
所以,上面提到的AopProxy是一个接口,它有两个实现:CglibAopProxy和JdkDynamicAopProxy。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

jdk和cglib的生成方式这里略过,感兴趣的话查看new JdkDynamicAopProxy(AdvisedSupport)CglibProxyFactory.createCglibProxy(config)即可。

拦截器调用


生成代理完成时,相关的拦截器已经配置完成了,现在只需在代理对象调用时回调这些拦截器即可,Jdk和cglib的代理和回调有些不同。

这里以大家熟悉的jdk dynamic proxy为例,如下图,JdkDynamicAopProxy实现了InvocationHandler:

invoke()的源码如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;

TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;

...
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}

如上可知,chain是通过AdvisedSupport(this.advised)的getInterceptorsAndDynamicInterceptionAdvice()获取的。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}

其中,this.advisorChainFactory是一个叫DefaultAdvisorChainFactory的工厂,主要实现了拦截器链的获取。
而且,会注册到AdvisorAdapterRegistry接口的实现DefaultAdvisorAdapterRegistry中。
DefaultAdvisorAdapterRegistry的构造器如下所示,可以看到,这里注册了一些常见的AdviceAdapter:

public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

上面这些adapters,主要有两个作用,以AfterReturningAdviceAdapter为例:

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}

public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}

}

作用之一是判断某个Advice属于什么类型;还有就是返回相对应的AdviceInterceptor。

advice的实现其实也是拦截器(实现了MethodInterceptor),如下所示:

和上面保持一致,以AfterReturingInterceptor为例:

public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}

回到JdkDynamicAopProxy.invoke(),其中的ReflectiveMethodInvocation.proceed()就是链调用主要入口, 如下:


public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}

Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}

上面主要是遍历每个 interceptorOrInterceptionAdvice,并调用相应的拦截器,这样串起来,整个流程就清晰了。
其中InterceptorAndDynamicMethodMatcher组合MethodInterceptor和MethodMatcher,使其作为通知链的一个element使用,如下:

class InterceptorAndDynamicMethodMatcher {

final MethodInterceptor interceptor;

final MethodMatcher methodMatcher;

public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
this.interceptor = interceptor;
this.methodMatcher = methodMatcher;
}

}

以上就是ProxyFactoryBean调用的主要流程,只是一个大概的流程,看源码的话,spring会有许多细节验证和处理。不过阅读时,一定要抓住重要的主线,还有熟悉常见的设计模式,这会让你有个全景的认识和理解。

DIY 属于自己的AOP

// TODO still in drafts 。