Spring自定义实现Aop或使用@Transaction,Bean对象注入为null的情况分析

一、前言

  有时我们需要通过使用Aop的方式对我们的程序进行切面监控,比如Service执行时间,记录log等。常用的两种方式一个是写execution表达式,还一种是自定义注解。但是在使用的过程中,有时我们会发现原本没问题的Service,现在使用里面注入的bean对象变为null了,针对这种情况我们分析一下。

二、情景再现

自定义注解Monitor

public @interface Monitor {
}

自定义Advice

@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)
public class ServiceAdvice {
    @Pointcut("@annotation(aoptest.Monitor)")
    public void annotation() {
    }


    @Before("annotation()")
    public void beforeMethodAdviceA(JoinPoint joinPoint){
        System.out.println("beforeMethodAdviceA");
    }
}

ServiceA,在ServiceA中注入ServiceB,并在a()方法和c()方法上加上注解。

@Component
public class ServiceA extends  SuperA implements InterfaceA{

    @Autowired
    private ServiceB serviceB;

    @Monitor
    public void a(){
        System.out.println("invoked method a");
        System.out.println(serviceB);
        System.out.println("---------------------");
        ((ServiceA) AopContext.currentProxy()).c();
    }


    @Monitor
    private void c() {
        System.out.println(serviceB);
        System.out.println("invoked method c");
    }

}

Main方法。

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("aoptest");
        ctx.refresh();
        ServiceA serviceA = (ServiceA) ctx.getBean("serviceA");
        serviceA.a();
    }
}

执行结果:

可以看到,在方法a中,serviceB是有值的,在调用方法c就为null了。这里((ServiceA) AopContext.currentProxy()).c()这样调用是为了c方法也保证有切面的功能,如果我们在a方法中直接this.c(),那么c方法是不会执行增强的方法。那为什么两个方法添加了同样的注解,ServiceA中注入的Bean对象ServiceB一个有值,一个就为null了呢。

先说结论:

在执行Spring Aop中,对方法进行增强时,不论时执行execution表达式,还是自定义注解,都不能对private方法进行

比如我们对一个service的private方法添加@Transaction注解,你会发现不仅失效,Service里注入的Bean也为null了。

三、源码解析

不论是@Transaction还是自定义注解,其实本质都是实现Spring Aop的自定义增强功能。Spring Aop匹配的源码解析可以看这篇文章。

这里我们主要看Spring Aop在找到应该被增强的对象后,创建的代理类是什么样。Spring创建代理类是通过AbstractAutoProxyCreator的postProcessAfterInitialization方法,在Bean对象创建完成并执行完populateBean注入所需的Bean对象后,在initializeBean方法中执行。

	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对象是否需要进行增强,现在我们直接看createProxy方法,看看Spring是如何创建。

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

createProxy方法其实就是创建一个proxyFactory,然后进行一些set,将需要的代理Bean和增强的方法先设置好。我们直接看getProxy方法。

getProxy方法有两种代理方式,Spring默认使用cglib方式,jdk方式同理。

	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 (ClassUtils.isCglibProxyClass(rootClass)) {
				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);

			// Configure CGLIB 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 ClassLoaderAwareUndeclaredThrowableStrategy(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);
		}
    }

我们知道cglib代理主要是通过继承子类方式,这里设置了callbacks和setCallbackFilter来确认不同方法走哪种。

比如getCallbacks方法中有一段

		Callback[] mainCallbacks = new Callback[] {
				aopInterceptor,  // for normal advice
				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)
		};

