java - spring - AOP

AOP: 面向切面编程(Aspect oriented Programming)

说白了就是把常用业务方法打包,在放在需要的位置。这个和OOP(面向对象)是不冲突的,只是为了完善面向对象编程中的一些业务逻辑问题。

比如:

A在运行的时候,打印日志。

B在运行的时候,打印日志。

传统方法:

A运行结束位置写一个log方法。

B运行结束位置写一个log方法。

这样万一是个大项目,有100方法都需要打印日志。万一有改动(比如客户要求改打印格式),全部修改一遍工作量很大。

所以为了解决这个问题,有了AOP编程思想。

 

因为打印log的方法是基本通用的,所以可以专门写一个方法集中管理。然后所有目标的方法执行的时候统一调用这里的方法。把方法分配到需要的位置。

Spring的AOP主要就是干这个的。

 

步骤:

1.  添加一个用类,集合了需要的方法

2. 添加spring的注解(我这里是@Component),交给spring组建管理

3. 添加aspect注解,表示这个是用来aop的类

4. 添加对应注解和表达式用来告诉spring这些方法是在哪些类的什么时候使用

5. 添加配置文件,开启AOP模式

 

java:

 

package com.itheima.service;



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import com.itheima.domain.Account;
import sun.misc.Signal;

@Component
@Aspect
public class AopTest {


    //* * com.itheima.service.AccountService.*(..)  返回任意权限,任意返回类型,的这个类中的任意方法,参数可以是任意。根据具体需求修改

    @Before(value = "execution(public * com.itheima.service.AccountService.aopTest(..))")//在AccountService类的aopTest方法运行前,运行该方法
    public void beforeShowData(){
        System.out.println("目标方法准备运行【Before】");
    }

    @After(value = "execution(public * com.itheima.service.AccountService.*(..))")   //在AccountService类的所有方法运行后,都运行该方法
    public void afterShowData(JoinPoint joinPoint){  //使用 JoinPoint作为参数可以获得方法的详细信息
        Signature s = joinPoint.getSignature(); //获取方法签名
        System.out.println(s.getName() + "方法运行了【After】");
    }

    @AfterReturning(value = "execution(public void com.itheima.service.AccountService.aopTest())", returning = "result")   //在AccountService类的aopTest正常结束后,运行该方法,如果有返回结果,则返回结果存入result(这里是void所以没有- -)
    public void afterReturnShowData(Object result){
        System.out.println("目标方法正常运行完毕了【AfterReturning】");
    }

    @AfterThrowing(value = "execution(public void com.itheima.service.AccountService.aopTest())", throwing = "exception")   //在AccountService类的aopTest抛出异常时,运行该方法,获得异常存入exception
    public void afterThrowData(Exception exception){
        System.out.println("目标方法抛出异常【AfterThrowing】,异常信息是" + exception);
    }


}

 

 

package com.itheima.test;

import com.itheima.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

    @Test
    public void test1() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("springConfig.xml");//手动加载配置文件
        AccountService as = (AccountService)ac.getBean("accountService");
        //as.findAll(); //测试spring能否运行


        as.aopTest();
    }
}

 

 

配置文件中添加:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

结果:

目标方法准备运行【Before】
aop测试。
aopTest方法运行了【After】
目标方法正常运行完毕了【AfterReturning】

 

其他注解: 

1. 上面的方法可以看出,如果一个方法前后都需要处理,那么需要写4次切入点表达式,很麻烦。

@PointCut   可以用来重用方法路径(execution后面跟着的 )

package com.itheima.service;



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import com.itheima.domain.Account;
import sun.misc.Signal;

@Component
@Aspect
public class AopTest {


    //* * com.itheima.service.AccountService.*(..)  返回任意权限,任意返回类型,的这个类中的任意方法,参数可以是任意。根据具体需求修改

    //抽取可重用的切入点(目标方法方法)表达式
    //1. 声明一个空方法
    //2. 添加Pointcut注解
    //3..切入点表达式(就是本来value后面跟着的那一串),作为参数写在Pointcut注解里
    //4, 用这个空方法代替切入点表达式

    @Pointcut("execution(public * com.itheima.service.AccountService.aopTest(..))")
    public void targetMethod(){}



    //@Before(value = "execution(public * com.itheima.service.AccountService.aopTest(..))")//在AccountService类的aopTest方法运行前,运行该方法
    @Before(value = "targetMethod()")
    public void beforeShowData(){
        System.out.println("目标方法准备运行【Before】");
    }

    @After(value = "targetMethod()")   //在AccountService类的所有方法运行后,都运行该方法
    public void afterShowData(JoinPoint joinPoint){  //使用 JoinPoint作为参数可以获得方法的详细信息
        Signature s = joinPoint.getSignature(); //获取方法签名
        System.out.println(s.getName() + "方法运行了【After】");
    }

    @AfterReturning(value = "targetMethod()", returning = "result")   //在AccountService类的aopTest正常结束后,运行该方法,如果有返回结果,则返回结果存入result(这里是void所以没有- -)
    public void afterReturnShowData(Object result){
        System.out.println("目标方法正常运行完毕了【AfterReturning】");
    }

    @AfterThrowing(value = "targetMethod()", throwing = "exception")   //在AccountService类的aopTest抛出异常时,运行该方法,获得异常存入exception
    public void afterThrowData(Exception exception){
        System.out.println("目标方法抛出异常【AfterThrowing】,异常信息是" + exception);
    }

}

 

 

2. 除了Before,After,AfterRunning,AfterThrowing,还有一个 @Round,可以同时实现前面4中注解的作用。

spring主要利用了反射的invoke来代理执行方法, 从而实现AOP功能。round可以看做invoke本身。

package com.itheima.service;



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import com.itheima.domain.Account;
import sun.misc.Signal;

@Component
@Aspect
public class AopTest {


    //* * com.itheima.service.AccountService.*(..)  返回任意权限,任意返回类型,的这个类中的任意方法,参数可以是任意。根据具体需求修改

    //抽取可重用的切入点(目标方法方法)表达式
    //1. 声明一个空方法
    //2. 添加Pointcut注解
    //3..切入点表达式(就是本来value后面跟着的那一串),作为参数写在Pointcut注解里
    //4, 用这个空方法代替切入点表达式

    @Pointcut("execution(public * com.itheima.service.AccountService.aopTest(..))")
    public void targetMethod(){}



    //@Before(value = "execution(public * com.itheima.service.AccountService.aopTest(..))")//在AccountService类的aopTest方法运行前,运行该方法
    @Before(value = "targetMethod()")
    public void beforeShowData(){
        System.out.println("目标方法准备运行【Before】");
    }

    @After(value = "targetMethod()")   //在AccountService类的所有方法运行后,都运行该方法
    public void afterShowData(JoinPoint joinPoint){  //使用 JoinPoint作为参数可以获得方法的详细信息
        Signature s = joinPoint.getSignature(); //获取方法签名
        System.out.println(s.getName() + "方法运行了【After】");
    }

    @AfterReturning(value = "targetMethod()", returning = "result")   //在AccountService类的aopTest正常结束后,运行该方法,如果有返回结果,则返回结果存入result(这里是void所以没有- -)
    public void afterReturnShowData(Object result){
        System.out.println("目标方法正常运行完毕了【AfterReturning】");
    }

    @AfterThrowing(value = "targetMethod()", throwing = "exception")   //在AccountService类的aopTest抛出异常时,运行该方法,获得异常存入exception
    public void afterThrowData(Exception exception){
        System.out.println("目标方法抛出异常【AfterThrowing】,异常信息是" + exception);
    }

    @Around(value = "targetMethod()") //环绕方法,可以综合以上4中方法的功能
    public void aroundData(ProceedingJoinPoint joinPoint){ //ProceedingJoinPoint 是 JoinPoint 的子类
        Object[] oArr = joinPoint.getArgs(); //获得参数

        try {
            System.out.println("Around的before, 方法开始执行");
            joinPoint.proceed(oArr);   //spring使用这个变成代理类来执行目标方法(目标方法本身就不执行了,改用这个执行),可以理解为,把执行方法的那段代码抠出来,换成了这个try catch。这句就是执行方法。
            System.out.println("Around的AfterReturning, 方法正常运行完毕");
        } catch (Throwable throwable) {
            System.out.println("Around的AfterThrowing, 方法抛出异常");
            throwable.printStackTrace();
        }finally {
            System.out.println("Around的After, 方法执行完毕");
        }

    }

}

 

结果:

Around的before, 方法开始执行
目标方法准备运行【Before】
aop测试。
Around的AfterReturning, 方法正常运行完毕
Around的After, 方法执行完毕
aopTest方法运行了【After】
目标方法正常运行完毕了【AfterReturning】

 

可以看出:

1. around 执行顺序优先于 其他方法  (因为Around是直接代替了目标方法,而其他方法是检测目标方法的执行过程然后添加。)

2. around 的after 在  AfterReturning 之前,这是比较正常的, 而其他4种如果全写,after 在  AfterReturning 之后, 执行顺序有问题(Spring的自身bug - -)

 

 

 

 

 

posted @ 2019-11-24 15:00  不咬人的兔子  阅读(140)  评论(0编辑  收藏  举报