Spring AOP使用与原理

news2024/11/23 11:22:42

AOP介绍

SpringAOP核心概念
上述中已经出现的关键词有Advice(顶级的通知类/拦截器)、MethodInvocation(方法连接点)、MethodInterceptor(方法拦截器)
SpringAOP在此基础上又增加了几个类,丰富了AOP定义及使用概念,包括
Advisor:包含通知(拦截器),Spring内部使用的AOP顶级接口,还需要包含一个aop适用判断的过滤器,考虑到通用性,过滤规则由其子接口定义,例如IntroductionAdvisor和PointcutAdvisor,过滤器用于判断bean是否需要被代理
Pointcut: 切点,属于过滤器的一种实现,匹配过滤哪些类哪些方法需要被切面处理,包含一个ClassFilter和一个MethodMatcher,使用PointcutAdvisor定义时需要
ClassFilter:限制切入点或引入点与给定目标类集的匹配的筛选器,属于过滤器的一种实现。过滤筛选合适的类,有些类不需要被处理
MethodMatcher:方法匹配器,定义方法匹配规则,属于过滤器的一种实现,哪些方法需要使用AOP

AOP使用方法

配置Advisor

这一步对SpringAOP使用者很关键,决定了我们如何定义配置Advisor,即SpringAOP和Aspectj,实际使用配置AOP方式有多种,还区分xml和注解,最终转化处理时我认为只分为这两种。其中Aspectj方式配置AOP应该是最常见应用最广泛的用法了。

前面提到Aspectj是一种静态代理,而SpringAOP是动态代理。但Aspectj的一套定义AOP的API非常好,直观易用。所以Spring引入了Aspectj,但只使用部分注解用来定义配置AOP,在获取Advisor阶段用来生成Advisor,与后面的代理生成和代理增强执行无关!
最少需要定义三个类,一个Advisor的实现类,一个Advice实现类(拦截器),一个aop适配过滤器(这里使用的Advisor为派生的PointcutAdvisor ,需要定义PointCut切点)。可以增加一个注解用于AOP埋点,需要给bean哪个方法进行切面,则方法上加上该注解。
Advisor:MyAdvisor,返回一个Advice,
Advice:MyInterceptAdvice,拦截器,invoke方法中可以添加切面逻辑代码
PointCut: MyPointCut,切点,匹配过滤出需要切面的类及方法,查找方法头注解了MyAnnotation的方法。
埋点注解:MyAnnotation
MyAdvisor.java

@Component
public class MyAdvisor implements PointcutAdvisor {

    @Override
    public Advice getAdvice() {
        return new MyInterceptAdvice();
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }

    @Override
    public Pointcut getPointcut() {
        return new MyPointCut();
    }
}

MyInterceptAdvice.java

public class MyInterceptAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("go go go MyAdvisor process!!!");
        return invocation.proceed();
    }
}

MyPointCut.java

public class MyPointCut implements Pointcut {

    @Override
    public ClassFilter getClassFilter() {
        return new MyClassFilter();
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new MyMethodMatcher();
    }

    private class MyMethodMatcher implements MethodMatcher {
        @Override
        public boolean matches(Method method, Class<?> targetClass) {
            Annotation[] annoArray = method.getDeclaredAnnotations();
            if (annoArray == null || annoArray.length == 0) {
                return false;
            }

            for (Annotation annotation : annoArray) {
                if (annotation.annotationType() == MyAnnotation.class) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public boolean isRuntime() {
            return false;
        }

        @Override
        public boolean matches(Method method, Class<?> targetClass, Object... args) {
            return false;
        }
    }

    private class MyClassFilter implements ClassFilter {

        @Override
        public boolean matches(Class<?> clazz) {
            return AnnotationUtils.isCandidateClass(clazz, MyAnnotation.class);
        }
    }
}

再定义一个测试类

@Component
public class TestService {

    @MyAnnotation
    public void test() {
        System.out.println("test!");
    }
}

运行方法测试发现aop生效

使用Aspectj间接配置Advisor

一个类就可以了,定义切点和增强方法

@Aspect
@Component
public class AopConfig {

    @Pointcut("execution(* cn.nec.aop.cnnecaop.service.*.*(..))")
    public void pointCut() {}
  
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoin) {
        System.out.println("go go go MyAspectJ process!!!");
        Object obj = null;
        try {
            obj = joinPoin.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return obj;
    }
}


注解的形式,实际上最终还是被封装成Advice接口

注解AOP的几个执行时机