ProxyCallbackFilter类的accept方法判断执行走哪个。

		public int accept(Method method) {
			if (AopUtils.isFinalizeMethod(method)) {
				logger.trace("Found finalize() method - using NO_OVERRIDE");
				return NO_OVERRIDE;
			}
			if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				if (logger.isTraceEnabled()) {
					logger.trace("Method is declared on Advised interface: " + method);
				}
				return DISPATCH_ADVISED;
			}
			// We must always proxy equals, to direct calls to this.
			if (AopUtils.isEqualsMethod(method)) {
				if (logger.isTraceEnabled()) {
					logger.trace("Found 'equals' method: " + method);
				}
				return INVOKE_EQUALS;
			}
			// We must always calculate hashCode based on the proxy.
			if (AopUtils.isHashCodeMethod(method)) {
				if (logger.isTraceEnabled()) {
					logger.trace("Found 'hashCode' method: " + method);
				}
				return INVOKE_HASHCODE;
			}
			Class<?> targetClass = this.advised.getTargetClass();
			// Proxy is not yet available, but that shouldn't matter.
			List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
			boolean haveAdvice = !chain.isEmpty();
			boolean exposeProxy = this.advised.isExposeProxy();
			boolean isStatic = this.advised.getTargetSource().isStatic();
			boolean isFrozen = this.advised.isFrozen();
			if (haveAdvice || !isFrozen) {
				// If exposing the proxy, then AOP_PROXY must be used.
				if (exposeProxy) {
					if (logger.isTraceEnabled()) {
						logger.trace("Must expose proxy on advised method: " + method);
					}
					return AOP_PROXY;
				}
				String key = method.toString();
				// Check to see if we have fixed interceptor to serve this method.
				// Else use the AOP_PROXY.
				if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Method has advice and optimizations are enabled: " + method);
					}
					// We know that we are optimizing so we can use the FixedStaticChainInterceptors.
					int index = this.fixedInterceptorMap.get(key);
					return (index + this.fixedInterceptorOffset);
				}
				else {
					if (logger.isTraceEnabled()) {
						logger.trace("Unable to apply any optimizations to advised method: " + method);
					}
					return AOP_PROXY;
				}
			}
			else {
				if (exposeProxy || !isStatic) {
					return INVOKE_TARGET;
				}
				Class<?> returnType = method.getReturnType();
				if (targetClass != null && returnType.isAssignableFrom(targetClass)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Method return type is assignable from target type and " +
								"may therefore return 'this' - using INVOKE_TARGET: " + method);
					}
					return INVOKE_TARGET;
				}
				else {
					if (logger.isTraceEnabled()) {
						logger.trace("Method return type ensures 'this' cannot be returned - " +
								"using DISPATCH_TARGET: " + method);
					}
					return DISPATCH_TARGET;
				}
			}
		}

设置完成后,执行createProxyClassAndInstance方法进行动态代理类的创建。

	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null && this.constructorArgTypes != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}

最后执行Enhancer的create方法。create方法执行时最后会调用Enhancer的generateClass方法。

	public void generateClass(ClassVisitor v) throws Exception {
		Class sc = (superclass == null) ? Object.class : superclass;

		if (TypeUtils.isFinal(sc.getModifiers()))
			throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
		List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
		filterConstructors(sc, constructors);

		// Order is very important: must add superclass, then
		// its superclass chain, then each interface and
		// its superinterfaces.
		List actualMethods = new ArrayList();
		List interfaceMethods = new ArrayList();
		final Set forcePublic = new HashSet();
		getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
                .................
        }

进入getMethods方法。

	private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic) {
		ReflectUtils.addAllMethods(superclass, methods);
		List target = (interfaceMethods != null) ? interfaceMethods : methods;
		if (interfaces != null) {
			for (int i = 0; i < interfaces.length; i++) {
				if (interfaces[i] != Factory.class) {
					ReflectUtils.addAllMethods(interfaces[i], target);
				}
			}
		}
		if (interfaceMethods != null) {
			if (forcePublic != null) {
				forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));
			}
			methods.addAll(interfaceMethods);
		}
		CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));
		CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
		CollectionUtils.filter(methods, new DuplicatesPredicate());
		CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));
	}

可以看到是先将所有的方法都先找到,但是在执行CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));

public class VisibilityPredicate implements Predicate {
    private boolean protectedOk;
    private String pkg;
    private boolean samePackageOk;

    public VisibilityPredicate(Class source, boolean protectedOk) {
        this.protectedOk = protectedOk;
        this.samePackageOk = source.getClassLoader() != null;
        this.pkg = TypeUtils.getPackageName(Type.getType(source));
    }

