动态署理概述
什么是署理
署理模式(Proxy pattern): 为另一个对象提供一个替人或占位符以控制对这个对象的访问
什么是动态署理?
动态署理就是,在程序运行期,创建目的对象的署理对象,并对目的对象中的方法进行功能性加强的一种技能。
在生成署理对象的过程中,目的对象稳定,署理对象中的方法是目的对象方法的加强方法。可以明白为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
署理的创建
创建署理的方法是postProcessAfterInitialization:如果bean被子类标识为署理,则利用配置的拦截器创建一个署理
- /**
- * Create a proxy with the configured interceptors if the bean is
- * identified as one to proxy by the subclass.
- * @see #getAdvicesAndAdvisorsForBean
- */
- @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;
- }
复制代码 wrapIfNecessary方法主要用于判断是否须要创建署理,如果Bean可以大概获取到advisor才须要创建署理
- /**
- * 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) {
- // 如果bean是通过TargetSource接口获取
- if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
- return bean;
- }
- // 如果bean是切面类
- if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
- return bean;
- }
- // 如果是aop基础类?是否跳过?
- if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
- this.advisedBeans.put(cacheKey, Boolean.FALSE);
- return bean;
- }
- // 重点:获取所有advisor,如果没有获取到,那说明不要进行增强,也就不需要代理了。
- 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;
- }
复制代码 获取全部的Advisor
我们看下获取全部advisor的方法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();
- }
复制代码 通过findEligibleAdvisors方法获取advisor, 如果获取不到返回DO_NOT_PROXY(不须要创建署理),findEligibleAdvisors方法如下
- /**
- * Find all eligible Advisors for auto-proxying this class.
- * @param beanClass the clazz to find advisors for
- * @param beanName the name of the currently proxied bean
- * @return the empty List, not {@code null},
- * if there are no pointcuts or interceptors
- * @see #findCandidateAdvisors
- * @see #sortAdvisors
- * @see #extendAdvisors
- */
- protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
- // 和上文一样,获取所有切面类的切面方法生成Advisor
- List<Advisor> candidateAdvisors = findCandidateAdvisors();
- // 找到这些Advisor中能够应用于beanClass的Advisor
- List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
- // 如果需要,交给子类拓展
- extendAdvisors(eligibleAdvisors);
- // 对Advisor排序
- if (!eligibleAdvisors.isEmpty()) {
- eligibleAdvisors = sortAdvisors(eligibleAdvisors);
- }
- return eligibleAdvisors;
- }
复制代码 获取全部切面类的切面方法生成Advisor
- /**
- * Find all candidate Advisors to use in auto-proxying.
- * @return the List of candidate Advisors
- */
- protected List<Advisor> findCandidateAdvisors() {
- Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
- return this.advisorRetrievalHelper.findAdvisorBeans();
- }
复制代码 找到这些Advisor中可以大概应用于beanClass的Advisor
- /**
- * Determine the sublist of the {@code candidateAdvisors} list
- * that is applicable to the given class.
- * @param candidateAdvisors the Advisors to evaluate
- * @param clazz the target class
- * @return sublist of Advisors that can apply to an object of the given class
- * (may be the incoming List as-is)
- */
- public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
- if (candidateAdvisors.isEmpty()) {
- return candidateAdvisors;
- }
- List<Advisor> eligibleAdvisors = new ArrayList<>();
- for (Advisor candidate : candidateAdvisors) {
- // 通过Introduction实现的advice
- 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;
- }
- // 是否能够应用于clazz的Advice
- if (canApply(candidate, clazz, hasIntroductions)) {
- eligibleAdvisors.add(candidate);
- }
- }
- return eligibleAdvisors;
- }
复制代码 创建署理的入口方法
获取全部advisor后,如果有advisor,则阐明须要加强,即须要创建署理,创建署理的方法如下:
- /**
- * Create an AOP proxy for the given bean.
- * @param beanClass the class of the bean
- * @param beanName the name of the bean
- * @param specificInterceptors the set of interceptors that is
- * specific to this bean (may be empty, but not null)
- * @param targetSource the TargetSource for the proxy,
- * already pre-configured to access the bean
- * @return the AOP proxy for the bean
- * @see #buildAdvisors
- */
- 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()) {
- // Explicit handling of JDK proxy targets (for introduction advice scenarios)
- if (Proxy.isProxyClass(beanClass)) {
- // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
- for (Class<?> ifc : beanClass.getInterfaces()) {
- proxyFactory.addInterface(ifc);
- }
- }
- }
- else {
- // No proxyTargetClass flag enforced, let's apply our default checks...
- 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);
- }
- // Use original ClassLoader if bean class not locally loaded in overriding class loader
- ClassLoader classLoader = getProxyClassLoader();
- if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
- classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
- }
- return proxyFactory.getProxy(classLoader);
- }
复制代码 proxyFactory.getProxy(classLoader)

