Spring中Aspectj和Schema-based AOP混用引起的错误

简介:          前几天要在项目中增加一个新功能用来监控某些模块的运行情况,自然就想到了使用Spring的AOP来实现。之前已经有类似的AOP代码,使用的是Schema-based形式配置的,也就是在Spring的ApplicationContext.xml中加入了: sampleService sampleAdvice 其中sampleService和sampleAdvice都是通过:自动引入的。

         前几天要在项目中增加一个新功能用来监控某些模块的运行情况,自然就想到了使用Spring的AOP来实现。之前已经有类似的AOP代码,使用的是Schema-based形式配置的,也就是在Spring的ApplicationContext.xml中加入了:

       <bean id="handlerBeanNameAutoProxyCreator"
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames">
			<list>
				<value>sampleService</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>sampleAdvice</value>
			</list>
		</property>
	</bean>        
其中sampleService和sampleAdvice都是通过:

<context:component-scan base-package="com.nokia.myapp"/>
自动引入的。

        这次要加的功能需要监控很多类,如果按照这个配置,就需要在beanNames的list中增加很多bean,并且还要修改原来的sampleAdvice代码判断是否是属于sampleService,无疑增加了程序的复杂度,这不是我想要的。这时就考虑使用AspectJ的Annotation,编程迅速,不会破坏现在的代码结构,也易于以后的维护。按照Spring Reference Document中@AspectJ Support章节的例子,很快写好了代码:

package com.nokia.myapp.aop.monitor;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class NewAspectjAOP{
	private Logger logger = LoggerFactory.getLogger("profile");
	
	@Around("execution(* com.nokia.myapp.client..*.*(..))")
	public Object serviceProfile(ProceedingJoinPoint pjp) throws Throwable {
		//Do something before method execution
		
		Object ret = null;
		Throwable th = null;
		
		try {
			ret = pjp.proceed();
		} catch(Throwable thr){
			th = thr;
		}
		
		//Do something after method execution
		
		if(th != null){
			throw th;
		}
		
		return ret;
	}
}
提交代码到测试服务器上进行测试,结果启动时就遇到了第一个问题:

 ERROR [org.springframework.web.servlet.DispatcherServlet] [Thread-2] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service1' defined in file [/opt/myapp/bin/WEB-INF/classes/com/nokia/myapp/Service1.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.nokia.myapp.Service1]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service2': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.nokia.myapp.Service1 com.nokia.myapp.Service2.locationService; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'service1': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:965) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:911) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
……

        根据提示查看代码,发现在Service2中通过@Autowired引入了Service1,而在Service1中也通过@Autowired引入了Service2。于是去掉了Service2在Service1中引用,而后在需要用的时候通过ApplicationContext.getBean(String beanName)获取Service2。

        修正了上面的错误,再次测试,程序可以正常启动,但是有些程序运行出现了奇怪的问题:每次ApplicationConext.getBeansOfType(Class class)获取sampleService时,总是会获取到其他的Bean,导致程序运行异常(不过本地Eclipse中运行测试没有问题)。使用Eclipse的Remote Debug功能连到开发服务器上调试,发现其他类getClass()获取的都是class com.nokia.myapp.Service2$$EnhancerByCGLIB$$ccadc5e,而SampleService却是$Proxy119。百思不得其解。

        最后请教了对Spring框架很熟悉的同事,终于发现了问题的原因:

由于@AspectJ声明的AOP和在Spring的配置文件中配置的AOP都监测了同一个类SampleService。于是在运行过程中,SampleService实现了CGLIB的三个接口,而根据Spring Reference Document中Proxying mechanisms

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

Spring又在实现了接口的SampleService的代理类外面包了JDK的代理实现,所以就造成了这个问题。

          找到了问题,修复就很容易了,只需要指定ProxyFactoryBean的proxyTargetClass属性:

<bean id="handlerBeanNameAutoProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" p:proxyTargetClass="true">


目录
相关文章
|
1月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
43 1
|
1月前
|
Java Spring 容器
Spring的AOP失效场景详解
Spring的AOP失效场景详解
100 0
|
28天前
|
设计模式 Java Maven
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
34 1
|
2月前
|
XML Java 数据格式
5个点轻松搞定Spring AOP底层实现原理
AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:
46 1
|
21天前
|
XML Java Maven
Spring之Aop的注解使用
Spring之Aop的注解使用
|
27天前
|
Java Spring
Spring 如何实现 AOP
Spring 如何实现 AOP
17 0
|
1月前
|
Java 编译器 程序员
Spring AOP 和 AspectJ 的比较
Spring AOP 和 AspectJ 的比较
37 0
|
1月前
|
Java Spring
【spring(三)】AOP总结
【spring(三)】AOP总结
|
1月前
|
Java 开发者 Spring
Spring AOP精讲
Spring AOP精讲
24 0
|
5月前
|
Java 编译器 数据安全/隐私保护
自定义注解与AOP结合使用
自定义注解与AOP结合使用
59 0