侧边栏壁纸
博主头像
小白博主等级

just do it!

  • 累计撰写 60 篇文章
  • 累计创建 77 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Spring中的AOP

小白
2019-11-28 / 0 评论 / 0 点赞 / 93 阅读 / 2,683 字

AOP的使用

AOP即面向切面编程,采用横切的技术将设计多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横切到业务流程的指定位置中;使用案例:项目中记录日志的功能,判断登录功能,参数校验功能等等。

AOP的原理

AOP是通过代理实现的,主要是静态代理以及动态代理两大类,

  • 静态代理:使用AOP框架提供的命令进行编译,从而在编译阶段就可生成AOP代理类,因此也成为代理增强;
  • 动态代理:运行时借助与JDK的动态代理、CGLIB字节码生成技术等,在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。

Spring中的使用(注解方式使用)

maven添加的依赖:

<!--AOP相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
   </exclusions>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

代理切面类

@Log4j2
@Component
@Aspect
public class LoginAspect {
    @Pointcut("execution(public * com.ant.sso.Controller..*.*(..))&&@annotation(com.ant.sso.Annotation.Login)")
    public void loginAdvice(){}

    @Before("loginAdvice()")
    public void loginBefore(JoinPoint joinPoint){
        Class clz=joinPoint.getTarget().getClass();
        Signature signature=joinPoint.getSignature();
        String name=signature.getName();
    }