- /**
- * Create a new proxy according to the settings in this factory.
- * <p>Can be called repeatedly. Effect will vary if we've added
- * or removed interfaces. Can add and remove interceptors.
- * <p>Uses the given class loader (if necessary for proxy creation).
- * @param classLoader the class loader to create the proxy with
- * (or {@code null} for the low-level proxy facility's default)
- * @return the proxy object
- */
- public Object getProxy(@Nullable ClassLoader classLoader) {
- return createAopProxy().getProxy(classLoader);
- }
复制代码 依据条件创建署理(jdk或cglib)
DefaultAopProxyFactory.createAopProxy
- @Override
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (!NativeDetector.inNativeImage() &&
- (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);
- }
- }
复制代码 小结
- config.isOptimize() 是通过optimize设置,表现配置是自定义的,默认是false;
- config.isProxyTargetClass()是通过<aop:config proxy-target-class="true" /> 来配置的,表现优先利用cglib署理,默认是false;
- hasNoUserSuppliedProxyInterfaces(config) 表现是否目的类实现了接口
由此可以知道:
Spring默认在目的类实现接口时是通过JDK署理实现的,只有非接口的是通过Cglib署理实现的。当设置proxy-target-class为true时在目的类不是接口大概署理类时优先利用cglib署理实现。
JDK署理
JDK动态署理是有JDK提供的工具类Proxy实现的,动态署理类是在运行时生成指定接口的署理类,每个署理实例(实现须要署理的接口)都有一个关联的调用处置处罚程序对象,此对象实现了InvocationHandler,终极的业务逻辑是在InvocationHandler实现类的invoke方法上。
JDK署理的流程如下:
JDK署理自动生成的class是由sun.misc.ProxyGenerator来生成的。
ProxyGenerator生成代码
我们看下sun.misc.ProxyGenerator生成代码的逻辑:
- /**
- * Generate a proxy class given a name and a list of proxy interfaces.
- *
- * @param name the class name of the proxy class
- * @param interfaces proxy interfaces
- * @param accessFlags access flags of the proxy class
- */
- public static byte[] generateProxyClass(final String name,
- Class<?>[] interfaces,
- int accessFlags)
- {
- ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
- final byte[] classFile = gen.generateClassFile();
- ...
- }
复制代码 generateClassFile方法如下:
一共三个步骤(把大象装进冰箱分几步?):
- 第一步:(把冰箱门打开)准备工作,将全部方法包装成ProxyMethod对象,包括Object类中hashCode、equals、toString方法,以及被署理的接口中的方法
- 第二步:(把大象装进去)为署理类组装字段,构造函数,方法,static初始化块等
- 第三步:(把冰箱门带上)写入class文件
从生成的Proxy代码看执行流程
从上述sun.misc.ProxyGenerator类中可以看到,这个类里面有一个配置参数sun.misc.ProxyGenerator.saveGeneratedFiles,可以通过这个参数将生成的Proxy类生存在本地,比如设置为true 执行后,生成的文件如下:

