I.はじめに
AOP (Aspect Oriented Programming) はアスペクト指向プログラミングと呼ばれ、主にプログラム開発におけるシステムレベルの問題 (ログ、トランザクション、許可待ちなど) を解決するために使用されます。Struts2 のインターセプタ設計は、次の考えに基づいています。 AOP、これは古典的なものです。
AOP の関連概念:
(1) 横断的な懸念事項: どのメソッドがインターセプトされるか、およびインターセプト後にそれらにどのように対処するか、これらの懸念事項は横断的な懸念事項と呼ばれます (2) アスペクト (
アスペクト): 通常はクラスであり、エントリ ポイントと通知を定義できます
( 3 ) JointPoint (接続ポイント): プログラム実行プロセスの明確なポイント、通常はメソッド呼び出し。インターセプトされたポイント Spring ではメソッド型の接続ポイントのみがサポートされるため、Spring では接続ポイントはインターセプトされたメソッドを指します。実際、接続ポイントはフィールドまたはコンストラクターにすることもできます (4) アドバイス (通知)
:特定のエントリ ポイントで AOP によって実行される拡張処理には、before (pre)、after (post)、afterReturning (final)、afterThrowing (例外)、around (surround) が含まれます。 (5) ポイントカット (エントリ ポイント): 接続ポイントを取得します
。通知ありは主にプログラム内のポイントカット式の記述に反映されます
(6) ウィービング (weaving): アスペクトを対象オブジェクトに適用し、プロキシ オブジェクトの作成を引き起こすプロセス
(7) 導入 (introduction): Under を変更せずにコードの前提として、実行時にいくつかのメソッドまたはフィールドをクラスに動的に追加できる導入部分
(8) AOP プロキシ (AOP Proxy): AOP フレームワークによって作成されるオブジェクト、プロキシはターゲット オブジェクトの拡張です。Spring の AOP プロキシには、JDK ダイナミック プロキシと CGLIB プロキシがあり、前者はインターフェイスベース、後者はサブクラスベース (9)
ターゲット オブジェクト (Target Object): 接続ポイントを含むオブジェクト。アドバイスされたオブジェクトまたはプロキシされたオブジェクトとも呼ばれます。POJO
2. ソースコード分析
以下は、Spring Boot の AOP の使用に関するソース コード分析です。Spring Boot が pom インポートを追加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
次にアスペクトクラスを作成します
@Aspect
@Component
@EnableAspectJAutoProxy
public class ServiceAdvice {
@Pointcut("@annotation(aoptest.Monitor)")
public void annotation() {
}
@Pointcut("execution(public * aoptest.*.*(..))")
public void webLog(){}
@Before("webLog()")
public void beforeMethodAdviceA(JoinPoint joinPoint){
System.out.println("beforeMethodAdviceA");
}
@Before("annotation()")
public void beforeMethodAdviceAnnotation(JoinPoint joinPoint){
System.out.println("AdviceAnnotation");
}
@Before("annotation()")
public void beforeMethodAdviceAnnotation2(JoinPoint joinPoint){
System.out.println("AdviceAnnotation2");
}
@After("webLog()")
public void afterMethodAdviceB(JoinPoint joinPoint){
System.out.println("afterMethodAdviceB");
}
}
このようにして、対応するメソッドが実行される前にエンハンス処理が実行されます。
まず @EnableAspectJAutoProxy アノテーションを入力します。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//导入了AspectJAutoProxyRegistrar
}
AspectJAutoProxyRegistrar クラスがインポートされます。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
.........................
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
AnnotationAwareAspectJAutoProxyCreator オブジェクトは、AspectJAutoProxyRegistrar クラスを通じてインポートされます。AnnotationAwareAspectJAutoProxyCreator オブジェクトは、BeanPostProcessor インターフェースを実装します。
AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization メソッドは、Bean オブジェクトの作成時に実行されます。
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;
}
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;
}
getAdvicesAndAdvisorsForBean メソッドは、拡張メソッドを検索し、Bean に AOP 処理が必要かどうかを確認するために使用されます。
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//找到增强集合
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//找到实现了Advisor接口的类和标有@Aspect注解的类
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//判断当前的Bean是否有方法适配AOP
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
protected List<Advisor> findCandidateAdvisors() {
// 找到所有实现Advisor接口的类
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//找到所有标有@Aspect的类
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
public List<Advisor> buildAspectJAdvisors() {
.......................
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
...............
if (this.advisorFactory.isAspect(beanType)) {
...........................
//此方法将@Aspect类中的包含Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class的增强方法找到
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
...........
}
}
// 現在の Bean が AOP に適応するメソッドを持っているかどうかを判断します
。 List<Advisor> applyAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//判断是否适配
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
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;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
//判断当前类是否有方法适配当前adviors
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
canApply メソッドでは、pc.getClassFilter().matches(targetClass) メソッドがクイック マッチを実行します(クラスが一致するかどうかのみを確認します)。PointcutExpressionImpl クラスの CouldMatchJoinPointsInType メソッドが呼び出されます。
public boolean couldMatchJoinPointsInType(Class aClass) {
ResolvedType matchType = world.resolve(aClass.getName());
if (matchType.isMissing() && (world instanceof ReflectionWorld)) {
// Class is a generated class that cannot be 'looked up' via getResource.
// For example a proxy or lambda.
// Use the class itself in this case
matchType = ((ReflectionWorld)world).resolveUsingClass(aClass);
}
ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world);
boolean couldMatch = pointcut.fastMatch(info).maybeTrue();
if (MATCH_INFO) {
System.out.println("MATCHINFO: fast match for '" + this.expression + "' against '" + aClass.getName() + "': "
+ couldMatch);
}
return couldMatch;
}
特定のポイントカットを通じて fastMatch メソッドを呼び出します。たとえば、AnnotationPointcut は、メソッドに対応するアノテーションがあるかどうかを判断し、通常、最も多くの実行式を使用するメソッドが KindedPointcut の fastMatch メソッドに入ります。
このメソッドはまず @Pointcut を解析して、インターフェイスまたはエンティティ クラスに適応されているかどうかを確認します。Bean オブジェクトが適切かどうかを確認します。式が適切でない場合は、次の 2 つの状況が考えられます。
1. ポイントカットポイントがエンティティクラスの場合、まずポイントカットクラスとBeanクラスが同じかどうかを判断し、同じクラスでない場合は親クラスの互換性を確認します。
2. カット点がインタフェースの場合は、まずBeanクラスがカット点のクラスと同じかどうかを判定し、同じでない場合はそのインタフェースクラスがアスペクトに適応したクラスであるかどうかをチェックする。
つまり、サブクラス A、親クラス SuperA の場合、ポイントカットが親クラス A を定義している場合 @Pointcut("execution(public * aoptest.SuperA.*(..))")、ポイントカット呼び出しは任意のメソッドになります。サブクラス A の場合、AOP 拡張も実行されます。
マッチング方法を入力してください
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (hasIntroductions) {
return true;
}
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
getPointcutExpression メソッドは、カット ポイントに対応する解析クラスを取得するために使用されます。
private PointcutExpression obtainPointcutExpression() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
return this.pointcutExpression;
}
private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
PointcutParser parser = initializePointcutParser(classLoader);
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]);
}
return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
this.pointcutDeclarationScope, pointcutParameters);
}
InitializePointcutParser は、ポイントカット クラス解析オブジェクトを初期化します。
private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
PointcutParser parser = PointcutParser
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
SUPPORTED_PRIMITIVES, classLoader);
parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
return parser;
}
//其中SUPPORTED_PRIMITIVES包含各种pointcut类型,如EXECUTION、annotation等
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
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);
}
parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()), this.pointcutDeclarationScope, pointcutParameters) は、現在のアドバイザーに対応する PointCut 解析クラスを作成するために使用されます。
public PointcutExpression parsePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters)
throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
PointcutExpressionImpl pcExpr = null;
try {
//解析切点表达式,用于创建对应的PointCut解析类
Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
pc = concretizePointcutExpression(pc, inScope, formalParameters);
validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
} catch (ReflectionWorld.ReflectionWorldException rwEx) {
throw new IllegalArgumentException(rwEx.getMessage());
}
return pcExpr;
}
protected Pointcut resolvePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters) {
try {
PatternParser parser = new PatternParser(expression);
parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
//创建PointCut实现类
Pointcut pc = parser.parsePointcut(); // more correctly: parsePointcut(true)
validateAgainstSupportedPrimitives(pc, expression);
IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
pc = pc.resolve(resolutionScope);
return pc;
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
}
}
parser.parsePointcut() を通じて PointCut 実装クラスを作成する
public Pointcut parsePointcut() {
Pointcut p = parseAtomicPointcut();
if (maybeEat("&&")) {
p = new AndPointcut(p, parseNotOrPointcut());
}
if (maybeEat("||")) {
p = new OrPointcut(p, parsePointcut());
}
return p;
}
private Pointcut parseAtomicPointcut() {
if (maybeEat("!")) {
int startPos = tokenSource.peek(-1).getStart();
Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
return p;
}
if (maybeEat("(")) {
Pointcut p = parsePointcut();
eat(")");
return p;
}
//如果Adviors的表达式是@开头,则创建AnnotationPointcut类
if (maybeEat("@")) {
int startPos = tokenSource.peek().getStart();
Pointcut p = parseAnnotationPointcut();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
int startPos = tokenSource.peek().getStart();
//之前实例类创建的是 @Before("annotation()") 和 @Before("webLog()")会进入parseSinglePointcut方法寻找
Pointcut p = parseSinglePointcut();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
public Pointcut parseSinglePointcut() {
int start = tokenSource.getIndex();
IToken t = tokenSource.peek();
Pointcut p = t.maybeGetParsedPointcut();
if (p != null) {
tokenSource.next();
return p;
}
String kind = parseIdentifier();
//这里也没有适配的,我们传入的是annotation() 和 webLog()
if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) {
p = parseKindedPointcut(kind);
} else if (kind.equals("args")) {
p = parseArgsPointcut();
} else if (kind.equals("this")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("target")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("within")) {
p = parseWithinPointcut();
} else if (kind.equals("withincode")) {
p = parseWithinCodePointcut();
} else if (kind.equals("cflow")) {
p = parseCflowPointcut(false);
} else if (kind.equals("cflowbelow")) {
p = parseCflowPointcut(true);
} else if (kind.equals("adviceexecution")) {
eat("(");
eat(")");
p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("handler")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new HandlerPointcut(typePat);
} else if (kind.equals("lock") || kind.equals("unlock")) {
p = parseMonitorPointcut(kind);
} else if (kind.equals("initialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.Initialization, sig);
} else if (kind.equals("staticinitialization")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION,
ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("preinitialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.PreInitialization, sig);
} else if (kind.equals("if")) {
// - annotation style only allows if(), if(true) or if(false)
// - if() means the body of the annotated method represents the if expression
// - anything else is an error because code cannot be put into the if()
// - code style will already have been processed and the call to maybeGetParsedPointcut()
// at the top of this method will have succeeded.
eat("(");
if (maybeEatIdentifier("true")) {
eat(")");
p = new IfPointcut.IfTruePointcut();
} else if (maybeEatIdentifier("false")) {
eat(")");
p = new IfPointcut.IfFalsePointcut();
} else {
if (!maybeEat(")")) {
throw new ParserException(
"in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method",
t);
}
// TODO - Alex has some token stuff going on here to get a readable name in place of ""...
p = new IfPointcut("");
}
} else {
boolean matchedByExtensionDesignator = false;
// see if a registered handler wants to parse it, otherwise
// treat as a reference pointcut
for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
if (pcd.getDesignatorName().equals(kind)) {
p = parseDesignatorPointcut(pcd);
matchedByExtensionDesignator = true;
}
}
if (!matchedByExtensionDesignator) {
tokenSource.setIndex(start);
//annotation() 和 webLog() 会进入这里
p = parseReferencePointcut();
}
}
return p;
}
アドバイザは @Before("webLog()") と @Before("annotation()") を作成するため、最後まで parseReferencePointcut に進み、参照タイプの PointCut を作成します。
引き続き前のresolvePointcutExpressionメソッドに戻ります
protected Pointcut resolvePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters) {
try {
PatternParser parser = new PatternParser(expression);
parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
//创建完ReferencePointcut类后
Pointcut pc = parser.parsePointcut(); // more correctly: parsePointcut(true)
validateAgainstSupportedPrimitives(pc, expression);
IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
//这里会解析ReferencePointcut这正的对应@PointCut的方法,找到真正的Pointcut解析类
pc = pc.resolve(resolutionScope);
return pc;
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
}
}
ここでは @PointCut に対応する ReferencePointcut のメソッドを解析し、実際の Pointcut 解析クラスを見つけます。
public final Pointcut resolve(IScope scope) {
assertState(SYMBOLIC);
Bindings bindingTable = new Bindings(scope.getFormalCount());
IScope bindingResolutionScope = scope;
if (typeVariablesInScope.length > 0) {
bindingResolutionScope = new ScopeWithTypeVariables(typeVariablesInScope, scope);
}
//根据引用切点方法名称,找到对应的point里真正的要创建的PointCut
this.resolveBindings(bindingResolutionScope, bindingTable);
bindingTable.checkAllBound(bindingResolutionScope);
this.state = RESOLVED;
return this;
}
public void resolveBindings(IScope scope, Bindings bindings) {
//找到对应名字的pointCut
ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name);
............
}
public ResolvedPointcutDefinition findPointcut(String name) {
for (Iterator<ResolvedMember> i = getPointcuts(); i.hasNext();) {
ResolvedPointcutDefinition f = (ResolvedPointcutDefinition) i.next();
// the ResolvedPointcutDefinition can be null if there are other problems that prevented its resolution
if (f != null && name.equals(f.getName())) {
return f;
}
}
}
//getPointcuts()方法会调用PointcutGetter.get 方法,会调用getDeclaredPointcuts()
private static class PointcutGetter implements Iterators.Getter<ResolvedType, ResolvedMember> {
@Override
public Iterator<ResolvedMember> get(ResolvedType o) {
return Iterators.array(o.getDeclaredPointcuts());
}
}
Java15ReflectionBasedReferenceTypeDelegate.getDeclaredPointcuts メソッドが呼び出されます。
public ResolvedMember[] getDeclaredPointcuts() {
if (pointcuts == null) {
Pointcut[] pcs = this.myType.getDeclaredPointcuts();
......
}
for (int i = 0; i < pcs.length; i++) {
String pcExpr = pcs[i].getPointcutExpression().toString();
org.aspectj.weaver.patterns.Pointcut pc = parser.resolvePointcutExpression(pcExpr, getBaseClass(), parameters[i]);
((ResolvedPointcutDefinition) pointcuts[i]).setParameterNames(pnames);
((ResolvedPointcutDefinition) pointcuts[i]).setPointcut(pc);
}
}
public Pointcut[] getDeclaredPointcuts() {
if (declaredPointcuts != null) return declaredPointcuts;
List<Pointcut> pointcuts = new ArrayList<Pointcut>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Pointcut pc = asPointcut(method);
if (pc != null) pointcuts.add(pc);
}
Pointcut[] ret = new Pointcut[pointcuts.size()];
pointcuts.toArray(ret);
declaredPointcuts = ret;
return ret;
}
private Pointcut asPointcut(Method method) {
org.aspectj.lang.annotation.Pointcut pcAnn = method.getAnnotation(org.aspectj.lang.annotation.Pointcut.class);
if (pcAnn != null) {
String name = method.getName();
if (name.startsWith(ajcMagic)) {
// extract real name
int nameStart = name.indexOf("$$");
name = name.substring(nameStart +2,name.length());
int nextDollar = name.indexOf("$");
if (nextDollar != -1) name = name.substring(0,nextDollar);
}
return new PointcutImpl(name,pcAnn.value(),method,AjTypeSystem.getAjType(method.getDeclaringClass()),pcAnn.argNames());
} else {
return null;
}
}
Java15ReflectionBasedReferenceTypeDelegate を使用して、Advisors メソッドに対応する Aspect クラス内の @Pointcut アノテーションが付けられたメソッドを検索します。次に、これらのメソッドに対して parser.resolvePointcutExpression(pcExpr, getBaseClass(),parameters[i]) を再度実行して解析します。対応する PointCut クラスを作成します。
マッチング方法に戻る
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (hasIntroductions) {
return true;
}
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
getTargetShadowMatchメソッドを実行します。このメソッドの機能は主に、Bean にこれらの Advisor に一致するメソッドがあるかどうかを判断することです。
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
..............
try {
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
}
}
public ShadowMatch matchesMethodExecution(Method aMethod) {
ShadowMatch match = matchesExecution(aMethod);
if (MATCH_INFO && match.maybeMatches()) {
System.out.println("MATCHINFO: method execution match on '" + aMethod + "' for '" + this.expression + "': "
+ (match.alwaysMatches() ? "YES" : "MAYBE"));
}
return match;
}
private ShadowMatchImpl getShadowMatch(Shadow forShadow) {
//之前创建的对应PointCut进行匹配判断
org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow);
Test residueTest = Literal.TRUE;
ExposedState state = getExposedState();
if (match.maybeTrue()) {
residueTest = pointcut.findResidue(forShadow, state);
}
ShadowMatchImpl sm = new ShadowMatchImpl(match, residueTest, state, parameters);
sm.setMatchingContext(this.matchContext);
return sm;
}
マッチング方法 matchInternal を入力します
public final FuzzyBoolean match(Shadow shadow) {
if (shadow.shadowId == lastMatchedShadowId) {
return lastMatchedShadowResult;
}
FuzzyBoolean ret;
// this next test will prevent a lot of un-needed matching going on....
if (shadow.getKind().isSet(couldMatchKinds())) {
ret = matchInternal(shadow);
} else {
ret = FuzzyBoolean.NO;
}
lastMatchedShadowId = shadow.shadowId;
lastMatchedShadowResult = ret;
return ret;
}
ここでは @annotation アノテーションを例として取り上げますが、残りも同様です。
protected FuzzyBoolean matchInternal(Shadow shadow) {
AnnotatedElement toMatchAgainst = null;
Member member = shadow.getSignature();
ResolvedMember rMember = member.resolve(shadow.getIWorld());
...................
annotationTypePattern.resolve(shadow.getIWorld());
return annotationTypePattern.matches(toMatchAgainst);
}
public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
if (!isForParameterAnnotationMatch()) {
boolean checkSupers = false;
if (getResolvedAnnotationType().isInheritedAnnotation()) {
if (annotated instanceof ResolvedType) {
checkSupers = true;
}
}
if (annotated.hasAnnotation(annotationType)) {
if (annotationType instanceof ReferenceType) {
ReferenceType rt = (ReferenceType) annotationType;
if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) {
rt.getWorld()
.getMessageHandler()
.handleMessage(
MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION,
annotationType, annotated), getSourceLocation()));
return FuzzyBoolean.NO;
}
}
................
}
ExactAnnotationTypePattern.matches メソッドが呼び出され、アノテーションがメソッドのアノテーションと一致するかどうかが判断されます。一致する場合は、メソッドを AOP で拡張する必要があります。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
canApply からは、Bean 内のいずれかのメソッドが Advoir を満たす場合、直接 true を返すことがわかります。これは、1 つのメソッドが一致したとしても、このクラスは動的プロキシを作成する必要があるため、当面はこの Bean 内の他のメソッドをループで判定する必要がないためです。
postProcessAfterInitialization メソッド内
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;
}
createProxy によって作成された動的プロキシは、特定のメソッドが Advisor に適しているかどうかを再度判断し、キャッシュに入れます。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
作成されるエージェントは主に JdkDynamicAopProxy と CglibAopProxy の 2 種類で、例として JdkDynamicAopProxy の invoke メソッドを取り上げます。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
..........................
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
..........................
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable 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;
}
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
if (match) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
前回の検証では、このクラスのメソッドが @Apsect に準拠している場合、動的プロキシが直接作成されることが確認されているためです。ここで判断されるのは、特定の呼び出し方法を強化する必要があるかどうかです。そして、AOP を増やすために必要な機能強化と対応するメソッドをキャッシュに入れます。
以上がAOPマッチングソースコード解析です。
AOP 一致ソース コード解析の概要:
1. @EnableAspectJAutoProxy アノテーションを介して AnnotationAwareAspectJAutoProxyCreator クラスをインポートすると、AnnotationAwareAspectJAutoProxyCreator クラスが BeanPostProcessor インターフェースを実装します。postProcessAfterInitialization メソッドで、@Apsect を含み Advisors インターフェイスを実装するクラスを探します。@Around、@Before、@After、@AfterReturning、@AfterThrowing を含むすべての拡張メソッドを検索します。
2. AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply メソッドを通じて照合します。マッチング手順は次のとおりです。
1) 照合用に AspectJExpressionPointcut.matches メソッドを入力します。AspectJExpressionPointcut.buildPointcutExpression メソッドを呼び出して、分析式を構築します。まず、@Before("webLog()") や @Before("annotation()") など、@Aound、@Before、@After などによって参照されるポイントカット メソッド名解析のために、ReferencePointcut クラスが最初に作成されます。 。
2) 次に、ReferencePointcut.resolveBindings メソッドを呼び出して、参照名が webLog() および annotation() である @PointCut メソッドを見つけます。そして最後に Java15ReflectionBasedReferenceTypeDelegate.getDeclaredPointcuts メソッドを呼び出すことによって、見つかった @PointCut が再度解析されて、実際の Pointcut 解析クラスが取得されます。
3) 以前に作成した ReferencePointcut は、concreteize1 メソッドを再度呼び出して、実際の Pointcut 解析クラスを見つけます。次にマッチングを行います。
3. Bean 内のいずれかのメソッドが Advoir を満たす場合は、直接 true を返します。これは、メソッドが正常に一致した場合でも、クラスで動的プロキシを作成する必要があるためです。
4. 最後に、特定の呼び出しメソッドを動的プロキシを通じて拡張する必要があるかどうかを判断します。そして、AOP を増やすために必要な拡張機能と対応するメソッドをキャッシュに入れます。