在Spring中使用AspectJ实现AOP

 

在Spring中,最经常使用的AOP框架是AspectJ,使用AspectJ实现AOP有2种方式:spring

 

 

基于XML的声明式AspectJ

一、在项目中添加包spring-aspects.jar(spring自带)、aspectjweaver.jar(须要本身下载添加)app

 

 二、新建包user,包下新建类User框架

1 public class User{
2     public void addUser(){
3         System.out.println("正在添加用户");
4         System.out.println("添加用户成功");
5     }
6 }

User可实现接口。函数

 

 

三、新建包my_aspect,包下新建切面类MyAspect。注意是新建Class,不是新建Aspect。测试

 1 public class MyAspect {
 2     //前置通知
 3     public void myBefore(){
 4         System.out.println("正在检查权限");
 5         System.out.println("权限已够");
 6     }
 7 
 8     //后置通知
 9     public void myAfterReturning(){
10         System.out.println("正在记录日志");
11         System.out.println("日志记录完毕");
12     }
13 
14     //环绕通知,同时在先后加强。
15     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
16         myBefore();
17         Object object=proceedingJoinPoint.proceed();
18         myAfterReturning();
19         return object;
20     }
21 
22     //异常通知
23     public void myAfterThrowing(Throwable e){
24         System.out.println("出错了:"+e.getMessage());
25     }
26 
27     //最终通知
28     public void myAfter(){
29         System.out.println("正在释放资源");
30         System.out.println("资源释放完毕");
31     }
32 }

 根据须要,写通知。函数名是自定义的,但不能使用aspectj的关键字。spa

可以使用形参 JoinPoint joinPoint ,经过 joinPoint.getSignature().getName() 获取加强的方法名。代理

 

 

四、在xml中配置日志

<!--目标类-->
    <bean id="user" class="user.User" />
    
    <!--切面类-->
    <bean id="myAspect" class="my_aspect.MyAspect" />
    
    <!--AOP配置-->
    <aop:config>
        <!--配置切面,一个aspect配置一个切面-->
        <aop:aspect ref="myAspect">
            <!--配置全局切入点-->
            <aop:pointcut  id="myPointCut" expression="execution(* user.*.*(..))" />
            <!--配置要使用的通知-->
            <aop:before method="myBefore" pointcut-ref="myPointCut" /> <aop:after-returning method="myAfterReturning" pointcut="execution(void user.User.addUser())" />
        </aop:aspect>
    </aop:config>

 

<aop:pointcut />配置的是全局切入点(全部通知均可引用),可在通知中使用pointcut-ref属性引用。也能够在单个通知中使用pointcut属性配置本身的切入点。

method属性指定这个通知对应的切面类中的方法。若是方法使用了参数JoinPoint(切入点),不用额外传参,pointcut/point-ref传递的就是切入点。

切入点指的就是目标方法。

 

 

  • 前置通知
    <aop:before method="myBefore" pointcut-ref="myPointCut" />

 

 

  • 后置通知
   <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" />

后置通知可以使用returning属性指定指定一个参数,这个参数表示目标方法的返回值,会被传递给后置通知的方法。returning属性只有后置通知能使用。

  <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal" />

 

 

  • 环绕通知(前、后加强)
  <aop:around method="myAround" pointcut-ref="myPointCut" />

环绕方法的参数 ProceedingJoinPoint proceedingJoinPoint 是JoinPoint的子类,不用额外传参。

若是同时使用前置/后置通知、环绕通知,则先执行环绕前,再执行前置(若是有),执行目标方法,执行后置(若是有),而后执行环绕后。

 
  • 异常通知
    <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>

异常方法须要一个参数 Throwable e ,须要用throwing属性传递参数。

异常通知在目标方法发生异常、抛出异常时自动执行,throwing表示的就是目标方法抛出的异常。

目标方法发生异常时,会先执行异常方法,而后在控制台打印错误信息。

 

 

  • 最终通知
    <aop:after method="myAfter" pointcut-ref="myPointCut" />

最终通知是在目标方法执行后、后置方法执行后/环绕方法执行后(若是有),才执行的。

若是以前的代码(目标方法)发生异常或者被异常停止,后置通知以及环绕通知的后半部分是不会执行的。

最终通知,就算前面代码发生异常、被异常停止,也会执行,除非退出JVM。因此常在最终方法中作一些断开链接、关闭资源的操做。

 

 

 

五、新建包test,包下新建测试类Test

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         User user=applicationContext.getBean("user", User.class);
5         user.addUser();
6     }
7 }

 

 

 

 

 

基于注解的声明式AspectJ

基于代理类的AOP很繁琐,基于xml的声明式AspectJ便捷不少,但若是要配置的切面不少,xml文件会很臃肿。基于注解的声明式AspectJ可解决此问题。

基于注解的声明式AspectJ是最经常使用的,简便、xml文件也比较简洁。

 

一、在项目中添加包spring-aspects.jar(spring自带)、aspectjweaver.jar(须要本身下载添加)

 

二、新建包user,包下新建类User(可实现接口)

1 @Repository("user") 2 public class User{
3     public void addUser(){
4         System.out.println("正在添加用户");
5         System.out.println("添加用户成功");
6     }
7 }

本来要在xml中配置目标类的Bean,这里使用注解自动装配。@Repository("user")是Dao层的注解,由于添加用户通常要操做数据库,这里只是写了一个模拟。

咱们显式指定Bean的id/name为user,其实@Repository默认id/name就是类名的camel写法。

 

 

三、新建包my_aspect,包下新建切面类MyAspect

 1 @Aspect 2 @Component  3 public class MyAspect {
 4 //全局切入点 5 @Pointcut("execution(void user.User.*())") 6 private void myPointCut(){}  7 
 8     //前置通知
 9     @Before("myPointCut()") 10     public void myBefore(){
11         System.out.println("正在检查权限");
12         System.out.println("权限已够");
13     }
14 
15     //后置通知
16     @AfterReturning("execution(void user.User.*())") 17     public void myAfterReturning(){
18         System.out.println("正在记录日志");
19         System.out.println("日志记录完毕");
20     }
21 
22     //环绕通知,同时在先后加强。
23     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
24         myBefore();
25         Object object=proceedingJoinPoint.proceed();
26         myAfterReturning();
27         return object;
28     }
29 
30     //异常通知
31     public void myAfterThrowing(Throwable e){
32         System.out.println("出错了:"+e.getMessage());
33     }
34 
35     //最终通知
36     public void myAfter(){
37         System.out.println("正在释放资源");
38         System.out.println("资源释放完毕");
39 
40     }
41 }

 

本来要在xm中配置切面Bean,这里使用@Aspect(使用aspectj配置,至关于<aop:congif>元素)、 @Component(配置为Bean,至关于<bean>元素)  2个注解配置。

 

须要使用注解把要使用的方法标注为对应的通知方法,须要指定切入点。

 

可先配置全局切入点:

    //全局切入点
    @Pointcut("execution(void user.User.*())")
    private void myPointCut(){}

而后在注解中经过   "方法名()"    引用:

    //前置通知
    @Before(value = "myPointCut()")

若是只配置切入点这一个参数,可简写:

    //前置通知
    @Before("myPointCut()")

 

 

也能够自行配置:

     //后置通知
    @AfterReturning("execution(void user.User.*())")

 

 

异常通知比较特殊,须要传递一个异常参数:

    //异常通知
    @AfterThrowing(value = "myPointCut()",throwing = "e")

 

只有使用了注解标注的方法才会做为通知自动调用。

 

其实就是把xml中的配置转换为相应的注解配置。

 

 

 

四、在xml中配置

    <!--启用Spring注解,指定使用了注解的包,有多个包时逗号分隔-->
    <context:component-scan base-package="user,my_aspect" />

    <!--启用AspectJ的注解-->
    <aop:aspectj-autoproxy />

 

 

五、新建包test,包下新建测试类Test

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         User user=applicationContext.getBean("user", User.class);
5         user.addUser();
6     }
7 }
相关文章
相关标签/搜索