    @Around("loginAdvice()")
    public Object loginAround(ProceedingJoinPoint proceedingJoinPoint){
        log.info("in loginAOP...");
        try{
            log.info("method name is {}",proceedingJoinPoint.getTarget());
            Object object=proceedingJoinPoint.proceed();
            return object;
        }catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

首先定义切入点通过@Pointcut注解来标注,切入点匹配表达式见下文。然后定义通知类型,Spring中AOP 的通知类型一共五大通知类型:前置通知、环绕通知、后置通知、异常通知、最终通知。

SpringBoot项目中直接在配置类上加注解

@EnableAspectJAutoProxy

即可开启项目的注解功能。

配置文件中开启注解功能则需在spring配置文件中加入

<context:annotation-config/>

注意:配置文件中加入注解功能时一定要去认定切点所在的类可以被Spring容器扫描到。

切点匹配表达式规则

excution

spring切面的最小粒度为方法级别,excution表达式可以明确指定方法返回的类型、类名、方法名、参数名等相关部件,从而精确匹配特定的切点方法,在Spring中大部分需要使用AOP的业务场景也只需要达到方法级别即可,因此excution表达式中的excution的使用时最广泛的。

语法
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
  1. ?表示当前项可以没有即不配置;
  2. modifiers-pattern:方法的可见性,如public,protected;
  3. ret-type-pattern:方法的返回值类型,如int,void等;
  4. declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
  5. name-pattern:方法名类型,如buisinessService();
  6. param-pattern:方法的参数类型,如java.lang.String;
  7. throws-pattern:方法抛出的异常类型,如java.lang.Exception;
举个栗子
@Pointcut("execution(public * com.ant.sso.Controller..*.*(..)))")

表示返回类型为所有,在com.ant.sso.Controller包下及其子包中不限方法名以及参数类型数量匹配切点

within

within表达式的粒度为类,其参数为全路径类名(可使用通配符),表示匹配当前表达式所有类都将被当前方法环绕。

语法
within(declaring-type-pattern)

within表达式只能指定到类级别,如下示例表示匹配com.ant.sso.Controller.UserController中的所有方法:

within(com.ant.sso.Controller.UserController)

within表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配com.ant.sso.Controller包下的所有类,不包括子包中的类:

within(com.ant.sso.Controller.*)

如下表达式表示匹配com.ant.sso.Controller包及子包下的所有类:

within(com.ant.sso.Controller..*)

args

args表达式的作用是匹配指定参数类型和指定参数数量的方法,无论其类路径或者是方法名是什么。这里需要注意的是,args指定的参数必须是全路径的。

语法
args(param-pattern)

如下示例表示匹配所有只有一个参数,并且参数类型是java.lang.Integer类型的方法:

args(java.lang.Integer)

也可以使用通配符,但这里通配符只能使用…,而不能使用*。如下是使用通配符的实例,该切点表达式将匹配第一个参数为java.lang.String,最后一个参数为java.lang.Integer,并且中间可以有任意个数和类型参数的方法:

args(java.lang.String,..,java.lang.Integer)

this和target

this和target表达式中都只能指定类或者接口,在面向切面编程规范中,this表示匹配调用当前切点表达式所指代对象方法的对象,target表示匹配切点表达式指定类型的对象。比如有两个类A和B,并且A调用了B的某个方法,如果切点表达式为this(B),那么A的实例将会被匹配,也即其会被使用当前切点表达式的Advice环绕;如果这里切点表达式为target(B),那么B的实例也即被匹配,其将会被使用当前切点表达式的Advice环绕。

@within

匹配带有指定注解的类

语法
@within(annotation-type)

如下所示示例表示匹配使用com.ant.sso.Annotation.Check注解标注的类:

@within(com.ant.sso.Annotation.Check)

@annotation

使用方式与@within的相似,表示匹配使用@annotation指定注解标注的方法将会被环绕.

语法
@annotation(annotation-type)

如下所示示例表示匹配使用com.ant.sso.Annotation.Check注解标注的方法:

@annotation(com.ant.sso.Annotation.Check)

@args

@within和@annotation分别表示匹配使用指定注解标注的类和标注的方法将会被匹配,@args则表示使用指定注解标注的类作为某个方法的参数时该方法将会被匹配。

语法
@args(annotation-type)

如下示例表示匹配使用了com.ant.sso.Annotation.TypeSet注解标注的类作为参数的方法:

@args(com.ant.sso.Annotation.TypeSet)

以上介绍了一些常用的切点表达式。

AOP通知类型

前置通知:@Before

在目标执行之前执行的通知,前置通知方法可以没有参数,也可以额外接受一个JoinPoint类型的参数,代表当前的连接点,通过该对象可以获取目标对象以及目标方法的相关信息。

注意:如果接受JoinPoint类型参数,必须保证其为方法的第一个参数。

环绕通知:@Around

在目标方法执行之前和之后都可以执行额外代码的通知,在环绕通知中必须显示的调用目标方法,目标方法才会执行,这个显示调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接受一个此类型的形参,spring容器会自动将该对象传入,注意这个参数必须处在环绕通知的第一个形参位置。

环绕通知需要返回返回值,否则不会向调用者返回值(返回者只能拿到一个null);环绕通知有控制目标方法是否执行、有控制是否返回值、改变返回值的能力。

注意:只有环绕通知可以接收ProceedingJoint,而其他通知只能接受JointPoint。使用环绕通知时一定要慎用,不是技术上不可行,而是要小心不要破坏软件分层“高内聚、低耦合”的目标。

后置通知:@After

在目标方法执行后执行的通知,可选择性的接受一个参数JoinPoint来获取连接点的额外信息,但这个参数必须处在参数的第一位。

异常通知:@AfterThrowing

在目标方法抛出异常时执行的通知,可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数第一位,另外还可以配置参数,让通知可以接收到目标方法抛出的异常对象。

最终通知:@return

在目标方法执行后执行的通知,和后置通知不同的是,后置通知是在方法正常返回后执行的通知,如果方法没有正常返回(例如抛出异常),则后置通知不会执行。而最终通知无论如何都会在方法调用后执行,即使目标方法没有正常执行。另外后置通知可以通过配置等到返回值,而最终通知无法得到,最终通知也可以额外接受一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。

JoinPoint&ProceedingJoinPoint

JoinPoint

JoinPoint对象封装了SpringAop中切面方法的信息,在七日面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象。

常用API:

Signature getSignature():获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class信息等。

Object[] getArgs():获取传入目标方法的参数对象。

Object[] getTarget():获取被代理的对象。Object getThis():获取代理的对象。

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只能用在环绕通知—@Around的切面方法中,相比较JoinPoint添加了俩个方法:

Object proceed() throw Throwable 执行目标方法;

Object proceed(Object[] var) throw Throwable:传入新的参数执行目标方法。

注意:切面中使用到JoinPoint、ProceedingJoinPoint对象时,一定要将其作为第一个参数传入切面执行的方法。

0

评论区