我们看下生成后的代码:
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by FernFlower decompiler)
- //
- package com.sun.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
- import java.util.List;
- import tech.pdai.springframework.service.IUserService;
- // 所有类和方法都是final类型的
- public final class $Proxy0 extends Proxy implements IUserService {
- private static Method m1;
- private static Method m3;
- private static Method m2;
- private static Method m0;
- private static Method m4;
- // 构造函数注入 InvocationHandler
- public $Proxy0(InvocationHandler var1) throws {
- super(var1);
- }
- public final boolean equals(Object var1) throws {
- try {
- return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
- } catch (RuntimeException | Error var3) {
- throw var3;
- } catch (Throwable var4) {
- throw new UndeclaredThrowableException(var4);
- }
- }
- public final List findUserList() throws {
- try {
- return (List)super.h.invoke(this, m3, (Object[])null);
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- public final String toString() throws {
- try {
- return (String)super.h.invoke(this, m2, (Object[])null);
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- public final int hashCode() throws {
- try {
- return (Integer)super.h.invoke(this, m0, (Object[])null);
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- public final void addUser() throws {
- try {
- super.h.invoke(this, m4, (Object[])null);
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- static {
- try {
- // 初始化 methods, 2个IUserService接口中的方法,3个Object中的接口
- m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
- m3 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("findUserList");
- m2 = Class.forName("java.lang.Object").getMethod("toString");
- m0 = Class.forName("java.lang.Object").getMethod("hashCode");
- m4 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("addUser");
- } catch (NoSuchMethodException var2) {
- throw new NoSuchMethodError(var2.getMessage());
- } catch (ClassNotFoundException var3) {
- throw new NoClassDefFoundError(var3.getMessage());
- }
- }
- }
复制代码 上述代码是比较容易明白的,我就不绘图了。
主要流程是:
- ProxyGenerator创建Proxy的具体类$Proxy0
- 由static初始化块初始化接口方法:2个IUserService接口中的方法,3个Object中的接口方法
- 由构造函数注入InvocationHandler
- 执行的时间,通过ProxyGenerator创建的Proxy,调用InvocationHandler的invoke方法,执行我们自定义的invoke方法
SpringAOP中JDK署理的实现
SpringAOP饰演的是JDK署理的创建和调用两个脚色,我们通过这两个方向来看下SpringAOP的代码(JdkDynamicAopProxy类)
SpringAOP Jdk署理的创建
署理的创建比较简单,调用getProxy方法,然后直接调用JDK中Proxy.newProxyInstance()方法将classloader和被署理的接口方法传入即可。
- @Override
- public Object getProxy() {
- return getProxy(ClassUtils.getDefaultClassLoader());
- }
- @Override
- public Object getProxy(@Nullable ClassLoader classLoader) {
- if (logger.isTraceEnabled()) {
- logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
- }
- return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
- }
复制代码 SpringAOP Jdk署理的执行
执行的方法如下:
- /**
- * Implementation of {@code InvocationHandler.invoke}.
- * <p>Callers will see exactly the exception thrown by the target,
- * unless a hook method throws an exception.
- */
- @Override
- @Nullable
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object oldProxy = null;
- boolean setProxyContext = false;
- TargetSource targetSource = this.advised.targetSource;
- Object target = null;
- try {
- // 执行的是equal方法
- if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
- // The target does not implement the equals(Object) method itself.
- return equals(args[0]);
- }
- // 执行的是hashcode方法
- else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
- // The target does not implement the hashCode() method itself.
- return hashCode();
- }
- // 如果是包装类,则dispatch to proxy config
- else if (method.getDeclaringClass() == DecoratingProxy.class) {
- // There is only getDecoratedClass() declared -> dispatch to proxy config.
- return AopProxyUtils.ultimateTargetClass(this.advised);
- }
- // 用反射方式来执行切点
- else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
- method.getDeclaringClass().isAssignableFrom(Advised.class)) {
- // Service invocations on ProxyConfig with the proxy config...
- return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
- }
- Object retVal;
- 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);
- // 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();
- }
- // Massage return value if necessary.
- Class<?> returnType = method.getReturnType();
- if (retVal != null && retVal == target &&
- returnType != Object.class && returnType.isInstance(proxy) &&
- !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
- // Special case: it returned "this" and the return type of the method
- // is type-compatible. Note that we can't help if the target sets
- // a reference to itself in another returned object.
- retVal = proxy;
- }
- else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
- throw new AopInvocationException(
- "Null return value from advice does not match primitive return type for: " + method);
- }
- return retVal;
- }
- finally {
- if (target != null && !targetSource.isStatic()) {
- // Must have come from TargetSource.
- targetSource.releaseTarget(target);
- }
- if (setProxyContext) {
- // Restore old proxy.
- AopContext.setCurrentProxy(oldProxy);
- }
- }
- }
复制代码 CGLIB署理
署理的流程
- 在上图中,我们可以通过在Enhancer中配置更多的参数来控制署理的行为,比如如果只希望加强这个类中的一个方法(而不是全部方法),那就增加callbackFilter来对目的类中方法进行过滤;Enhancer可以有更多的参数类配置其行为,不外我们在学习上述主要的流程就够了。
- final方法为什么不能被署理?很显然final方法没法被子类覆盖,固然不能署理了。
- Mockito为什么不能mock静态方法?由于mockito也是基于cglib动态署理来实现的,static方法也不能被子类覆盖,以是显然不能mock。但PowerMock可以mock静态方法,由于它直接在bytecode上工作。
SpringAOP中Cglib署理的实现
SpringAOP封装了cglib,通过其进举措态署理的创建。
我们看下CglibAopProxy的getProxy方法
- @Override
- public Object getProxy() {
- return getProxy(null);
- }
- @Override
- public Object getProxy(@Nullable ClassLoader classLoader) {
- if (logger.isTraceEnabled()) {
- logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
- }
- try {
- Class<?> rootClass = this.advised.getTargetClass();
- Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
- // 上面流程图中的目标类
- Class<?> proxySuperClass = rootClass;
- if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
- proxySuperClass = rootClass.getSuperclass();
- Class<?>[] additionalInterfaces = rootClass.getInterfaces();
- for (Class<?> additionalInterface : additionalInterfaces) {
- this.advised.addInterface(additionalInterface);
- }
- }
- // Validate the class, writing log messages as necessary.
- validateClassIfNecessary(proxySuperClass, classLoader);
- // 重点看这里,就是上图的enhancer,设置各种参数来构建
- 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回调接口,即方法的增强点
- Callback[] callbacks = getCallbacks(rootClass);
- Class<?>[] types = new Class<?>[callbacks.length];
- for (int x = 0; x < types.length; x++) {
- types[x] = callbacks[x].getClass();
- }
- // 上节说到的filter
- enhancer.setCallbackFilter(new ProxyCallbackFilter(
- this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
- enhancer.setCallbackTypes(types);
- // 重点:创建proxy和其实例
- return createProxyClassAndInstance(enhancer, callbacks);
- }
- catch (CodeGenerationException | IllegalArgumentException ex) {
- throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
- ": Common causes of this problem include using a final class or a non-visible class",
- ex);
- }
- catch (Throwable ex) {
- // TargetSource.getTarget() failed
- throw new AopConfigException("Unexpected AOP exception", ex);
- }
- }
复制代码 获取callback的方法如下,提几个明白的要点吧,具体读者在学习的时间发起把我的例子跑一下,然后打一个断点进行明白。
- rootClass: 即目的署理类
- advised: 包含上文中我们获取到的advisor加强器的聚集
- exposeProxy: 在xml配置文件中配置的,背景就是如果在事务A中利用了署理,事务A调用了目的类的的方法a,在方法a中又调用目的类的方法b,方法a,b同时都是要被加强的方法,如果不配置exposeProxy属性,方法b的加强将会失效,如果配置exposeProxy,方法b在方法a的执行中也会被加强了
- DynamicAdvisedInterceptor: 拦截器将advised(包含上文中我们获取到的advisor加强器)构建配置的AOP的callback(第一个callback)
- targetInterceptor: xml配置的optimize属性利用的(第二个callback)
- 最后连同其它5个默认的Interceptor 返回作为cglib的拦截器链,之后通过CallbackFilter的accpet方法返回的索引从这个聚集中返回对应的拦截加强器执行加强操作。
- private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
- // Parameters used for optimization choices...
- boolean exposeProxy = this.advised.isExposeProxy();
- boolean isFrozen = this.advised.isFrozen();
- boolean isStatic = this.advised.getTargetSource().isStatic();
- // Choose an "aop" interceptor (used for AOP calls).
- Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
- // Choose a "straight to target" interceptor. (used for calls that are
- // unadvised but can return this). May be required to expose the proxy.
- Callback targetInterceptor;
- if (exposeProxy) {
- targetInterceptor = (isStatic ?
- new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
- }
- else {
- targetInterceptor = (isStatic ?
- new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
- }
- // Choose a "direct to target" dispatcher (used for
- // unadvised calls to static targets that cannot return this).
- Callback targetDispatcher = (isStatic ?
- new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
- Callback[] mainCallbacks = new Callback[] {
- aopInterceptor, //
- targetInterceptor, // invoke target without considering advice, if optimized
- new SerializableNoOp(), // no override for methods mapped to this
- targetDispatcher, this.advisedDispatcher,
- new EqualsInterceptor(this.advised),
- new HashCodeInterceptor(this.advised)
- };
- Callback[] callbacks;
- // If the target is a static one and the advice chain is frozen,
- // then we can make some optimizations by sending the AOP calls
- // direct to the target using the fixed chain for that method.
- if (isStatic && isFrozen) {
- Method[] methods = rootClass.getMethods();
- Callback[] fixedCallbacks = new Callback[methods.length];
- this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);
- // TODO: small memory optimization here (can skip creation for methods with no advice)
- for (int x = 0; x < methods.length; x++) {
- Method method = methods[x];
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
- fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
- chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
- this.fixedInterceptorMap.put(method, x);
- }
- // Now copy both the callbacks from mainCallbacks
- // and fixedCallbacks into the callbacks array.
- callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
- System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
- System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
- this.fixedInterceptorOffset = mainCallbacks.length;
- }
- else {
- callbacks = mainCallbacks;
- }
- return callbacks;
- }
复制代码 可以结合调试,方便明白

AOP在嵌套方法调用时不生效
在一个实现类中,有2个方法,方法A,方法B,其中方法B上面有个注解切面,当方法B被外部调用的时间,会进入切面方法。
但当方法B是被方法A调用时,并不能从方法B的注解上,进入到切面方法,即我们经常碰到的方法嵌套时,AOP注解不生效的标题。
案例
外部调用AOP方法正常进入
通过外部,调用方法B,可以正常进入切面方法,这个场景的代码如下:
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface DemoAnno {
- }
复制代码
- @Aspect
- @Order(-1)
- @Component
- public class DemoAspect {
- @Before("@annotation(da)")
- public void beforDoSomething(JoinPoint point, DemoAnno da) throws Exception {
- System.out.println("before method B, print 'hello,world' " );
- }
- }
复制代码
- public interface DemoService {
- void methodDemoA();
- void methodDemoB();
- }
复制代码
- @Service
- public class DemoServiceImpl implements DemoService {
- @Override
- public void methodDemoA(){
- System.out.println("this is method A");
- }
- @Override
- @DemoAnno
- public void methodDemoB() {
- System.out.println("this is method B");
- }
- }
复制代码
- @Autowired
- DemoService demoService;
- @Test
- public void testMethod(){
- demoService.methodDemoA();
- demoService.methodDemoB();
- }
复制代码 输出效果:
- this is method A
- before method B, print 'hello,world'
- this is method B
复制代码 方法嵌套调用,AOP不生效
上面的代码,做下修改。在DemoServiceImpl实现类中,通过方法A去调用方法B,然后再单元测试类中,调用方法A。代码修改后如下:
- @Service
- public class DemoServiceImpl implements DemoService {
- @Override
- public void methodDemoA(){
- System.out.println("this is method A");
- methodDemoB();
- }
- @Override
- @DemoAnno
- public void methodDemoB() {
- System.out.println("this is method B");
- }
- }
复制代码
- this is method A
- this is method B
复制代码 原因分析
场景1中,通过外部调用方法B,是由于spring在启动时,根据切面类及注解,生成了DemoService的署理类,在调用方法B时,现实上是署理类先对目的方法进行了业务加强处置处罚(执行切面类中的业务逻辑),然后再调用方法B本身。以是场景1可以正常进入切面方法;
场景2中,通过外部调用的是方法A,虽然spring也会创建一个cglib的署理类去调用方法A,但当方法A调用方法B的时间,属于类里面的内部调用,利用的是实例对象本身去去调用方法B,非aop的cglib署理对象调用,方法B天然就不会进入到切面方法了。
解决方案
但现实上我们期望的是,方法A在调用方法B的时间,仍然可以大概进入切面方法,即须要AOP切面生效。这种环境下,在调用方法B的时间,须要利用AopContext.currentProxy()获取当前的署理对象,然后利用署理对象调用方法B。
注:须要开启 exposeProxy=true 的配置,springboot项目中,可以在启动类上面,添加 @EnableAspectJAutoProxy(exposeProxy = true)注解。
- @Service
- public class DemoServiceImpl implements DemoService {
- @Override
- public void methodDemoA(){
- System.out.println("this is method A");
- DemoService service = (DemoService) AopContext.currentProxy();
- service.methodDemoB();
- }
- @Override
- @DemoAnno
- public void methodDemoB() {
- System.out.println("this is method B");
- }
- }
复制代码 面试题专栏
Java面试题专栏已上线,欢迎访问。
- 如果你不知道简历怎么写,简历项目不知道怎么包装;
- 如果简历中有些内容你不知道该不应写上去;
- 如果有些综合性标题你不知道怎么答;
那么可以私信我,我会尽我所能帮助你。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |