springboot实现AOP切面编程

概述

AOP(Aspect Oriented Programming) 即面向切面编程。面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程(将交叉业务逻辑封装成成切面,利用AOP功能将切面织入到主业务逻辑———与主业务逻辑无关的代码,使用场景如:安全检查,事物,日志等 。

AOP是一种方式,实现AOP的库有名的有两个AspectJ和spring AOP.

核心概念

  • AOP的相关术语 连接点(JoinPoint) : 连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点(被代理对象的所有方法)

  • 切点(Pointcut): 切入点是指我们要对哪些Joinpoint进行拦截的定义(被代理对象加强的方法)

  • 通知(Advice) : 通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

  • 目标对象(target): 代理的目标对象, 被通知的对象,也就是真正的业务逻辑;

  • 织入(weaving) : 是指把增强应用到目标对象来创建新的代理对象的过程(生成代理对象的过程)

  • 切面(Aspect) : 是切入点和通知的结合,以后咱们自己来编写和配置的

AOP的通知类型

  • 前置通知 : 在目标类的方法执行之前执行。

    • 配置文件信息:
    • 应用:可以对方法的参数来做校验
  • 最终通知 : 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。

    • 在配置文件中编写具体的配置:

    • 应用:例如像释放资源

  • 后置通知 : 方法正常执行后的通知。

    • 在配置文件中编写具体的配置:

    • 应用:可以修改方法的返回值

  • 异常抛出通知 : 在抛出异常后通知

    • 在配置文件中编写具体的配置:

    • 应用:包装异常的信息

  • 环绕通知: 方法的执行前后执行。环绕通知使用灵活.其他方式都能用环绕方式实现。

    • 在配置文件中编写具体的配置:

    • 要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。

使用aspectj实现AOP功能

在pom.xml中引入依赖:

  	
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>5.0.8.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
            <version>1.8.9version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.8.9version>
        dependency>
        
方式一:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
//注解表明这是一个AspectJ文件,编译器在编译的时候,就会自动去解析,然后将代码注入到相应的JPonit中。
@Aspect //必须的注解
@Order(-1)
public class AOPDemo {
    
    
  
    @Pointcut(value = "execution(* com.controller.update(..))")
    private void beforePointcut(){
        //切面,方法里的内容不会执行
    }
    @Before(value = "beforePointcut()")
    public void before(JoinPoint joinPoint){
    	//@Before是在方法执行前无法终止原方法执行
        System.out.println("前置通知。。。"+joinPoint);
    }
    
    
    @Pointcut(value = "execution(* com.controller.save(..))")
    private void afterReturningPointcut(){}
    @AfterReturning(value = "afterReturningPointcut()",returning = "result")
    public void afterReturning(Object result){
        System.out.println("后置通知。。。"+result);
    }

    
    
    
    @Pointcut(value = "execution(* com.controller.delete(..))")
    private void aroundPointcut(){}
    @Around(value = "aroundPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		//用@Around用环绕通知可以拒绝可以放行
        System.out.println("环绕通知。。。");
        Object obj = joinPoint.proceed();
        System.out.println("环绕通知。。。");

        return obj;
    }

    
    
    @Pointcut(value = "execution(* com.controller.findOne(..))")
    private void afterThrowingPointcut(){}
    @AfterThrowing(value = "afterThrowingPointcut()",throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("异常抛出通知"+e.getMessage());
    }

    
    
    
  	@Pointcut(value = "execution(* com.controller.findAll(..))")
    private void afterPointcut(){}
    //|| 关联多个切面
    @After(value = "afterPointcut() || afterReturningPointcut()")
    public void after(){
        System.out.println("最终通知");
    }
  

}
  • execution: 处理JPoint的类型,例如call,execution

    例如定义切入点表达式 execution (* com.sample.service.impl…*. *(…)) 整个表达式可以分为五个部分:

    1、execution(): 表达式主体。

    2、第一个*号:表示返回类型, *号表示所有的类型。

    3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

    4、第二个*号:表示类名,*号表示所有的类。

    5、*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数

多个切点例如:
@Before("execution (
android.app.Activity.onCreate(…)) ||execution (* android.app.Activity.onDestroy()) "

JoinPoint 对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用api:

  • Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类
    Class等信息
  • Object[] getArgs(); 获取传入目标方法的参数对象
  • Object getTarget(); 获取被代理的对象
  • Object getThis(); 获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了

  • Object proceed() throws Throwable //执行目标方法 ,放行进入被代理对象的方法
  • Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
    两个方法.
方式二使用注解:

1.首先,我们需要自定义一个注解类

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR,ElementType.METHOD})
public @interface AopLog {}

2.然后,创建一个切面文件,内部通过一个Pointcut来指定在带有我们上面自定义注解类AopLog注解的所有方法上进行拦截。

    @Aspect
    public class AopLogPointcut {
        //在带有AopLog注解的方法进行切入(注:此处的 * *前面都要有一个空格)
        @Pointcut("execution(@com.yn.aspectj.selfmakeaop.AopLog * *(..))")
        public void logPointcut(){} //注意,这个函数必须要有实现,否则Java编译器会报错

        @After("logPointcut()")
        public void onLogPointcutAfter(JoinPoint joinPoint) throws Throwable{
            Log.i("AOP","onLogPointcutAfter:"+joinPoint.getSignature());
            }
    }

3.最后,只需要在要进行切入的函数前加上@AopLog注解即可。
public class MainActivity extends Activity {

         @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            testAopLog();
        }

        @AopLog
        public void testAopLog() {
            Log.i("AOP","in testAopLog");
        }
    }

你可能感兴趣的:(java开发笔记)