    public boolean evaluate(Object arg) {
        Member member = (Member)arg;
        int mod = member.getModifiers();
        if (Modifier.isPrivate(mod)) {
            return false;
        } else if (Modifier.isPublic(mod)) {
            return true;
        } else if (Modifier.isProtected(mod) && this.protectedOk) {
            return true;
        } else {
            return this.samePackageOk && this.pkg.equals(TypeUtils.getPackageName(Type.getType(member.getDeclaringClass())));
        }
    }

将该Bean的private私有方法过滤掉了。之后创建一个代理类子类。

既示例中生成的就是 代理类 ServiceA@EnhancerCglib,他的父类是ServiceA。

那么既然是实例化的动态代理子类,里面的所有注入的Bean对象都应该为null,为什么public的方法注入的Bean对象确有值呢。

回到CglibAopProxy类的DynamicAdvisedInterceptor类中的intercept方法

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

我们可以看到,如果不是私有的方法,符合匹配增强的方法中,target = targetSource.getTarget();这行代码获取的是Spring 生成的Bean对象,该对象在执行postProcessAfterInitialization前已经完成自身所需Bean对象的注入。之后在执行增强方法和自己的方法时,传入的是Spring生成的Bean对象,而非通过Cglib创建的代理对象。

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

也就是说在执行示例中ServiceA的a()方法时,调用的生成动态代理类serviceA@cglib类中的targetBean,即正常的Spring生成的ServiceA类。

在调用ServiceA的c()方法时,因为生成的代理类serviceA@cglib类中没有c这个私有方法,所以代理类直接调用父类的c方法,但是代理类中的需要注入的Bean又没有,所以在执行c方法时就报null了。

我们将之前的代码打印出hashcode看一下。

@Component
public class ServiceA extends  SuperA implements InterfaceA{

    @Autowired
    private ServiceB serviceB;

    @Monitor
    public void a(){
        System.out.println("invoked method a");
        System.out.println(serviceB);
        System.out.println("hashcode:"+this.hashCode());
        System.out.println( ((ServiceA) AopContext.currentProxy()).hashCode());
        System.out.println("---------------------");
        ((ServiceA) AopContext.currentProxy()).c();
    }

    @Monitor
    private void c() {
        System.out.println(serviceB);
        System.out.println("hashcode:"+this.hashCode());
        System.out.println("invoked method c");
    }
}

执行结果:

可以看到,在执行a方法时,因为时spring生成的Bean,hashcode是547201549,而下一行当前cglib代理对象的hashcode是-675809067。在执行c方法时,他的hashcode是-675809067。说明调用c方法和a方法时,已经不是同一个Bean对象,所以注入的Bean对象serviceB没有值。

如果改成this.c

    @Monitor
    public void a(){
        System.out.println("invoked method a");
        System.out.println(serviceB);
        System.out.println("hashcode:"+this.hashCode());
        System.out.println( ((ServiceA) AopContext.currentProxy()).hashCode());
        System.out.println("---------------------");
        this.c();
    }

    @Monitor
    private void c() {
        System.out.println(serviceB);
        System.out.println("hashcode:"+this.hashCode());
        System.out.println("invoked method c");
    }

执行结果:

可以看到是同一个hashcode,serviceB也有值了。

四、总结

1、Spring自定义实现Aop或使用@Transaction时,目标方法不能为private。

2、Spring Aop创建的动态代理类一般使用cglib方式,cglib通过继承父类方式。将需要增强的类作为父类,创建的代理类为子类,并重写目标方法,equals方法、hashcode方法等。

3、调用匹配的增强方法时,实际入参是Spring生成的Bean对象。

    通过target = targetSource.getTarget();

    new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

4、调用动态代理类中没有的方法时,则是生成的代理类对象直接调用父类的方法。因为代理类只是通过无参构造方法生成,所以他的所有成员变量和需要注入的Bean对象都为null。

5、equals方法和hashcode方法被重写,所以值肯定不一样。但是如果直接打印 this,会发现是一样的。原因是会调用Object类的toString方法。

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

因为创建的动态代理类会把ServiceA、ServiceA的父类中(Object)所有public方法都会创建,所以会调用Object类的toString。虽然不执行增强,但是也是会进入DynamicAdvisedInterceptor类的intercept方法进行判断,看是否执行增强。一旦进行intercept方法,那么传入的就是target对象,也就是ServiceA对象本身了。所以不管在a方法还是c方法打印this,结果都是spring创建的serviceA对象。

猜你喜欢

转载自blog.csdn.net/yytree123/article/details/113727434
今日推荐