基本概念:
- AOP( Aspect-Oriented Programming):直译过来就是面向切面编程。最早是由AOP联盟(AOP Alliance)组织提出来的概念。解决的问题场景:将一些公共代码(日志记录,性能统计,安全控制,事务处理,异常处理等等)抽离出来,系统运行时可动态的应用到业务系统上。
- 通知(Advice):也称增强,用于实现增强功能的.
- 连接点(Joinpoint):在匹配目标的哪个位置做增强(before、after、around、return、throw).
- 切点(Pointcut):用于匹配类或方法,一个切点可以对应多个连接点.
- 织入 (Weaving):织入是把切面应用到目标对象,一般是通过代理对象实现.
Spring中另外两个术语:
- Advisor:可以理解为是个组件,包含advice和pointcut两个组件
- Advised:它是spring AOP代理工厂的配置类接口,负责操作和管理 Advisor 的
Spring AOP与AspectJ:
AOP是一种概念,Spring AOP和AspectJ都实现了AOP;Spring使用了AspectJ的部分API,生成代理对象Spring用的是JDK动态代理和CGLIB,而AspectJ是通过修改字节码来完成。
Aspectj可参考另外博文:Java Agent【java探针】及AspectJ-优快云博客
PointCut继承体系:
Pointcut:有两部分组成:ClassFilter 、MethodMatcher;ClassFilter用于过滤Class;MethodMatcher用于匹配方法
package org.springframework.aop
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
/**
* Pointcut由ClassFilter和MethodMatcher构成
*/
public interface Pointcut {
/**
* 返回用于匹配Class的过滤器
*/
ClassFilter getClassFilter();
/**
* 返回用于匹配方法的过滤器
*/
MethodMatcher getMethodMatcher();
/**
* Pointcut实例: 切点可以匹配适用于所有Class以及方法
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
package org.spring.aop.pointcut;
import org.springframework.aop.IntroductionAdvisor;
/**
* 切点与目标Class做匹配的过滤器
*
* 可以用作{@link Pointcut}的一部分功能,也可以用在引介增强{@link IntroductionAdvisor}匹配目标类
* 在Spring AOP中主要是为目标类的原有方法增强功能,但是还有一种特殊增强叫引介增强,它不是增强原有的方法而是为原有目标类增加新的方法。
*/
public interface ClassFilter {
/**
* 切点与目标Class进行匹配,判断切点与其是否匹配适用
*/
boolean matches(Class<?> clazz);
/**
* ClassFilter实例,切点可以匹配适用于所有目标Class
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
package org.springframework.aop;
import java.lang.reflect.Method;
/**
* 切点与目标方法做匹配的过滤器
*/
public interface MethodMatcher {
/**
* 静态匹配:只匹配方法对象
*/
boolean matches(Method method, Class<?> targetClass);
/**
* 标注动态匹配的,如果返回true,进行动态匹配;返回false,不需要动态匹配
*/
boolean isRuntime();
/**
* 动态匹配:匹配方法对象和方法参数对象
* 匹配方法:匹配名称、方法注解、方法返回类型、方法返回类型的注解
* 匹配参数:匹配参数个数、参数的类型、参数类型的注解
*/
boolean matches(Method method, Class<?> targetClass, Object[] args);
/**
* MethodMatcher实例,切点可以匹配适用于所有目标方法
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
MethodMatcher有个特殊子类IntroductionAwareMethodMatcher,处理<引介增强>这种特殊情况。
<引介增强>与Spring AOP中的普通增强有些不同,普通增强是为目标类的原有方法增强功能,而引介增强是为目标类增加新方法来达到增强的能力
/**
* MethodMatcher的一种特殊子接口。对方法进行匹配时,将<引介增强>考虑进去,做一些特殊逻辑处理
*/
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
/**
* 与目标方法进行静态匹配时.将<引介增强>因素考虑进去
*
* @param hasIntroductions
* 是否有<引介增强>
* @return
*/
boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions);
}
定义一个普通类,用于演示用例
import org.springframework.transaction.annotation.Transactional;
public class UserService {
@Transactional
public void say() {
System.out.println("Hello World!!");
}
}
自定义类,实现接口Pointcut, ClassFilter, MethodMatcher
import org.springframework.aop.Pointcut;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
public class CustomPointcut implements Pointcut, ClassFilter, MethodMatcher {
//---------------------------------------------------------------------
// Implementation of Pointcut
//---------------------------------------------------------------------
@Override
public ClassFilter getClassFilter() {
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
return this;
}
//---------------------------------------------------------------------
// Implementation of ClassFilter
//---------------------------------------------------------------------
@Override
public boolean matches(Class<?> clazz) {
//判断目标类是否存在注解@Service
if (clazz.isAnnotationPresent(Service.class)) {
return true;
}
return false;
}
//---------------------------------------------------------------------
// Implementation of MethodMatcher
//---------------------------------------------------------------------
@Override
public boolean matches(Method method, Class<?> targetClass) {
//判断目标方法是否存在注解@Transactional
if (method.isAnnotationPresent(Transactional.class)) {
return true;
}
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object[] args) {
throw new UnsupportedOperationException("Illegal MethodMatcher usage");
}
}
测试代码:
public static void main(String[] args) throws Exception {
CustomPointcut pointcut = new CustomPointcut();
boolean matchResult = pointcut.matches(UserService.class);
System.out.println("ClassFilter.matches(Class<?>)===匹配结果==>" + matchResult);
Method method = UserService.class.getDeclaredMethod("say", new Class[]{String.class});
matchResult = pointcut.matches(method, UserService.class);
System.out.println("MethodMatcher.matches(Method, Class<?>)===匹配结果==>" + matchResult);
matchResult = pointcut.matches(method, UserService.class, new Class[]{String.class});
System.out.println("MethodMatcher.matches(Method, Class<?>, args)===匹配结果==>" + matchResult);
}
执行结果:
Spring2.0开始引入AspectJ,AspectJExpressionPointcut是Spring基于AspectJ表达式而实现的一个Pointcut,AspectJExpressionPointcut在目前spring中使用非常频繁,所以非常重要。
package org.springframework.aop.aspectj;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.patterns.NamePattern;
import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
import org.aspectj.weaver.reflect.ShadowMatchImpl;
import org.aspectj.weaver.tools.ContextBasedMatcher;
import org.aspectj.weaver.tools.FuzzyBoolean;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.aspectj.weaver.tools.MatchingContext;
import org.aspectj.weaver.tools.PointcutDesignatorHandler;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.AbstractExpressionPointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* 基于AspectJ表达式功能实现的一个Pointcut子类
* @since 2.0
*/
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();
static {
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
}
private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class);
/**
* 切点声明范围
*/
private Class<?> pointcutDeclarationScope;
/**
* 切点参数名称列表
*/
private String[] pointcutParameterNames = new String[0];
/**
* 切点参数类型列表
*/
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
/**
* Spring bean工厂
*/
private BeanFactory beanFactory;
/**
* AspectJ切点类加载器,构建AspectJ切点时使用
*/
private transient ClassLoader pointcutClassLoader;
/**
* AspectJ表达式
* AspectJ包内的类org.aspectj.weaver.tools.PointcutExpression
* 封装解析execution(* org.spring.aop.service..*.*(..))的对象
*/
private transient PointcutExpression pointcutExpression;
/**
* 缓存切点与方法匹配的结果
* ShadowMatch属于AspectJ包内的类org.aspectj.weaver.tools.ShadowMatch
*
* ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(methodToMatch);
*
* 切点与目标方法匹配后返回ShadowMatch对象,shadowMatch.alwaysMatches()为true代表二者匹配;shadowMatch.neverMatches()为true代表二者不匹配
*/
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
/**
* 无参构造函数
*/
public AspectJExpressionPointcut() {
}
/**
* 有参构造函数
* @param declarationScope
* @param paramNames
* 切点参数名称列表
* @param paramTypes
* 切点参数名称列表
*/
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
this.pointcutDeclarationScope = declarationScope;
if (paramNames.length != paramTypes.length) {
throw new IllegalStateException(
"Number of pointcut parameter names must match number of pointcut parameter types");
}
this.pointcutParameterNames = paramNames;
this.pointcutParameterTypes = paramTypes;
}
/**
* 设置切点声明范围
*/
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
this.pointcutDeclarationScope = pointcutDeclarationScope;
}
/**
* 设置切点参数名称
*/
public void setParameterNames(String... names) {
this.pointcutParameterNames = names;
}
/**
* 设置切点参数类型
*/
public void setParameterTypes(Class<?>... types) {
this.pointcutParameterTypes = types;
}
/**
* 设置bean工厂
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* 返回自身对象,AspectJExpressionPointcut实现了接口ClassFilter
*/
public ClassFilter getClassFilter() {
checkReadyToMatch();
return this;
}
/**
* 返回自身对象,AspectJExpressionPointcut实现了接口IntroductionAwareMethodMatcher
*/
public MethodMatcher getMethodMatcher() {
checkReadyToMatch();
return this;
}
/**
* 检查切入点是否准备好匹配,惰性构建(用到时再构建)AspectJ切入点表达式
* - 就是将字符串表达式转化为AspectJ表达式对象
* String expression = execution(* org.spring.aop.service..*.*(..))
* PointcutExpression pointcutExpression=new PointcutExpressionImpl(Pointcut pointcut, String expression, PointcutParameter[] params, World inWorld)
*/
private void checkReadyToMatch() {
//判断表达式expression是否为空
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
//创建AspectJ切点类加载器
this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
ClassUtils.getDefaultClassLoader());
//创建AspectJ切点表达式
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}
/**
* 创建AspectJ切点表达式
*/
private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
//创建并初始化AspectJ切点解析器
PointcutParser parser = initializePointcutParser(classLoader);
//构建AspectJ切点参数对象数组
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
for (int i = 0; i < pointcutParameters.length; i++) {
//根据传入设置的切点参数名称和切点参数类型构建切点参数对象
pointcutParameters[i] = parser.createPointcutParameter(
this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
}
// 创建AspectJ切点表达式
return parser.parsePointcutExpression(
replaceBooleanOperators(getExpression()),
this.pointcutDeclarationScope, pointcutParameters);
}
/**
* 创建并初始化AspectJ切点解析器
*/
private PointcutParser initializePointcutParser(ClassLoader cl) {
//创建AspectJ切点解析器
PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
SUPPORTED_PRIMITIVES, cl);
parser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
return parser;
}
/**
* 将xml配置中的切点表达式中的特殊符号转化为AspectJ切点解析器可识别的
* " and " 替换为 " && "
* " or " 替换为 " || "
* " not " 替换为 " ! "
*/
private String replaceBooleanOperators(String pcExpr) {
String result = StringUtils.replace(pcExpr, " and ", " && ");
result = StringUtils.replace(result, " or ", " || ");
result = StringUtils.replace(result, " not ", " ! ");
return result;
}
/**
* 返回AspectJ切点表达式
*/
public PointcutExpression getPointcutExpression() {
checkReadyToMatch();
return this.pointcutExpression;
}
//---------------------------------------------------------------------
// Implementation of ClassFilter
//---------------------------------------------------------------------
/**
* 切点与目标class匹配
*/
public boolean matches(Class<?> targetClass) {
checkReadyToMatch();
try {
try {
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
if (fallbackExpression != null) {
return fallbackExpression.couldMatchJoinPointsInType(targetClass);
}
}
}
catch (BCException ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
return false;
}
//---------------------------------------------------------------------
// Implementation of IntroductionAwareMethodMatcher
//---------------------------------------------------------------------
/**
* 静态匹配,考虑引介增强场景
*/
public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
checkReadyToMatch();
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (beanHasIntroductions) { //引介增强直接返回true,因为引介增强的方法是新增的,不可能通过表达式能匹配上
return true;
}
return false;
}
}
//---------------------------------------------------------------------
// Implementation of MethodMatcher
//---------------------------------------------------------------------
/**
* 静态匹配
*/
public boolean matches(Method method, Class<?> targetClass) {
return matches(method, targetClass, false);
}
/**
* 标注动态匹配的,如果返回true,进行动态匹配;返回false,不需要动态匹配
* AspectJ表达式:this()、target()、@annotation()、@within()、@target()、args()、@args()等都视为动态匹配,一般会进入3个参数的matches方法中
*/
public boolean isRuntime() {
checkReadyToMatch();
return this.pointcutExpression.mayNeedDynamicTest();
}
/**
* 动态匹配的
* AspectJ表达式:this()、target()、@annotation()、@within()、@target()、args()、@args()等都视为动态匹配,一般会进入3个参数的matches方法中
*
* 如何理解Spring的动态匹配,动态匹配一般是无法直接和目标类或方法匹配的,可能是和他们的代理对象、注解、方法注解、方法返回类型等等做比较,总之就是无法直接做匹配,还需要进一步的处理
*/
public boolean matches(Method method, Class<?> targetClass, Object[] args) {
checkReadyToMatch();
ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
ShadowMatch originalShadowMatch = getShadowMatch(method, method);
ProxyMethodInvocation pmi = null;
//aop目标对象
Object targetObject = null;
//aop代理对象
Object thisObject = null;
try {
/**
* 在spring AOP框架中,advisor一般是个list链,Spring AOP一般会在第一个位置默认添加一个全匹配的拦截器ExposeInvocationInterceptor
* ExposeInvocationInterceptor在第一次被调用的时候会将对应的MethodInvocation放到ThreadLocal中,MethodInvocation中含有目标对象和代理对象
* 这个地方就是从ThreadLocal中拿到MethodInvocation,进而拿到目标对象和代理对象,像表达式this(),是用this中的表达式和代理对象做比较,此处正好可以应用上
*/
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
targetObject = mi.getThis();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
pmi = (ProxyMethodInvocation) mi;
thisObject = pmi.getProxy();
}
catch (IllegalStateException ex) {
// No current invocation...
// TODO: Should we really proceed here?
logger.debug("Couldn't access current invocation - matching with limited context: " + ex);
}
JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
/*
* Do a final check to see if any this(TYPE) kind of residue match. For
* this purpose, we use the original method's (proxy method's) shadow to
* ensure that 'this' is correctly checked against. Without this check,
* we get incorrect match on this(TYPE) where TYPE matches the target
* type but not 'this' (as would be the case of JDK dynamic proxies).
* <p>See SPR-2979 for the original bug.
*/
if (pmi != null) { // there is a current invocation
/*RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
return false;
}*/
if (joinPointMatch.matches()) {
bindParameters(pmi, joinPointMatch);
}
}
return joinPointMatch.matches();
}
protected String getCurrentProxiedBeanName() {
return ProxyCreationContext.getCurrentProxiedBeanName();
}
//------------------------------------------------------------------------------------------------------------
// 下方属于AspectJ框架的API,基本掌握上面的代码逻辑即可,Spring源码用到很多其它知识,理解会用即可,暂时没必要深究
//------------------------------------------------------------------------------------------------------------
/**
* Get a new pointcut expression based on a target class's loader rather than the default.
*/
private PointcutExpression getFallbackPointcutExpression(Class<?> targetClass) {
try {
ClassLoader classLoader = targetClass.getClassLoader();
if (classLoader != null && classLoader != this.pointcutClassLoader) {
return buildPointcutExpression(classLoader);
}
}
catch (Throwable ex) {
logger.debug("Failed to create fallback PointcutExpression", ex);
}
return null;
}
private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
if (shadowMatch instanceof DefensiveShadowMatch) {
return new RuntimeTestWalker(((DefensiveShadowMatch) shadowMatch).primary);
}
return new RuntimeTestWalker(shadowMatch);
}
private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
// Note: Can't use JoinPointMatch.getClass().getName() as the key, since
// Spring AOP does all the matching at a join point, and then all the invocations
// under this scenario, if we just use JoinPointMatch as the key, then
// 'last man wins' which is not what we want at all.
// Using the expression is guaranteed to be safe, since 2 identical expressions
// are guaranteed to bind in exactly the same way.
invocation.setUserAttribute(getExpression(), jpm);
}
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
// Avoid lock contention for known Methods through concurrent access...
ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
synchronized (this.shadowMatchCache) {
// Not found - now check again with full lock...
PointcutExpression fallbackExpression = null;
Method methodToMatch = targetMethod;
shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the declaring ClassLoader instead...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex2) {
fallbackExpression = null;
}
}
if (shadowMatch == null && targetMethod != originalMethod) {
methodToMatch = originalMethod;
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex3) {
// Could neither introspect the target class nor the proxy class ->
// let's try the original method's declaring class before we give up...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex4) {
fallbackExpression = null;
}
}
}
if (shadowMatch == null) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackExpression.matchesMethodExecution(methodToMatch));
}
this.shadowMatchCache.put(targetMethod, shadowMatch);
}
}
}
return shadowMatch;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AspectJExpressionPointcut)) {
return false;
}
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AspectJExpressionPointcut: ");
if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
sb.append("(");
for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
sb.append(this.pointcutParameterTypes[i].getName());
sb.append(" ");
sb.append(this.pointcutParameterNames[i]);
if ((i+1) < this.pointcutParameterTypes.length) {
sb.append(", ");
}
}
sb.append(")");
}
sb.append(" ");
if (getExpression() != null) {
sb.append(getExpression());
}
else {
sb.append("<pointcut expression not set>");
}
return sb.toString();
}
/**
* Handler for the Spring-specific {@code bean()} pointcut designator
* extension to AspectJ.
* <p>This handler must be added to each pointcut object that needs to
* handle the {@code bean()} PCD. Matching context is obtained
* automatically by examining a thread local variable and therefore a matching
* context need not be set on the pointcut.
*/
private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
private static final String BEAN_DESIGNATOR_NAME = "bean";
public String getDesignatorName() {
return BEAN_DESIGNATOR_NAME;
}
public ContextBasedMatcher parse(String expression) {
return new BeanNameContextMatcher(expression);
}
}
/**
* Matcher class for the BeanNamePointcutDesignatorHandler.
* <p>Dynamic match tests for this matcher always return true,
* since the matching decision is made at the proxy creation time.
* For static match tests, this matcher abstains to allow the overall
* pointcut to match even when negation is used with the bean() pointcut.
*/
private class BeanNameContextMatcher implements ContextBasedMatcher {
private final NamePattern expressionPattern;
public BeanNameContextMatcher(String expression) {
this.expressionPattern = new NamePattern(expression);
}
public boolean couldMatchJoinPointsInType(Class someClass) {
return (contextMatch(someClass) == FuzzyBoolean.YES);
}
public boolean couldMatchJoinPointsInType(Class someClass, MatchingContext context) {
return (contextMatch(someClass) == FuzzyBoolean.YES);
}
public boolean matchesDynamically(MatchingContext context) {
return true;
}
public FuzzyBoolean matchesStatically(MatchingContext context) {
return contextMatch(null);
}
public boolean mayNeedDynamicTest() {
return false;
}
private FuzzyBoolean contextMatch(Class<?> targetType) {
String advisedBeanName = getCurrentProxiedBeanName();
if (advisedBeanName == null) { // no proxy creation in progress
// abstain; can't return YES, since that will make pointcut with negation fail
return FuzzyBoolean.MAYBE;
}
if (BeanFactoryUtils.isGeneratedBeanName(advisedBeanName)) {
return FuzzyBoolean.NO;
}
if (targetType != null) {
boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
return FuzzyBoolean.fromBoolean(
matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
}
else {
return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
}
}
private boolean matchesBeanName(String advisedBeanName) {
if (this.expressionPattern.matches(advisedBeanName)) {
return true;
}
if (beanFactory != null) {
String[] aliases = beanFactory.getAliases(advisedBeanName);
for (String alias : aliases) {
if (this.expressionPattern.matches(alias)) {
return true;
}
}
}
return false;
}
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, just initialize state after deserialization.
ois.defaultReadObject();
// Initialize transient fields.
// pointcutExpression will be initialized lazily by checkReadyToMatch()
this.shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
}
private static class DefensiveShadowMatch implements ShadowMatch {
private final ShadowMatch primary;
private final ShadowMatch other;
public DefensiveShadowMatch(ShadowMatch primary, ShadowMatch other) {
this.primary = primary;
this.other = other;
}
public boolean alwaysMatches() {
return this.primary.alwaysMatches();
}
public boolean maybeMatches() {
return this.primary.maybeMatches();
}
public boolean neverMatches() {
return this.primary.neverMatches();
}
public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) {
try {
return this.primary.matchesJoinPoint(thisObject, targetObject, args);
}
catch (ReflectionWorldException ex) {
return this.other.matchesJoinPoint(thisObject, targetObject, args);
}
}
public void setMatchingContext(MatchingContext aMatchContext) {
this.primary.setMatchingContext(aMatchContext);
this.other.setMatchingContext(aMatchContext);
}
}
}
关于AspectJ表达式的使用可以参考:Spring AOP之Aspectj表达式-优快云博客
Advice继承体系:
图中所示接口皆为标识接口,接口内容为空.
例如:Advice
package org.aopalliance.aop;
public interface Advice {
}
接口全路径:
-
Advice:org.aopalliance.aop.Advice
-
Interceptor:org.aopalliance.intercept.Interceptor
-
BeforeAdvice:org.springframework.aop.BeforeAdvice
-
AfterAdvice:org.springframework.aop.AfterAdvice
-
ThrowsAdvice:org.springframework.aop.ThrowsAdvice
AOP联盟定义接口:Advice、Interceptor
Spring AOP定义接口:BeforeAdvide(标识:前置增强)、AfterAdvice(标识:后置增强)、ThrowsAdvice(标识:异常增强)
注:AOP联盟组织定义了一套AOP标准接口,供各个AOP框架使用,位于aopalliance.jar中
类关系图继续延伸:
Spring AOP内部针对AspectJ方式的每种连接点(around、before、after、after-returning、after-throwing)都实现了相应的Advice
-
<aop:around> ===> AspectJAroundAdvice
-
<aop:before> ===> AspectJMethodBeforeAdvice
-
<aop:after> ===> AspectJAfterAdvice
-
<aop:after-returning> ===> AspectJAfterReturningAdvice
-
<aop:after-throwing> ===> AspectJAfterThrowingAdvice
定义Advice方法行为的三个接口:MethodBeforeAdvice、MethodInterceptor、AfterReturningAdvice、
重点关注MethodBeforeAdvice、MethodInterceptor、AfterReturningAdvice,他们内部都有方法定义,决定了5中Advice(AspectJMethodBeforeAdvice、AspectJAroundAdvice、AspectJAfterAdvice、AspectJAfterThrowingAdvice、AspectJAfterReturningAdvice)的方法行为。
package org.aopalliance.intercept;
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
package org.springframework.aop;
import java.lang.reflect.Method;
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
package org.springframework.aop;
import java.lang.reflect.Method;
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}
单个Advice的类关系图:
观察上面拆分后的类关系图:
1、Interceptor、MethodInterceptor均属于AOP联盟接
-
org.aopalliance.Interceptor
-
org.aopalliance.MethodInterceptor
2、ThrowsAdvice(Spring aop内部没为其实现子的接口或类,需自行实现,如果发现Advice是ThrowsAdvice类型的,将会被转为ThrowsAdviceInterceptor处理)
3、AspectJMethodBeforeAdvice:标识接口为BeforeAdvice,方法行为来源MethodBeforeAdvice
4、AspectJAroundAdvice:标识接口为Interceptor,方法行为来源MethodInterceptor
5、AspectJAfterAdvice、AspectJAfterThrowingAdvice:标识接口为AfterAdvice,方法的行为来源于MethodInterceptor
6、AspectJAfterReturningAdvice:标识接口为AfterAdvice,方法行为来源AfterReturningAdvice
以AspectJAroundAdvice为例写个demo了解实现过程
普通业务类:
package org.spring.aop.advice.test;
public interface IUserService {
void say();
}
package org.spring.aop.advice.test;
public class UserService implements IUserService {
public void say() {
System.out.println("Hello world!!");
}
}
增强业务类:
package org.spring.aop.advice.test;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAdvice {
public Object info(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===============log record start==============");
Object object = pjp.proceed();
System.out.println("===============log record end================");
return object;
}
}
测试类:
package org.spring.aop.advice.test;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.config.MethodLocatingFactoryBean;
import org.springframework.aop.config.SimpleBeanFactoryAwareAspectInstanceFactory;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
public class AspectJAroundAdviceTest {
public static void main(String[] args) throws Throwable {
//===============准备测试数据=============start====
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(LogAdvice.class);
beanFactory.registerBeanDefinition("logAdvice", beanDefinition);
//===============准备测试数据=============end====
MethodLocatingFactoryBean methodLocatingFactoryBean = new MethodLocatingFactoryBean();
methodLocatingFactoryBean.setTargetBeanName("logAdvice");
methodLocatingFactoryBean.setMethodName("info");
methodLocatingFactoryBean.setBeanFactory(beanFactory);
//获取增强方法对象,获取的是logAdvice.info()对应的method对象
Method aspectJAroundAdviceMethod = methodLocatingFactoryBean.getObject();
//获取pointcut对象
String expression = "execution(* org.spring.aop.advice.test.UserService.*(..))";
AspectJExpressionPointcut expressionPointcut = new AspectJExpressionPointcut();
expressionPointcut.setExpression(expression);
//获取Advice对象
SimpleBeanFactoryAwareAspectInstanceFactory aif = new SimpleBeanFactoryAwareAspectInstanceFactory();
aif.setAspectBeanName("logAdvice");
aif.setBeanFactory(beanFactory);
//创建AspectJAroundAdvice对象
AspectJAroundAdvice aspectJAroundAdvice = new AspectJAroundAdvice(aspectJAroundAdviceMethod, expressionPointcut, aif);
UserService userService = beanFactory.getBean(UserService.class);
JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(aspectJAroundAdvice, userService);
//通过代理将通知应用到业务代码上
IUserService userServiceProxy = (IUserService) jdkDynamicAopProxy.getProxy();
userServiceProxy.say();
}
private static class MyMethodInvocation extends ReflectiveMethodInvocation {
protected MyMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
}
}
private static class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
private AspectJAroundAdvice aspectJAroundAdvice;
private UserService userService;
public JdkDynamicAopProxy(AspectJAroundAdvice aspectJAroundAdvice, UserService userService) {
this.aspectJAroundAdvice = aspectJAroundAdvice;
this.userService = userService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation mi = new MyMethodInvocation(proxy, userService, method, args, UserService.class, Arrays.asList());
//业务增强
return aspectJAroundAdvice.invoke(mi);
}
@Override
public Object getProxy() {
return getProxy(Thread.currentThread().getContextClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
return Proxy.newProxyInstance(classLoader, new Class[]{IUserService.class}, this);
}
}
}
执行结果:
Advisor继承体系:
Advisor比较简单,主要就是持有Advice和PointCut。
引介增强(Introduction):Spring中除了可以在方法维度上增强功能,还可以在类维度上增强功能,并且是不改变原有的类,通过动态新增方法
引介增强比较简单,只简单介绍使用方法
方式一:硬编码方式
普通业务类:
package org.spring.aop.introduction.service;
public interface IUserService {
void say();
}
package org.spring.aop.introduction.service;
public class UserService implements IUserService {
public void say() {
System.out.println("Hello world!!");
}
}
引介增强类:
package org.spring.aop.introduction.advice;
public interface ILogService {
void logInfo();
}
package org.spring.aop.introduction.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInfo;
public class LogAdvice implements MethodInterceptor, IntroductionInfo, ILogService {
@Override
public void logInfo() {
System.out.println("======print log========");
}
@Override
public Class[] getInterfaces() {
return new Class[]{ILogService.class};
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if(!invocation.getMethod().getName().equals("logInfo")) {
return invocation.proceed();
} else {
logInfo();
return null;
}
}
}
package org.spring.aop.introduction.advice;
public interface IAddService {
void doSomething();
}
package org.spring.aop.introduction.advice;
public class AddService implements IAddService {
@Override
public void doSomething() {
System.out.println("I am doing well!!!");
}
}
测试代码:
package org.spring.aop.introduction;
import org.spring.aop.introduction.advice.AddService;
import org.spring.aop.introduction.advice.IAddService;
import org.spring.aop.introduction.advice.ILogService;
import org.spring.aop.introduction.advice.LogAdvice;
import org.spring.aop.introduction.service.IUserService;
import org.spring.aop.introduction.service.UserService;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.DeclareParentsAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
public class MainTest {
public static void main(String[] args) {
IUserService service = new UserService();
ProxyFactory proxyFactory = new ProxyFactory(service);
IUserService serviceProxy = (IUserService) proxyFactory.getProxy();
serviceProxy.say();
System.out.println("\n-------------对目标对象增强功能-----------------\n");
//创建advice
LogAdvice advice = new LogAdvice();
//创建advisor
Advisor advisor = new DefaultIntroductionAdvisor(advice);
proxyFactory.addAdvisor(advisor);
serviceProxy.say();
System.out.println("\n-------------对目标对象引介增强功能方式1-----------------\n");
ILogService logService = (ILogService)proxyFactory.getProxy();
logService.logInfo();
proxyFactory.removeAdvisor(advisor);
System.out.println("\n-------------对目标对象引介增强功能方式2-----------------\n");
//创建advisor
advisor = new DeclareParentsAdvisor(IAddService.class, "org.spring.aop.introduction.service.*", AddService.class);
proxyFactory.addAdvisor(advisor);
IAddService addService = (IAddService)proxyFactory.getProxy();
addService.doSomething();
}
}
执行结果:
方式二:xml配置方式
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="org.spring.aop.introduction.service.UserService"/>
<!--<bean id="addService" class="org.spring.aop.introduction.advice.AddService"/>-->
<aop:config>
<aop:aspect>
<aop:declare-parents types-matching="org.spring.aop.introduction.service.*" implement-interface="org.spring.aop.introduction.advice.IAddService" default-impl="org.spring.aop.introduction.advice.AddService"/>
<!--<aop:declare-parents types-matching="org.spring.aop.service.UserService" implement-interface="org.spring.aop.service.IAddService" delegate-ref="addService"/>-->
</aop:aspect>
</aop:config>
</beans>
测试代码:
package org.spring.aop.introduction;
import org.spring.aop.introduction.advice.IAddService;
import org.spring.aop.introduction.service.IUserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XmlMainTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
applicationContext.start();
Object objService = applicationContext.getBean("userService");
IUserService userService = (IUserService)objService;
userService.say();
System.out.println("\n-------------对目标对象引介增强-----------------\n");
IAddService addService = (IAddService)objService;
addService.doSomething();
applicationContext.stop();
}
}
执行结果: