4. AOP
使用面向对象编程(OOP)有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志、安全检测等,我们只有在每个对象里引用公共行为,这样程序中就产生了大量的重复代码,程序就不便于维护了,所以就有了一个对面向对象编程的补充,即面向方面编程(AOP),AOP所关注的方向是横向的,不同于OOP的纵向。
AOP注入到Bean中的入口:
BeanFactory的getBean()方法的一大堆逻辑中会调用postProcessAfterInitialization方法,postProcessAfterInitialization方法会返回被代理的bean对象。也就是说getBean()方法会拿出被代理的对象。
AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,而实现BeanPostProcessor后,在getBean时,spring会遍历所有已经注册的BeanProcessor,调用其postProcessAfterInitialization方法,AOP的主要逻辑在此postProcessAfterInitialization方法中。AnnotationAwareAspectJAutoProxyCreator继承自AbstractAutoProxyCreator,所以后续需要看下实现类AbstractAutoProxyCreator#postProcessAfterInitialization()方法
4.1 动态代理
// AbstractAutoProxyCreator#postProcessAfterInitialization()
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 返回代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 在缓存中寻找Bean,如果发现该Bean无需代理(没匹配到Advisors)则直接返回
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;
}
// 获取增强方法或者增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// protected static final Object[] DO_NOT_PROXY = null;将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;
}
后面将主要看一下getAdvicesAndAdvisorsForBean和createProxy方法。
4.1.1 获取增强方法或增强器
protected Object[] getAdvicesAndAdvisorsForBean(
Class> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 主要实现逻辑在这里
List advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List findEligibleAdvisors(Class> beanClass, String beanName) {
// 获取所有定义的所有增强器
List candidateAdvisors = findCandidateAdvisors();
// 获取可以被apply的增强器
List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 根据@Priority注解中的value排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findCandidateAdvisors
- 获取所有beanName,这一步骤中所有在beanFactory中注册的bean都会被提取出来。
- 遍历所有beanName,并找出声明AspectJ注解的类(反射获取注解@Aspect),进行进一步的处理。
a. 获取PointCut注解,并获取其中的表达式@PointCut(“execution(* .test*()..)”)。
b. 获取@Before,@Around,@After等注解,并交给对应的增强器。 - 对标记为AspectJ注解的类进行增强器的提取。
- 将提取结果加入缓存。
其中2.a 步,在经历比较深的调用层次后,我们可以找到关键代码,如下:
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
Class> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
// 根据不同的注解类型封装不同的增强器
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
以前置增强和后置增强为例
前置增强:
在拦截器链中放置MethodBeforeAdviceInterceptor,而在MethodBeforeAdviceInterceptor中又放置了AspectJMethodBeforeAdvice,并在调用invoke时首先串联调用。
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 先调用AspectJMethodBeforeAdvice#before(),再执行原有方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
// 反射调用增强方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
@Override
public boolean isBeforeAdvice() {
return true;
}
@Override
public boolean isAfterAdvice() {
return false;
}
后置增强:
与前置增强不同,没有提供中间的类,而是在拦截器链中直接使用AspectJAfterAdvice
// 实现了MethodInterceptor接口
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 调用原方法
return mi.proceed();
}
finally {
// 反射调用增强方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return true;
}
}
findCandidateAdvisors方法中返回了所有的增强器,下面看一下如何寻找匹配的增强器。
findAdvisorsThatCanApply
这个方法中主要调用了AopUtils#canApply()方法,用于判断pointCut中表达式与该bean是否匹配。注意哦,findAdvisorsThatCanApply是从getBean方法一路调用过来的。
public static boolean canApply(Advisor advisor, Class> targetClass, boolean hasIntroductions) {
/**
* 处理引介增强,引介增强主要是可以给被代理类动态增加一个父类或实现一个接口(提供默认实现方法)或添加一个字段
* 用的不太多,没细看
*/
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
/**
* 判断该pointCut能否被应用在该类上
* 找到该类的所有接口,遍历它们的所有方法,看是否与pointCut定义的表达式匹配
*/
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// IntroductionAdvisor或PointcutAdvisor以外的其他Advisor直接返回true
return true;
}
}
通过getAdvicesAndAdvisorsForBean方法获取了所有可以被应用在该Bean上的增强器,供下一步创建动态代理使用。
4.1.2 根据获取的增强进行代理
根据配置创建不同类型的动态代理
- 通过getProxy()将代理类返回
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
- Spring的代理有JDKProxy和CglibProxy的实现
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 设置 则config.isProxyTargetClass()为true,强制使用cglib
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() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 返回cglib动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 返回jdk动态代理
return new JdkDynamicAopProxy(config);
}
}
a. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。(JDK动态代理只能对实现了接口的类生成代理)
b. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
c. 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
d. JDK动态代理的实现方式
i. 实现InvocationHandler函数
ii. 实现invoke方法,在此对原方法进行增强
iii. getProxy方法,返回代理对象
- Spring AOP中的JdkDynamicAopProxy也与此相似,JdkDynamicAopProxy实现了InvocationHandler接口,AOP的核心逻辑就写在JdkDynamicAopProxy的invoke方法中。
返回了一个代理对象,当真正执行Bean中的方法时,会执行代理对象的invoke方法
a. 获取当前方法的拦截器链(包含增强方法等)
b. 如果拦截器为空,直接调用切点方法
c. 如果有拦截器,执行拦截器链
i. 如果拦截器链中的拦截器的method与当前传入的method匹配则执行该拦截器
ii. 如果method不匹配,则继续调用拦截器链,直到最后一个,如果也不匹配则执行被代理的方法
iii. 拦截器链将代理的工作委托给了各个增强器,如MethodBeforeAdviceInterceptor
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// 解决内部调用aop(如事务)不生效的问题,将proxy暴露出来。将proxy注入AopContext。
// 可以使用((UserService)AopContext.currentProxy()).testRollback2()调用,AopContext中存了一个threadlocal的proxy
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class> targetClass = (target != null ? target.getClass() : null);
// 获取之前得到的拦截器链(Advisors)
List
/**
* 递归方法,循环执行拦截器链中的所有拦截器
*/
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) {
// 动态匹配当前被调用的方法与getBean()中注入的拦截器链是否匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
// 如果匹配则执行
return dm.interceptor.invoke(this);
}
else {
// 没匹配到,则继续遍历拦截器链
return proceed();
}
}
else {
// 普通拦截器,直接调用拦截器,如AspectJAfterAdvice#invoke(this),传入this,可以继续执行proceed方法,继续遍历全部拦截器链
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
- 使用Cglib进行动态代理,对应逻辑在CglibAopProxy类中,具体实现AOP的逻辑与JdkDynamicAopProxy类似。
4.1.3 AspectJ
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
- 通知:
- 定义:切面也需要完成工作。在 AOP 术语中,切面的工作被称为通知。
- 工作内容:通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决何时执行这个工作。
- Spring 切面可应用的 5 种通知类型:
Before——在方法调用之前调用通知
After——在方法完成之后调用通知,无论方法执行成功与否
After-returning——在方法执行成功之后调用通知
After-throwing——在方法抛出异常后进行通知
Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
- 连接点:
- 定义:连接点是一个应用执行过程中能够插入一个切面的点。
- 连接点可以是调用方法时、抛出异常时、甚至修改字段时、
- 切面代码可以利用这些点插入到应用的正规流程中
- 程序执行过程中能够应用通知的所有点。
- 切点:
- 定义:如果通知定义了“什么”和“何时”。那么切点就定义了“何处”。切点会匹配通知所要织入的一个或者多个连接点。
- 通常使用明确的类或者方法来指定这些切点。
- 作用:定义通知被应用的位置(在哪些连接点)
- 要对哪些方法进行增强,比如对test()方法进行before和after的增强,test()方法为切点。
- 切面:
- 定义:切面是通知和切点的集合,通知和切点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。
- 引入:
- 引入允许我们向现有的类中添加方法或属性
- 织入:
- 织入是将切面应用到目标对象来创建的代理对象过程。
- 切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可以织入
4.2 静态代理
AOP的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类并无差别,所以在效率上会相对高些。