Spring Aop

      AOP解决的问题:是面向切面编程,将核心业务代码与外围业务(日志记录、权限校验、异常处理、事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一。

        AOP的实现有Aspectj(静态代理)、JDK动态代理、CGLIB动态代理,SpringAOP不是一种新的AOP实现,其底层采用的是JDK/CGLIB动态代理。

       AspectJ是一个面向切面的框架,是最首创的AOP技术,用来提供全面的AOP方案,Aspectj是一种静态代理,而SpringAOP是动态代理。但Aspectj的一套定义AOP的API非常好,直观易用。所以Spring引入了Aspectj.

      SpringAOP 不是一个完备的AOP 方案,它是Spring提供的一个标准易用的aop框架,依托Spring的IOC容器,提供了极强的AOP扩展增强能力,对项目开发提供了极大地便利。

  •  Spring中,有两种方式可以实现动态代理--JDK动态代理和CGLIB动态代理。
  • SpringAOP不是一种新的AOP实现,使用了 AspectJ库的注解并且使用 AspectJ库对切点表达式进行解析和匹配,但AOP运行时并不使用 AspectJ的编译器和织入,仍然是使用纯粹的SpringAOP实现,代理部分仍然是使用的JDK动态代理和Cglib代理。
  • SpringAOP配置方式核心是Advisor,可以自定义Advisor,也可以通过AspectJ间接定义Advisor
  • SpringAOP的实现遵循了AOP联盟规范,AOP联盟顶级API接口贯穿了整个AOP过程 

1.静态代理

      静态代理就是将一个实现类,使用委托代理的方式让其去执行。比如我们需要买东西,可让跑腿代理我们去购买,至于跑腿怎么买,在哪买我们不用关心,我们只需要接收购买的东西就行。

/**
 * 定义抽象的类,代表抽象的角色
 */
public interface Customer {
    void shop(String shopName);
}
/**
 * 实现抽象的业务,小明需要买东西
 */
public class XiaoMingCustomer implements Customer {
    @Override
    public void shop(String shopName) {
        System.out.println("小明需要买" + shopName);
    }
}
/**
 * 代理的角色,跑腿
 */
public class Proxy implements Customer {

    private Customer customer;

    public Proxy(Customer customer) {
        this.customer = customer;
    }

    @Override
    public void shop(String shopName) {
        System.out.println("===调用前====");
        this.customer.shop(shopName);
        System.out.println("===调用后====购买的东西" + shopName);
    }
}
    public static void main(String[] args) {
        Customer customer = new XiaoMingCustomer();

        Proxy proxy = new Proxy(customer);

        proxy.shop("盐");
    }

 

 2.JDK动态代理

jdk动态代理通过实现InvocationHandler接口和实现类进行绑定,通过 Proxy.newProxyInstance得到代理类的实例。

Proxy.newProxyInstance(iCustomerInstance.getClass().getClassLoader(), iCustomerInstance.getClass().getInterfaces(), this);

新增一个抽象类

/**
 * 定义抽象的类,代表抽象的角色
 */
public interface ICustomer {
    void shop(String shopName);
}

 创建一个实现类,实现业务

/**
 * 实现抽象的业务,小明需要买东西
 */
public class CustomerImpl implements ICustomer {
    @Override
    public void shop(String shopName) {
        System.out.println("小明需要买" + shopName);
    }
}

将代理类和实现类进行绑定

/**
 * Customer的代理类
 */
public class CustomerInvocationHandler implements InvocationHandler {

    ICustomer iCustomerInstance;

    public Object bind(ICustomer iCustomerInstance) {
        this.iCustomerInstance = iCustomerInstance;
        return Proxy.newProxyInstance(iCustomerInstance.getClass().getClassLoader(), iCustomerInstance.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----调用前处理");
        method.invoke(this.iCustomerInstance, args);
        System.out.println("----调用后处理");
        return null;
    }

    public static void main(String[] args) {
        ICustomer iCustomer = new CustomerImpl();
        CustomerInvocationHandler customInvocationHandler = new CustomerInvocationHandler();
        ICustomer bindProxy = (ICustomer) customInvocationHandler.bind(iCustomer);
        bindProxy.shop("盐");
    }
}

 代理类增强,使用反射机制

public class CustomerEnhanceInvocationHandler implements InvocationHandler {

    private Object implClassInstance;

    public Object bind(Class<?> implClass) {
        try {
            implClassInstance = implClass.newInstance();
        } catch (Exception e) {
        }
        return Proxy.newProxyInstance(implClassInstance.getClass().getClassLoader(), implClassInstance.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----代理增强调用前处理");
        method.invoke(this.implClassInstance, args);
        System.out.println("----代理增强调用后处理");
        return null;
    }

    public static void main(String[] args) {
        CustomerEnhanceInvocationHandler customInvocationHandler = new CustomerEnhanceInvocationHandler();
        ICustomer bindProxy = (ICustomer) customInvocationHandler.bind(CustomerImpl.class);
        bindProxy.shop("盐");
    }
}

3.CGLIB动态代理

        通过Enhancer.create得到代理类的实例,通过实现MethodInterceptor接口和业务类进行绑定。

新建一个业务类

public class Customer {
    public void shop(String shopName) {
        System.out.println("小明需要买" + shopName);
    }
}

新建一个实现MethodInterceptor接口类,对代理和业务类进行绑定。

public class CustomerMethodIntercepto implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----调用前处理");
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("----调用后处理");
        return obj;
    }

    public static void main(String[] args) {
        Enhancer en = new Enhancer();
        en.setSuperclass(Customer.class);
        en.setCallback(new CustomerMethodIntercepto());

        Customer customer = (Customer) en.create();
        customer.shop("盐");
    }
}

4.Spring AOP实现方式:

1.直接配置Advisor(实现接口)
        实现Advisor接口,定义拦截器和拦截规则(切点)。需要定义三个类,一个Advisor的实现类,一个Advice实现类(拦截器),一个aop适配过滤器(这里使用的Advisor为派生的PointcutAdvisor ,需要定义PointCut切点)
2.间接配置Advisor(常用的注解方式)
使用Aspectj jar包提供的注解定义AOP配置,由Spring解析配置生成Advisor。

  1. 前置通知:在我们执行目标方法之前运行(@Before,参数)
  2. 后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
  3. 返回通知:在我们的目标方法正常返回值后运行(@AfterReturning
  4. 异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
  5. 环绕通知:动态代理, 需要手动执行procced()方法(其实就是执行我们的目标方法执行之前相当于前置通知,执行之后就相当于我们后置通知(@Around​​​​​

4.1.Spring AOP支持的AspectJ切入点指示符

spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:

         execution:用于匹配方法执行的连接点;

         within:用于匹配指定类型内的方法执行;

         this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

         target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

         args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

         @within:用于匹配所以持有指定注解类型内的方法;

         @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

         @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

         @annotation:用于匹配当前执行方法持有指定注解的方法;

         bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

         reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

       AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。

4.2. 通知参数 

1) JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据:

        getSignature() //获取修饰符+ 包名+组件名(类名) +方法名

        getSignature().getName() //获取方法名

        getSignature().getDeclaringTypeName()//获取包名+组件名(类名)

2)ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法:

3) JoinPoint.StaticPart:提供访问连接点的静态部分,如被通知方法签名、连接点类型等:

4.3 实现方式

新建一个注解,切入点使用注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLogAnnotation {

    String name();
}

 新建一个测试业务类

@Service
public class CustomService {

    @Autowired
    ApplicationContext applicationContext;

    public void passByIoc() {
        applicationContext.getBean(CustomService.class).printMessage("passByIoc调用");
    }


    /**
     * 使用this方法调用printMessage不会被AOP拦截
     *
     * @param args
     */
    public void passByThis(Integer args) {
        this.printMessage("passByThis调用");
    }

    @MyLogAnnotation(name = "this is MyLogAnnotation printMessage")
    public String printMessage(String message) {
//        System.out.println("printMessage执行了;message=" + message);
        return "printMessage return!";
    }
}

   新建一个切面类

@Aspect
@Component
public class AopAspect {

    //    @Before(value = "execution(* com.lx.lxbootaop.aopimpl.components.ExpressionComponent.passByIoc())")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("AOP拦截到==passByIoc:" + joinPoint.getSignature());
    }

    //    @Before(value = "execution(* com.lx.lxbootaop.aopimpl.components.ExpressionComponent..*(java.lang.Integer))")
    public void doBeforeArgs(JoinPoint joinPoint) {
        System.out.println("aop==passByThis:" + joinPoint.getSignature());
    }

    @Pointcut("execution(* com.lx.lxbootaop.aopimpl.service.CustomService.printMessage(*))")
    public void pointPrintMessage() {

    }

    /**
     * 执行之前
     *
     * @param joinPoint
     */
    //    @Before(value = "execution(* com.lx.lxbootaop.dynamicproxy.components.ExpressionComponent.printMessage(*))")
    @Before("pointPrintMessage()")
    public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().toString();
        String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
        System.out.println("+++AOP 之前:" + name + "++++args:" + args);
    }

    /**
     * 执行返回之前
     *
     * @param joinPoint
     * @param pringMessageResult
     */
    @AfterReturning(value = "pointPrintMessage()", returning = "pringMessageResult")
    public void afterReturning(JoinPoint joinPoint, Object pringMessageResult) {
        String name = joinPoint.getSignature().toString();
        String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
        System.out.println("---AOP返回之前:" + name + "---args=" + args + "result=" + pringMessageResult);
    }

    /**
     * 执行返回之后
     */
    @After(value = "pointPrintMessage()")
    public void aferReturning(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().toString();
        System.out.println("---AOP返回之后:" + name);
    }


    /**
     * 执行异常返回之前
     *
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "pointPrintMessage()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        String name = joinPoint.getSignature().toString();
        System.out.println("---AOP返回异常:" + name + "----Exception=" + exception.getMessage());
    }


    @Around(value = "pointPrintMessage()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("---AOP环绕之前:" + joinPoint.getSignature());
        Object result = joinPoint.proceed(joinPoint.getArgs());
        System.out.println("---AOP环绕之后:" + joinPoint.getSignature() + "+++result=" + result);
        return result;
    }


    @Pointcut("@annotation(com.lx.lxbootaop.aopimpl.MyLogAnnotation)")
    public void pointAnnotation() {

    }

    /**
     * 注解形式
     */
    @Before("pointAnnotation() && @annotation(myLogAnnotation)")
    public void annotationBefore(JoinPoint joinPoint, MyLogAnnotation myLogAnnotation) {
        String args = Arrays.stream(joinPoint.getArgs()).map(row -> row.toString()).collect(Collectors.joining(","));
        System.out.println("&&&AOP拦截到注解 Before&&& args=" + args + ";name=" + myLogAnnotation.name());
    }
}

 

参考:

SpringAOP学习--SpringAOP简介及原理_程序源程序的博客-CSDN博客_springaop

Spring AOP详细介绍_Trycol的博客-CSDN博客

Spring AOP 和 AspectJ的区别_wonder why的博客-CSDN博客_aspectj

Spring 之AOP AspectJ切入点语法详解(最全面、最详细。)_Rulon147的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值