編程式創建@AspectJ代理

原文路徑:https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-aspectj-programmatic

In addition to declaring aspects in your configuration by using either <aop:config> or <aop:aspectj-autoproxy>, it is also possible to programmatically create proxies that advise target objects. Here, we want to focus on the ability to automatically create proxies by using @AspectJ aspects.

You can use the org.springframework.aop.aspectj.annotation.AspectJProxyFactory class to create a proxy for a target object that is advised by one or more @AspectJ aspects. The basic usage for this class is very simple, as the following example shows:

// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();

創建maven項目並引入如下的依賴包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

首先創建實體類、註解和業務類

package com.example.managingtransactions.proxy.programmatic.entity;


public class User {

    private Long userId;

    private String userName;

    public User(Long userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}
package com.example.managingtransactions.proxy.programmatic.entity;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Usage {
}
package com.example.managingtransactions.proxy.programmatic.service;


import com.example.managingtransactions.proxy.programmatic.entity.Usage;
import com.example.managingtransactions.proxy.programmatic.entity.User;

public interface UserService {

    User findUser(Long userId);
}
package com.example.managingtransactions.proxy.programmatic.service;

import com.example.managingtransactions.proxy.programmatic.entity.Usage;
import com.example.managingtransactions.proxy.programmatic.entity.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Usage
    @Override
    public User findUser(Long userId) {
        return new User(userId, "TomCat");
    }
}

創建兩個Aspect類

package com.example.managingtransactions.proxy.programmatic.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SecurityManager {

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.someapp.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.example.managingtransactions.proxy.programmatic.service..*)")
    public void inServiceLayer() {}

    @Before("inServiceLayer()")
    public void before(){
        System.out.println("============SecurityManager========before========");
    }

}
package com.example.managingtransactions.proxy.programmatic.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UsageTracker {

    @Around("@annotation(com.example.managingtransactions.proxy.programmatic.entity.Usage)")
    public Object tracker(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long end = System.currentTimeMillis();
            System.out.println("耗時" + (end - start) + "ms");
        }

    }
}

創建Spring配置類和啓動類

package com.example.managingtransactions.proxy.programmatic;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class BootConfig {
}
package com.example.managingtransactions.proxy.programmatic;

import com.example.managingtransactions.proxy.programmatic.aspect.SecurityManager;
import com.example.managingtransactions.proxy.programmatic.aspect.UsageTracker;
import com.example.managingtransactions.proxy.programmatic.entity.User;
import com.example.managingtransactions.proxy.programmatic.service.UserService;
import com.example.managingtransactions.proxy.programmatic.service.UserServiceImpl;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringStartMain {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BootConfig.class);
        UsageTracker usageTracker = applicationContext.getBean(UsageTracker.class);
        // create a factory that can generate a proxy for the given target object
        AspectJProxyFactory factory = new AspectJProxyFactory(new UserServiceImpl());

        // add an aspect, the class must be an @AspectJ aspect
        // you can call this as many times as you need with different aspects
        factory.addAspect(SecurityManager.class);

        // you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
        factory.addAspect(usageTracker);

        // now get the proxy object...
        UserService proxy = factory.getProxy();

        User user = proxy.findUser(2563L);

        System.out.println(user.getUserName());

        applicationContext.close();
    }
}

測試結果如下:
在這裏插入圖片描述
查看代理對象信息如下:
在這裏插入圖片描述
執行如下代理的方法時

// now get the proxy object...
UserService proxy = factory.getProxy();
User user = proxy.findUser(2563L);

首先進入JdkDynamicAopProxy類的invoke方法
在這裏插入圖片描述

主要的邏輯1:獲取增強攔截鏈

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
/**
 * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
 * for the given method, based on this configuration.
 * @param method the proxied method
 * @param targetClass the target class
 * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
 */
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;
}
根據方法進行緩存 使用攔截器鏈構造工廠來真實生成攔截器鏈

在這裏插入圖片描述
看一下這個advisorChainFactory是怎麼來的
在這裏插入圖片描述

通過AdvisorChainFactory獲取攔截器增強鏈
@Override
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 com.example.managingtransactions.proxy.programmatic.service.UserServiceImpl
	Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
	Boolean hasIntroductions = null;

	for (Advisor advisor : advisors) {
		if (advisor instanceof PointcutAdvisor) {
			// Add it conditionally. 通常邏輯 轉變成PointcutAdvisor 
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			// PreFiltered默認爲false
			// 1. 獲取PointcutAdvisor中的ClassFilter進行過濾
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
				// 2. 獲取PointcutAdvisor中的MethodMatcher進行匹配  
				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) {
					// 3. 如果方法也匹配的話 就獲取方法攔截器 
					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;
}

在這裏插入圖片描述
在這裏插入圖片描述
AspectJ expression匹配邏輯

AspectJExpressionPointcut專門用於處理AspectJ expression,是否與當前方法匹配
在這裏插入圖片描述

// 當前類:org.springframework.aop.aspectj.AspectJExpressionPointcut
// method = public abstract com.example.managingtransactions.proxy.programmatic.entity.User com.example.managingtransactions.proxy.programmatic.service.UserService.findUser(java.lang.Long)
// targetClass = class com.example.managingtransactions.proxy.programmatic.service.UserServiceImpl
// hasIntroductions = false
@Override
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
	// 獲取切面語句
	obtainPointcutExpression();
	ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

	// Special handling for this, target, @this, @target, @annotation
	// in Spring - we can optimize since we know we have exactly this class,
	// and there will never be matching subclass at runtime.
	if (shadowMatch.alwaysMatches()) {
		return true;
	}
	else if (shadowMatch.neverMatches()) {
		return false;
	}
	else {
		// the maybe case
		if (hasIntroductions) {
			return true;
		}
		// A match test returned maybe - if there are any subtype sensitive variables
		// involved in the test (this, target, at_this, at_target, at_annotation) then
		// we say this is not a match as in Spring there will never be a different
		// runtime subtype.
		RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
		return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
	}
}

