Spring AOP

时间:2020-01-18
本文章向大家介绍Spring AOP,主要包括Spring AOP使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

AOP应用场景(横切性问题):

日志记录、权限校验、事务控制、效率检查(统计函数执行时间),散落在各个地方,AOP实现解耦,对这些问题进行统一管理。

基本概念:

  • 连接点(JoinPoint):描述了目标对象上的方法。
  • 切入点(Pointcut):是连接点的集合,描述了满足特定条件的方法的集合。
  • 目标对象(Target):被代理的对象、被增强的对象、原始对象
  • 通知(Advice):在方法执行的什么时间(分为前置、后置、环绕、异常、最终五类)以及做什么(增强的功能)
  • 切面(Aspect):切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!
  • 织入(Weaving):把切面应用到目标对象,并创建出代理对象的过程。只要把切面声明为一个bean,放入到容器中,spring就会自动完成织入过程。

使用示例

启用@AspectJ支持,以下注解参数默认值为false,可以指定为true,意味着后续的aop会强制使用cgLib增强

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AppConfig {}

<aop:aspectj-autoproxy/>

声明一个切面,交由spring来管理

@Component
@Aspect
public class UserAspect {
    /**
     * 定义切入点,匹配UserDao所有方法调用
     */
    @Pointcut("execution(* com.yao.dao.UserDao.*(..))")
    public void pintCut(){
        System.out.println("point cut");
    }
    /**
     * 添加 Advice。
     * 可以不指定JoinPoint参数,也可以指定JoinPoint参数用于获取被增强函数的执行参数
     */
    @Before("com.yao.aop.UserAspect.pintCut()")
    public void beforeAdvice(JoinPoint joinPoint){
        // 打印函数的执行参数
        for(int i = 0; i < joinPoint.getArgs().length; i++) {
            System.out.println(joinPoint.getArgs()[i])
        }
        System.out.println("before");
    }
}

springAop和AspectJ的关系

Aop是一种概念。springAop、AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的。spring AOP提供两种编程风格:

  • @AspectJ support:利用aspectj的注解
  • Schema-based AOP support:xml aop:config 命名空间

spring底层使用的是JDK或者CGLIB来完成的代理。

连接点JoinPoint

execution

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。表达式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

这里问号表示当前项可以有也可以没有,其中各项的语义如下

  • modifiers-pattern:方法的可见性,如public,protected;
  • ret-type-pattern:方法的返回值类型,如int,void等;
  • declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
  • name-pattern:方法名类型,如buisinessService();
  • param-pattern:方法的参数类型,如java.lang.String;
  • throws-pattern:方法抛出的异常类型,如java.lang.Exception;

示例:

@Pointcut("execution(* com.chenss.dao.*.*(..))")  //匹配com.chenss.dao包下的任意接口和类的任意方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")  //匹配com.chenss.dao包下的任意接口和类的public方法
@Pointcut("execution(public * com.chenss.dao.*.*())")  //匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")  //匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")  //匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")  //匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(public * *(..))")  //匹配任意的public方法
@Pointcut("execution(* te*(..))")  //匹配任意的以te开头的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")  //匹配com.chenss.dao.IndexDao接口中任意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")  //匹配com.chenss.dao包及其子包中任意的方法

within

 表达式的最小粒度为类,与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等。示例

@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法
@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

args

匹配指定参数类型和指定参数数量的方法,与包名和类名无关。args匹配的是运行时传递给方法的参数类型

@Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified

this

作用在某个类的所有方法上,当代理对象是目标类或子类时,可以匹配到

JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)

@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个

target

作用在某个类的所有方法上,当被代理对象是目标类或子类时,可以匹配到

@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类
@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法

 测试:IndexDao实现了Dao接口,外部通过getBean(Dao.class)来获取bean。

不强制使用cgLib,即注解参数设置为false时,意味着此时是jdk代理:

使用target(com.dao.IndexDao):能匹配到

使用this:匹配不到,因为代理对象基于接口,与接口Dao实现类IndexDao没有关系

强制使用cgLib:

使用target:匹配不到

使用this:能匹配到,cgLib基于继承,即代理对象是继承了IndexDao的

@annotation

作用方法级别。上述所有表达式都有@ 比如@Target(里面是一个注解类xx,表示所有加了xx注解的类,和包名无关)
@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配带有com.chenss.anno.Chenss注解的方法

bean

匹配bean的名字

@Pointcut("bean(dao1)")//名称为dao1的bean上的任意方法
@Pointcut("bean(dao*)")

注意:上述所有的表达式可以混合使用,||、 &&、 !,如:

@Before("pointCut1()&&!pointCut2()")

Proceedingjoinpoint 和JoinPoint的区别

Proceedingjoinpoint 继承了JoinPoint,添加了proceed()方法,这个是aop代理链执行的方法,用于执行被增强函数。JoinPoint仅能获取相关参数,无法执行被增强函数。JoinPoint的方法

  1. java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
  2. Signature getSignature() :获取连接点的方法签名对象;
  3. java.lang.Object getTarget() :获取连接点所在的目标对象;
  4. java.lang.Object getThis() :获取代理对象本身;

Proceedingjoinpoint的proceed()方法有重载,有个带参数的方法,可以修改目标方法的的参数。通过这个process方法来实现环绕advice,仅当使用Around环绕通知时,才可以注入这个类型的参数

@Around("point()")
public Object fn1(ProceedingJoinPoint pjp) throws Throwable {
    // 环绕通知开始
    Object retVal = pjp.proceed();
    // 环绕通知结束
    return retVal;
}

1

原文地址:https://www.cnblogs.com/hellohello/p/12209510.html