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