在這裏插入圖片描述
深度匹配

private ShadowMatch getTargetShadowMatch(Method method, Class<?> targetClass) {
	Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
	if (targetMethod.getDeclaringClass().isInterface()) {
		// Try to build the most specific interface possible for inherited methods to be
		// considered for sub-interface matches as well, in particular for proxy classes.
		// Note: AspectJ is only going to take Method.getDeclaringClass() into account.
		Set<Class<?>> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass);
		if (ifcs.size() > 1) {
			try {
				Class<?> compositeInterface = ClassUtils.createCompositeInterface(
						ClassUtils.toClassArray(ifcs), targetClass.getClassLoader());
				targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface);
			}
			catch (IllegalArgumentException ex) {
				// Implemented interfaces probably expose conflicting method signatures...
				// Proceed with original target method.
			}
		}
	}
	// 由於當前非接口 所以走這裏
	return getShadowMatch(targetMethod, method);
}

在這裏插入圖片描述
在這裏插入圖片描述

AdvisorAdapterRegistry解析Advisor獲取方法攔截器

在這裏插入圖片描述
在這裏插入圖片描述
從以上關係圖中不難看出,AdvisorAdapter用於從Advisor中獲取MethodInterceptor。在DefaultAdvisorAdapterRegistry註冊了常用的AdvisorAdapter,通過遍歷AdvisorAdapter,解析出Advisor中的方法攔截器。

比如針對於前置處理器的AdvisorAdapter

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

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

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

}

整體處理邏輯

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
	List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
	Advice advice = advisor.getAdvice();
	if (advice instanceof MethodInterceptor) {
		interceptors.add((MethodInterceptor) advice);
	}
	for (AdvisorAdapter adapter : this.adapters) {
		if (adapter.supportsAdvice(advice)) {
			interceptors.add(adapter.getInterceptor(advisor));
		}
	}
	if (interceptors.isEmpty()) {
		throw new UnknownAdviceTypeException(advisor.getAdvice());
	}
	return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

在這裏插入圖片描述
在這裏插入圖片描述
環繞通知也是作爲MethodInterceptor來處理的
在這裏插入圖片描述
如果當前的切面語句需要運行時判斷匹配的,比如註解,則需要再次進行包裝
在這裏插入圖片描述
在這裏插入圖片描述

根據獲取到的所有攔截器並緩存

在這裏插入圖片描述

根據代理對象、目標對象以及攔截器構造MethodInvocation對象
// We need to create a method invocation...
MethodInvocation invocation =
		new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();

ReflectiveMethodInvocation是針對代理對象、目標對象以及攔截器鏈的包裝,當真正發起調用的時候,會遍歷裏面的攔截器鏈發起調用。
比較重要的兩個參數

/**
 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
 * that need dynamic checks.
 */
protected final List<?> interceptorsAndDynamicMethodMatchers;

/**
 * Index from 0 of the current interceptor we're invoking.
 * -1 until we invoke: then the current interceptor.
 */
private int currentInterceptorIndex = -1;

在這裏插入圖片描述
核心邏輯

@Override
@Nullable
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;
		Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
		// 運行時動態匹配
		if (dm.methodMatcher.matches(this.method, 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);
	}
}

ExposeInvocationInterceptor攔截器邏輯:

// 通過線程局部變量進行當前調用的暴露
private static final ThreadLocal<MethodInvocation> invocation =
		new NamedThreadLocal<>("Current AOP method invocation");

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	MethodInvocation oldInvocation = invocation.get();
	invocation.set(mi);
	try {
		return mi.proceed();
	}
	finally {
		invocation.set(oldInvocation);
	}
}

在這裏插入圖片描述
在這裏插入圖片描述
MethodBeforeAdviceInterceptor攔截器邏輯

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
	// 首選執行before邏輯
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
	// 執行目標方法
	return mi.proceed();
}

在這裏插入圖片描述

@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
	invokeAdviceMethod(getJoinPointMatch(), null, null);
}

運行時動態匹配->around+annotation
在這裏插入圖片描述
AspectJAroundAdvice
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章