Day69-回顾Spring篇之AOP(面向切面编程)

Day69-回顾Spring篇之AOP(面向切面编程)

Aop(Aspect Oriented Programming),面向切面编程,这是对面向对象思想的一种补充。面向切面编程,就是在程序运行时,不改变程序代码的情况下,动态的增强方法的功能,常见的使用场景非常多:

  1. 日志
  2. 事务
  3. 数据库操作

这些操作中,无一例外,都有很多模板化的代码,而解决模板化代码,消除臃肿就是Aop的强项。

在aop中,有几个常见的概念:

概念说明
切点要添加代码的地方,称为切点
通知(增强)通知就是相切点动态添加的代码
切面切点+通知
连接点切点的定义

一、Aop的实现

Aop实际上基于Java动态代理来实现的。

Java中的动态代理有两种实现方式:

  • cglib
  • jdk

二、动态代理

基于JDK的动态代理

1.定义一个计算机接口:

public interface MyCalculator{
    int add(int a,int b);
}

2.定义计算机接口的实现:

public class MyCalculatorImpl implements MyCalculator{
    public int add(int a,int b){
        return a+b;
    }
}

3.定义代理类

public class CalculatorProxy{
    public static Object getInstance(final MyCalculatorImpl myCalculator){
        return Proxy.new ProxyInstance(CalculatorProxy.class.getClassLoader(),myCalculator.getClass().getInterfaces(),new InvocationHandler(){
            public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
                System.out.println(method.getName()+"方法开始执行啦...");
              	Object invoke = method.invoke(myCalculator,args);
                System.out.println(method.getName()+"方法执行结束啦...");
                return invoke;
            }
        });
    }
}
Proxy.newProxyInstance方法接收三个参数,第一个是一个classloader,第二个是代理多项实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在invoke方法中实现。

三、Aop五种通知类型

  • 前置通知
  • 后置通知
  • 异常通知
  • 返回通知
  • 环绕通知

具体实现,依然是给计算器的方法增强功能。

首先,在项目中,引入Spring依赖(这次需要引入Aop相关的依赖):

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
</dependencies>

接下来,定义切点,这里介绍两种切点的定义方法:

  • 使用自定义注解
  • 使用规则

其中,使用自定义注解标记切点,是侵入式的,所以这种方式在实际开发中不推荐,仅作为了解,另一种使用规则来定义切点的方式,无侵入,一般推荐使用这种方式。

自定义注解

首先自定义一个注解:

package com.fu.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

}

然后在需要拦截的方法上,添加该注解,在add方法上添加@Action注解,表示该方法将被aop拦截,而其他未添加该注解的方法则不受影响。

import org.springframework.stereotype.Component;

@Component
public class MyCalculatorImpl implements MyCalculator {
    @Override
    @Action
    public int add(int a, int b) {
        System.out.println(a + "-" + b + "=" + (a+b));
        return a+b;
    }
    @Override
    @Action
    public void min(int a, int b) {
        System.out.println(a + "-" + b + "=" + (a-b));
    }
}

接下来,定义增强(通知、Advice)

package com.fu.aop;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
//@Aspect注解表示这是一个切面
@Aspect
public class LogAspect {

    /**
     * 前置通知
     * @param joinPoint 包含了目标方法的关键信息
     * @Before 注解表示这是一个前置通知,即在目标方法之前执行,注解中,需要填入切点
     */
    @Before("@annotation(Action)")
    public void before(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行...");
    }

    /**
     * 后置通知
     * @param joinPoint 包含了目标方法的关键信息
     * @After 表示这是一个后置通知,即在目标方法执行之后执行
     */
    @After("@annotation(Action)")
    public void after(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了...");
    }

    /**
     * 返回通知 可以在该方法中获取目标方法的返回值,如果目标方法的返回值为void,则收到null
     * @param joinPoint
     * @param r 返回参数名称,和这里方法的参数名一一对应
     */
    @AfterReturning(value = "@annotation(Action)",returning = "r")
    public void returning(JoinPoint joinPoint,Object r){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"返回通知:"+r);
    }

    /**
     * 异常通知
     * @param joinPoint
     * @param e 目标方法所抛出的异常,注意,这个参数必须是目标方法所抛出的异常或者所抛出的异常的父类.
     *          只有这样,才会捕获.如果香兰姐所有,参数类型声明为Exception
     */
    @AfterThrowing(value = "@annotation(Action)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法抛出异常了"+e.getMessage());
    }

    /**
     * 环绕通知,环绕通知是上面四种通知集大成这,环绕通知的核心类似在反射中执行方法
     * @param pjp
     * @return
     */
    public Object  around(ProceedingJoinPoint pjp){
        Object proceed = null;
        try{
            //这个相当于method.invoke方法,我们可以在这个方法的前后分别添加日志,就相当于前置/后置通知
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰杰杰のblog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值