  • @Before
    • 前置通知:目标方法之前执行
  • MethodBeforeAdviceInterceptor
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}
  • @After
    • 后置通知:目标方法之后执行(始终执行)
  • AspectJAfterAdvice
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
  • @AfterReturning
    • 返回通知:执行方法结束前执行(异常不执行)
    • AfterReturningAdviceInterceptor
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
  • @AfterThrowing
    • 异常通知:出现异常的时候执行
    • AspectJAfterThrowingAdvice
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
  • @Around
    • 环绕通知:环绕目标方法执行(执行前后都在一个方法中自定义)
    • AspectJAroundAdvice
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
		ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
		JoinPointMatch jpm = getJoinPointMatch(pmi);
		return invokeAdviceMethod(pjp, jpm, null, null);
	}

around方法中,不会有自动执行process的逻辑。因此,需要在自己定义的方法中自行执行

AOP原理分析

AOP入口

SpringAOP是对bean的一种扩展,是后处理器的一种处理。Spring bean在 执行初始化方法前后,会使用所有BeanPostProcessor对bean进行特殊处理。Aop代理即是一种对bean特殊处理。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }
   else {
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }

   return wrappedBean;
}


此时用于代理的BeanPostProcessor登场,对需要代理的bean进行代理
对应的BeanPostProcessor为AbstractAutoProxyCreator的子类,执行AbstractAutoProxyCreator.postProcessAfterInitialization()。

@Override
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;
}

earlyProxyReferences中存放的是循环依赖提前暴露bean生成的代理
注意!如果earlyProxyReferences已经存在了这个bean,代表该bean已经被提前暴露生成过了代理,那么不再进行重复代理!
紧接着进入wrapIfNecessary


