Spring学习-06课SpringAOP @Aspectj使用详解

247 阅读3分钟

AOP @Aspecj使用过程

1、pom.xml加入依赖关系 spring-context aspectj spring-aop

注:去掉aspectjweaver运行时状态

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.25</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.19</version>
        <!--<scope>runtime</scope>-->
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.25</version>
    </dependency>


</dependencies>

2、创建切面类

package com.kdy.aspects;


import com.kdy.annotation.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect    //标记切面
@Component  //标记为bean组件
public class LogAspect {

    @Pointcut("execution(* com.kdy.service.impl.*.*(..))")
    public void pp(){}



    //前置通知
    //@Before("execution(* com.kdy.service.impl.*.*(..))")
   // @Before("execution(public * com.kdy.service.impl.*.add(..))")
   //@Before("within(com.kdy.service..*)")
   // @Before("!@annotation(com.kdy.annotation.Logger)")
    //@Before("execution(* com.kdy.service.impl.*.*(..))")
    //@Before("@annotation(com.kdy.annotation.Logger)")
    //@Before("pp() && @annotation(logger)")

    public void before(JoinPoint joinPoint, Logger logger) {
   // public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
       System.out.println("注解信息"+logger.doname());
        System.out.println(name+"方法运行了,参数是"+Arrays.asList(args));

        System.out.println("前置通知1");
    }

    //后置通知
   // @After("execution(* com.kdy.service.impl.*.*(..))")
    @After("pp()")
    public void after(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name);
        Object[] args = joinPoint.getArgs();

        System.out.println(Arrays.asList(args));

        System.out.println("后置通知");
    }

    //后置异常通知
    @AfterThrowing(value = "execution(* com.kdy.service.impl.*.*(..))",throwing = "ex")
    public void afterThrow(JoinPoint joinPoint,Exception ex) {
        System.out.println(ex);
        System.out.println("后置异常通知");
    }


    //后置返回通知

    @AfterReturning(value = "execution(* com.kdy.service.impl.*.*(..))",returning = "returnValue")
    public void afterReturning(JoinPoint joinPoint,Object returnValue ) {
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(returnValue);
        System.out.println("后置返回通知");
    }

    //环绕通知
    @Around("pp()")
    public Object arround(ProceedingJoinPoint proceedingJoinPoint){
        String name = proceedingJoinPoint.getSignature().getName();
        Object[] args = proceedingJoinPoint.getArgs();
        Object proceed=null;
        try {
            System.out.println("环绕前通知"+name+"方法,参数是"+Arrays.asList(args));
            //利用反射调用目标方法 就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知"+name+"方法 返回值是"+proceed);
        }catch (Throwable e){
            System.out.println("环绕异常通知"+name+"方法 异常是"+e);
        }finally {
            System.out.println("环绕通知 "+name+"方法的后置通知");
        }

        //返回值
        return proceed;
    }

}

3、spring xml配置加入aop

<context:component-scan base-package="com.kdy"></context:component-scan>


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

4、添加相关业务代码及测试代码

package com.kdy.tests;

import com.kdy.entity.User;
import com.kdy.service.IUserService;
import com.kdy.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test01() throws Exception {
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:/spring_aop.xml");
        //UserServiceImpl bean = ioc.getBean(UserServiceImpl.class);
      //  IUserService bean = ioc.getBean(IUserService.class);
        //Object bean = ioc.getBean("userServiceImpl");

        //System.out.println(bean.getClass());
        //class jdk.proxy2.$Proxy21 JDK动态代理类
        //class com.kdy.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$9ae7e6fc
        //没有代理使用cglib代理类
        IUserService bean = ioc.getBean(IUserService.class);
        bean.select(1);

        System.out.println("#################");

        bean.add(null);



    }
}

AOP切点表达式

execultion(modifiter-pattern? ret-type-pattern decaaring-type-pattern? name-pattern(param-pattern) throw-pattern?)

切入点修饰符

execution: 切入点修饰符

还可以用 within 通过类名匹配,粗粒度的切入点表达式

@annotation() 匹配注解的切入点表达式

例子: @Before("within(com.kdy.service..)")

@annotation(java.lang.Override) //这个是错误的 因为Override 只在源码阶段存在 在class编译文件中可以看到没有@Override注解

访问修饰符

modifiter-pattern?

不写代表可以匹配任何访问修饰符

方法返回值

ret-type-pattern

如果切入点的方法返回值不一样可以用 * 代表任意返回值

自定义的要写完整的限定名

包名类名

decaaring-type-pattern?
* 代表任何包名 但是只能匹配一级
.. 代表子孙都可以匹配

类名可以写* 也可以写成*Impl进行部分匹配

方法名

name-pattern *代表任意方法

参数

自带可以写最后的限定名 自定义的要写完整限定名

如果匹配任意使用..

切入点表达式运算

支持 && || !三种常用运算关系


AOP 通知参数

JoinPoint joinPoint (连接点)

获取方法名 joinPoint.getSingnature().getName();

获取参数名 joinPoint.getArgs();

获取返回值 在注解中使用 returning = "returnValue" 指定 returnValue 为返回值

获取异常

在注解中使用 throwing="ex" 参数为 Excetion ex 捕获异常

简化切入点表达式

@PonitCut("execultion(......)") public void pointCut(){ }

@Before("pointCut()")

获取注解值

@Before("ponitCut() && @annotation(logger)") public void before(JoinPoint joinPoint,Logger logger){ ....... logger.注解参数名();//获取注解值 ...... }

环绕通知

1、proceeding 语义 一系列行动 2、包含了前置通知 后置返回通知 后置异常通知 后置通知 3、 proceed = proceedingJoinPoint.proceed(args); 实际调用执行方法 4、ProceedingJoinPoint 是 JoinPoint 的子类

@Around("pp()")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
    String name = proceedingJoinPoint.getSignature().getName();
    Object[] args = proceedingJoinPoint.getArgs();
    Object proceed=null;
    try {
        System.out.println("环绕前通知"+name+"方法,参数是"+Arrays.asList(args));
        //利用反射调用目标方法 就是method.invoke()
        proceed = proceedingJoinPoint.proceed(args);
        System.out.println("环绕返回通知"+name+"方法 返回值是"+proceed);
    }catch (Throwable e){
        System.out.println("环绕异常通知"+name+"方法 异常是"+e);
    }finally {
        System.out.println("环绕通知 "+name+"方法的后置通知");
    }

    //返回值
    return proceed;
}