Spring AOP的一些实践

我运用Spring AOP的场景

在工程中通常会引用其他部门的接口,这些接口实际的运行情况和性能是怎么样的,我需要加上监控。
在这里我的做法是利用AOP的round机制来监控外部API的调用,根据Spring 2.5的文档Chapter6 中的说明,如果只需要简单的对API的调用加以监控的话那么建议使用Spring AOP 当然也可以使用AspectJ的AOP从网上给出的测评结果来说AspectJ的AOP的性能要好一些,因为Spring的AOP是基于动态代理的方式,而AspectJ应该是基于字节码的方式来做的,在这里我并没有复杂的AOP需求,而且根据网上的查询结果其性能开销都在nanosecond级别,从学习成本上来说考虑使用Spring AOP,而且之前我已经有了使用在事务中Spring AOP的经历觉得配置还是很容易接受的。

进入正文

选用什么方式的AOP定义方式,我选择了@Aspect注解的方式,而不是schema的方式因为个人感觉@Aspect方式更加简洁。
那么如何使用Spring AOP,假定有com.company.A这个类需要被添加上切片机制
我们先定义一个Aspect BAspect如下

@Aspect
public class BAspect {
    @Around("execution(public * com.company.*.*.*(..))")
    public Object doInvocationProfiler(ProceedingJoinPoint pjp) throws Throwable {
            // something you want to do         
        try {
            return pjp.proceed();
        } catch (Throwable t) {
            throw t;
        } finally {
            // something you want to do 
        }
    }
}

这里有两个比较重要的地方,一个就是@Aspect注解,这个是AspectJ提供的注解,Spring AOP继续使用了这种注解方式,注意Spring AOP虽然可以使用AspectJ的@Aspect注解但并不是说起AOP方式就是采用的AspectJ的方式。另外注意的是就是@Around注解,这是advice(Spring中文文档中将advice翻译为通知,这里我沿用该翻译),通知的类型分为下面几种:

  1. before advice
  2. after returnning advice
  3. after throwing advice
  4. after finally advice
  5. around advice

详细的信息可以参见Spring reference文档,我这里主要采用的是around通知,因为他可以全方位的控制整个API的调用情况。Around中的表达式是AspectJ的pointcut的表达式( 文档 )。我在这里使用的是execution切片,上面这段切入点匹配的就是com.company包下及其子包的任何类任何方法任何返回的api调用。

写完了Aspect我们要让Spring AOP正确的运行,还需要进行一些配置
依赖包:
为了使用@Aspect注解我们需要加入aspectj的两个包

<dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
</dependency>

applicationContext.xml配置
我们还需要在applicationContext中添加aop的配置,

<!-- enable spring aop aspectj style -->
<aop:aspectj-autoproxy />

另外我还需要将AspectB这个切片添加到context中

<bean id="apsectB" class="com.company.BAspect"></bean>

做完了前两步一部API的切片已经可以正常运行(注意是一部分可以正常运行,至于为什么,先买个关子,稍后再说)

Spring AOP proxy机制

在之前的描述中我就重点说明了Spring的AOP是基于proxy的。但是有一个问题是,jdkdynamicproxy只支持接口形式的代理,也就是说被代理的类需要实现接口,如果没有实现任何接口的类Spring会采用CGLIB的代理方式去代理。如下:

class ServiceA implements IService {
}
class ServiceB {
}

上面这两个类,如果被加入了切片机制,那么ServiceA会采用动态代理的方式而ServiceB会使用CGLIB的proxy方式,如果要使用CGLIB的方式,我们需要在工程中引入CGLIB的依赖

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
</dependency>

做完这些是不是就可以高枕无忧了呢,假设有一个类C

@Service
class C {
     @Autowired
     private ServiceA serviceA;
}

这个时候webapp启动时很可能报,can not set serviceA to Proxy$xx的错误,这其实是一个类型转化的错误,因为ServiceA实现了IService所以会采用JDKDynamicProxy的方式创建代理,而jdkDynamic是不可以直接转化为target对象的,所以导致了报错,如何解决这个问题呢?
我们只需要将aspectj-autoproxy的proxy-target-class属性设置为true既可以解决,这个参数表示使用CGLIB proxy,默认是使用jdsdynamicproxy方式

<!-- enable spring aop aspectj style -->
<aop:aspectj-autoproxy proxy-target-class="true" />

到此就是此次Spring AOP的全部内容,今后我可能还会对AspectJ的AOP方式进行研究。

Meta

Published: April 18, 2012 Author: ivan Comments:   Word Count: 250
Bookmark and Share

Next: JAVA描述符

Previous: 【翻译】HTML、URL以及Javascript转义

Tags

aop aspectj java spring

Article Links

  1. Pointcuts
Comments powered by Disqus