/**
 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 * @param bean the raw bean instance
 * @param beanName the name of the bean
 * @param cacheKey the cache key for metadata access
 * @return a proxy wrapping the bean, or the raw bean instance as-is
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
    //advisedBeans的对应value为fasle,则代表不需要代理
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
       //对于本身就是advisorBean和需要跳过的bean不进行代理
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

	// Create proxy if we have advice.
	// 获取拦截器,及Advisor,看样子也可以获取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;
}

可以看到代理生成分了两步,获取AdvicesAndAdvisors,然后生成代理对象
在这里插入图片描述
getAdvicesAndAdvisorsForBean方法

@Override
	@Nullable
	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) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

获取所有的Advisor

findCandidateAdvisors用于获取所有的Advisor,默认获取直接配置的Advisor。即实现了Advisor的所有bean。

advisors.add(this.beanFactory.getBean(name, Advisor.class));

AnnotationAwareAspectJAutoProxyCreator重写了findCandidateAdvisors,不仅可以获取直接配置得Advisor,还可以获取用AspectJ间接定义的Advisor,即把AspectJ定义的bean转化为Advisor。
所以使用AnnotationAwareAspectJAutoProxyCreator可以同时支持两种配置AOP方式!

获取AspectJ注解间接定义的Advisor

if (this.advisorFactory.isAspect(beanType)) {
      aspectNames.add(beanName);
      AspectMetadata amd = new AspectMetadata(beanType, beanName);
      if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
         MetadataAwareAspectInstanceFactory factory =
               new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
         List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
         if (this.beanFactory.isSingleton(beanName)) {
            this.advisorsCache.put(beanName, classAdvisors);
         }
         else {
            this.aspectFactoryCache.put(beanName, factory);
         }
         advisors.addAll(classAdvisors);
      }
      else {
         // Per target or per this.
         if (this.beanFactory.isSingleton(beanName)) {
            throw new IllegalArgumentException("Bean with name '" + beanName +
                  "' is a singleton, but aspect instantiation model is not singleton");
         }
         MetadataAwareAspectInstanceFactory factory =
               new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
         this.aspectFactoryCache.put(beanName, factory);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
}

查找过滤合适的Advisor

findAdvisorsThatCanApply用于查找可用的Advisor,遍历所有的Advisor,使用Advisor的PointCut执行匹配方法,对bean Class的方法挨个进行匹配,能匹配到说明该Advisor合格,加入到返回结果中,这里只展示了其中一种切点的处理逻辑–PointcutAdvisor。

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 ?
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }

   return false;
}

分别根据每个advisor pointCut的classFilter和MyMethodMatcher进行匹配。

过滤规则扩展

直接实现Advisor,和实现Advisor的扩展接口有什么区别?
Advisor是顶级接口,其实器残缺的,没有给出过滤匹配的方式
扩展接口IntroductionAdvisor和PointcutAdvisor。

Advisor没有过滤匹配规则,会匹配所有bean(不包含特殊bean)
IntroductionAdvisor给出了class类型过滤方式,会匹配限定类型的bean
PointcutAdvisor给出了class类型+方法匹配过滤方式,会匹配限定类型限定方法的bean。

显然PointcutAdvisor功能最强大,适用性和实用性最强

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;
	}
}

对于Aspect扩展

	extendAdvisors(eligibleAdvisors);
@Override
	protected void extendAdvisors(List<Advisor> candidateAdvisors) {
		AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
	}
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
		// Don't add advisors to an empty list; may indicate that proxying is just not required
		if (!advisors.isEmpty()) {
			boolean foundAspectJAdvice = false;
			for (Advisor advisor : advisors) {
				// Be careful not to get the Advice without a guard, as this might eagerly
				// instantiate a non-singleton AspectJ aspect...
				if (isAspectJAdvice(advisor)) {
					foundAspectJAdvice = true;
					break;
				}
			}
			if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
				advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
				return true;
			}
		}
		return false;
}

如果是sAspectJAdvice,那么添加一个advisor,ExposeInvocationInterceptor这个的用处后面看

生成代理对象及代理执行

使用可用的Advisor和当前bean对象生成动态代理对象

	if (specificInterceptors != DO_NOT_PROXY) {
        //代码当前bean被代理
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
ProxyFactory proxyFactory = new ProxyFactory();
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.getProxy(getProxyClassLoader());createAopProxy().getProxy(classLoader);

createAopProxy用于创建代理类,进入源码可以看到支持两种代理方式,其中JDK动态代理需要bean实现接口。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

注意这里有有个坑,不能仅仅看类是否有继承接口,还要看一个配置
proxyTargetClass
true
目标对象实现了接口 – 使用CGLIB代理机制
目标对象没有接口(只有实现类) – 使用CGLIB代理机制
false
目标对象实现了接口 – 使用JDK动态代理机制(代理所有实现了的接口)
目标对象没有接口(只有实现类) – 使用CGLIB代理机制
在这里插入图片描述

可见如果proxyTargetClass是true,那么不管什么类都会使用cglib代理,只有目标类本身就接口/代理类才会使用jdk代理
在这里插入图片描述

JDK动态代理

public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

是不是熟悉了,和手动定义JDK动态代理一样,对于jdk动态代理的拦截器InvocationHandler就是其本身
核心就是
Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, invocationHandler)。
再来看看核心方法invoke

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
   // We can skip creating a MethodInvocation: just invoke the target directly
   // Note that the final invoker must be an InvokerInterceptor so we know it does
   // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
   Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
   retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
   // 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();
}

从Advisor中获取拦截器,然后生成一个连接点(ReflectiveMethodInvocation),包含链接器和代理信息,执行连接点的proceed方法,会链式调用拦截器,执行所有的切面代码。

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);
   }
}

 @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("go go go MyAdvisor process!!!");
        return invocation.proceed();
    }

切面执行完毕后会继续调用ReflectiveMethodInvocation的invoke方法,currentInterceptorIndex自增,执行匹配到的Advice,执行完毕执行invokeJoinpoint方法

注意点

jdk代理是对接口做的代理,匹配advice时,时根据接口
例如根据注解匹配,注解值放在子类上时无效的,需要放到接口上
例如

public interface ITestService {
     @MyAnnotation
     void test();
}

ExposeInvocationInterceptor的作用是什么

我们前面知道了,如果有注解间接定义的advisor,那么会添加一个ExposeInvocationInterceptor到首位
第一个执行的拦截器就会是ExposeInvocationInterceptor
从英文名字,顾名思义,暴露调用器的拦截器。
其就是起了暴露一个调用器作用的拦截器。
1、那么其暴露了什么调用器?
2、是如何暴露的,通过什么方法实现?
3、暴露给谁?
4、在什么时候起到拦截作用?
调用链首先调用此ExposeInvocationInterceptor拦截器的invoke方法,将MethodInvocation mi 设置到
ThreadLocal invocation 里面

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

至于为什么要把MethodInvocation塞到threadLocal中 invocation中
是因为通过注解@Aspectj定义的advise执行时,其对应的接口没有MethodInvocation这个参数
例如前置执行器

@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

但是又需要使用,因此需要使用threadLocal进行传递

@Override
	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
@Nullable
	protected JoinPointMatch getJoinPointMatch() {
		MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		return getJoinPointMatch((ProxyMethodInvocation) mi);
	}

那么获取joinpoint是为了干什么
例如有一个前置执行方法

  @Before(value = "execution(public * test(int)) && args(a)")
    public void before(JoinPoint joinPoint,int a){
        System.out.println("------------------hello 方法执行前-------------------");
    }

需要传参,joinPoint就是从MethodInvocation中获取

CGLIB动态代理类

	Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return createProxyClassAndInstance(enhancer, callbacks);

与jdk自然不同,用的是cglib创建代理的方式
生效的callback可能有多个,这里看 DynamicUnadvisedInterceptor

	@Override
		@Nullable
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// We can skip creating a MethodInvocation: just invoke the target directly.
					// Note that the final invoker must be an InvokerInterceptor, so we know
					// it does nothing but a reflective operation on the target, and no hot
					// swapping or fancy proxying.
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

执行proceed的流程与jdk的流程基本相同。
执行时生成的连接点为CglibMethodInvocation,是JDK动态代理连接点ReflectiveMethodInvocation的子类,执行的还是ReflectiveMethodInvocation的proceed方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21150.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于密度的划分、DBSCAN(机器学习)

目录 居于密度的划分 DBSCAN算法 居于密度的划分 基于划分聚类和基于层次聚类的方法在聚类过程中根据距离来划分类簇&#xff0c;因此只能够用于挖掘球状簇。 为了解决这一缺陷&#xff0c;基于密度聚类算法利用密度思想&#xff0c;将样本中的高密度区域(即样本点分布稠密的…

图的概念(1)

图是什么&#xff1f; 首先&#xff0c;我们导入需要的包&#xff1f; import numpy as np import random import networkx as nx from IPython.display import Image import matplotlib.pyplot as plt 图的定义&#xff1f; 图表示物件与物件之间关系的数学对象&#xff0c;…

Metasploit入门用法

靶机介绍 Difficult&#xff1a; As always, it’s a very easy box for beginners. Goal&#xff1a; Get flag Download&#xff1a;https://www.vulnhub.com/entry/funbox-scriptkiddie,725/ 解题过程&#xff1a; 1.使用nmap进行端口扫描:Nmap -sV IP 2.使用search命令查…

yolov5加入分割头,多任务头

Yolov5同时进行目标检测和分割分割_MidasKing的博客-CSDN博客_yolov5分割 用YOLOv5ds训练自己的数据集,注意点!_用猪头过日子.的博客-CSDN博客 基于pytorch用yolov5算法实现目标检测与分割_无损检测小白白的博客-CSDN博客 原理 代码:

一种三自由度机器人的设计(CAD+Solidworks+文档)

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1课题研究背景及其意义 1 1.2三自由度机器人的发展与现状 1 1.3三自由度机器人的原理和介绍 3 1.4三自由度机器人目前存在问题 4 1.5三自由度机器人的力控制问题 4 1.6三自由度机器人的发展趋势 5 第2章 总体技术方案及系统组成 7 2.…

[附源码]SSM计算机毕业设计电子病历信息管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

AVL的单旋和双旋—附图超详细

文章目录前言&#xff1a;AVL 的插入寻找插入位置更新平衡因子调整AVL右旋左旋左右双旋右左双旋完整代码前言&#xff1a; 我们知道二叉排序树的搜索效率很高&#xff0c;能达到(logn)的时间复杂度&#xff0c;但是当元素有序导致二叉搜索树变成了一个长条&#xff08;图左&am…

游戏优化之空间划分

使用的动机 定义&#xff1a;将对象根据它们的位置存储在数据结构中&#xff0c;来高效地定位对象。 在游戏中&#xff0c;AI向最近的敌人攻击是很常见的&#xff0c;但如果有很多单位的话&#xff0c;他们AI需要频繁的查找单位&#xff0c;然后在检测是不是距离最近的单位&a…

Git工具使用全解

Git工具使用全解 文章目录Git工具使用全解1.企业开发中的版本控制器2.Git工具的使用场景3.Git工具操作流程(三板斧操作)4.Git工具的安装与常用命令4.1 Git的安装4.2 Git基本操作指令5.Git工具常见问题解决5.1 常见问题&#xff1a;分支冲突解决办法5.2 常见问题&#xff1a;合并…

Linux | 动静态库 | 动静态链接 | makefile库打包 | 第三方库使用

文章目录何为动静态库库文件的链接静态链接静态库打包动态链接动态库打包第三方库的使用静态库的使用动态库的使用在系统层面上的动态链接理解何为动静态库 静态库(.a)&#xff1a;在程序编译链接时将静态库二进制码拷贝到程序代码中&#xff0c;程序运行时不再需要外部的静态库…

面试:类相关---Java、Android有哪些类加载器

Android类加载器与Java类加载器的对比 - 掘金 Android | 类加载器与插件化 - 简书 相关复习&#xff1a; 面试&#xff1a;热修复原理_沙漠一只雕得儿得儿的博客-CSDN博客_android 热修复原理面试 面试&#xff1a;类的初始化过程_沙漠一只雕得儿得儿的博客-CSDN博客 什么…

内置 230+ 工具,它值得被官方纳入标准库

经过了几十年的发展&#xff0c;Python 的轮子生态越来越丰富&#xff0c;例如针对网络请求有 requests&#xff0c;针对命令行开发有 typer 等等&#xff0c;这些第三方库给我们的日常开发带来了极大的便利。 今天我推荐另一个第三方库 – Boltons&#xff0c;和大多数第三方…

go语言之不必要的拷贝

其实我也是个golang开发者~~ Go语言本来就以轻量快速著称&#xff0c;一位GitHub员工却偶然发现&#xff1a; 只改变一个字符的位置&#xff0c;能把一段代码运行速度提高足足42%。 简直就像是…… 这个简单有效的技巧一经发布&#xff0c;就引来众多程序员围观。 原作者自己…

论文中常见的拟合散点验证图(R语言版)

论文中常见的拟合散点验证图&#xff08;R语言版&#xff09; 如上图所示&#xff0c;是论文中常见的validation图&#xff0c;python也能实现相似的图绘。 今天先介绍R语言版&#xff0c;python改期再介绍吧 这张图需要依次实现下列功能&#xff1a; data实测与data模拟的散…

RabbitMQ系列【11】延迟队列

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录前言1. 过期消息实现延迟队列2. 过期队列实现延迟队列3. 插件实现延迟队列3.1 安装插件3.2 代码实现3.3 测试前言 延迟队列&#xff1a;即消息进入队列后不会立即被消费&#xff0c;只有到达指…

MySQL事务隔离机制 -- 必须说透

文章目录前言一、什么是数据库事务二、事务并发带来的4类问题三、事务4种隔离级别四、Mysql演示4种隔离级别总结前言 如何控制并发是数据库领域中非常重要的问题之一&#xff0c;MySQL为了解决并发带来的问题&#xff0c;设计了事务隔离机制、锁机制、MVCC机制&#xff0c;用一…

c# 实验七 图像列表框及树形视图控件的使用

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;《项目专栏》 &#x1f4e7;如果文章知识点有错误的地方&#xf…

[附源码]java毕业设计四六级考试管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

m基于matlab的wcdma软切换算法的研究分析和仿真

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 软切换是WCDMA系统的关键技术之一&#xff0c;软切换算法和相关参数的设置直接影响着系统的容量和服务质量。通过WCDMA系统的软切换技术可以提高小区覆盖率和系统容量。所以软切换技术是…

【ASM】字节码操作 工具类与常用类 LocalVariablesSorter 类 简单介绍与使用

文章目录 1.概述2. LocalVariablesSorter#2.1 class info2.2 fields3.案例3.1 编码实现3.2 编码实现v21.概述 在上一节:【ASM】字节码操作 工具类与常用类 GeneratorAdapter 介绍 我们知道了对于GeneratorAdapter 类来说,它非常重要的一个特点:将一些visitXxx()方法封装成一…