spring AOP

AOP:全称是AspectOriented Programming, 即面向切面编程。在不修改源码的基础上,对我们的已有方法进行增强。
说白了就是把我们程序复用的代码抽取出来,在需要执行的时候,使用动态代理的技术,进行增强

优势:

减少重复代码
提高开发效率
维护方便

2. AOP中的术语

  • JoinPoint: 连接点(所有可以被增强的方法)

    ​ 类里面哪些方法可以被增强,这些方法称为连接点. 在spring的AOP中,指的是业务层的类的所有现有的方法。

  • Pointcut: 切入点(具体项目中真正已经被增强的方法)

    ​ 在类里面可以有很多方法被增强,但是实际开发中,我们只对具体的某几个方法而已,那么这些实际增强的方法就称之为切入点

  • Advice: 通知/增强 (具体用于增强方法的代码)

    ​ 增强的逻辑、称为增强,比如给某个切入点(方法) 扩展了校验权限的功能,那么这个校验权限即可称之为增强 或者是通知

    ​ 通知分为:

    ​ 前置通知: 在被增强的方法之前执行.

    ​ 后置通知: 在被增强的方法之后执行. 特点: 可以得到被增强方法的返回值

    ​ 异常通知: 在被增强的方法执行出现异常的时候执行. 如果方法没有异常,不会执行. 特点:可以获得异常的信息

    ​ 最终通知: 指的是无论是否有异常,总是被执行的。

    ​ 环绕通知:在方法之前和方法之后执行. 特点:可以阻止目标方法执行

Spring AOP包含以下几种通知类型:

Before advice:在连接点之前运行但不能阻止执行到连接点的通知(除非它抛出异常)。
After returning advice:在连接点正常完成后要运行的通知(例如,如果方法返回并且不引发异常)。
After throwing advice: 如果方法通过引发异常而退出,则要执行的通知。
After (finally) advice:无论连接点退出的方式如何(正常或异常返回),都要执行的通知。
Around advice:环绕连接点(如方法调用)的通知。这是最有力的通知。around通知可以在方法调用前后执行自定义行为。它还负责通过返回自己的返回值或引发异常来选择是继续到连接点还是快捷地执行通知的方法。

  • Aspect: 切面(所有的通知都是在切面中的),就是存放通知的类

AOP 的底层动态代理实现有两种方案。

​ 一种是使用JDK的动态代理。 这种是早前我们在前面的基础增强说过的。 这一种主要是针对有接口实现的情况。 它的底层是创建接口的实现代理类, 实现扩展功能。也就是我们要增强的这个类,实现了某个接口,那么我就可以使用这种方式了. 而另一种方式是使用了cglib 的动态代理,这种主要是针对没有接口的方式,那么它的底层是创建被目标类的子类,实现扩展功能.

JDK方式
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,参考:Java动态代理详解

但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

public class JDKProxyTest {
    public static void main(String[] args) {
        //使用jdk的动态代理,来代理AccountDao接口
        AccountDao accountDao = (AccountDao) Proxy.newProxyInstance(AccountDao.class.getClassLoader(), new Class[]{AccountDao.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //proxy就是代理对象
                //method就是需要被代理的方法
                //args就是方法的参数
                if (method.getName().equals("findAll")) {
                    //执行查询所有的sql语句
                    String sql = "select * from account";
                    Class.forName("com.mysql.jdbc.Driver");
                    Connection conn = DriverManager.getConnection("jdbc:mysql:///day29?characterEncoding=utf8", "root", "123");
                    PreparedStatement pstm = conn.prepareStatement(sql);
                    ResultSet rst = pstm.executeQuery();
                    List<Account> accountList = new ArrayList<>();
                    while (rst.next()) {
                        int id = rst.getInt("id");
                        String name = rst.getString("name");
                        double money = rst.getDouble("money");
                        Account account = new Account();
                        account.setId(id);
                        account.setName(name);
                        account.setMoney(money);
                        accountList.add(account);
                    }
                    return accountList;
                }
                return null;
            }
        });

        System.out.println(accountDao.findAll());
    }
}

CgLib方式
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

添加坐标

<!--Spring核心容器-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!--SpringAOP相关的坐标-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.7</version>
    </dependency>

使用CgLib方式实现: 第三方的代理机制,不是jdk自带的. 没有实现接口的类产生代理,使用的是字节码的增强技术,其实就是产生这个类的子类。

不需要有接口

public class CglibProxyTest {
    public static void main(String[] args) {
        //使用cglib的动态代理,代理AccountService对象
        AccountService accountService = new AccountService();
        Enhancer enhancer = new Enhancer();

        //设置enhancer的父类
        enhancer.setSuperclass(AccountService.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //o 表示代理对象
                // method表示方法
                //objects表示参数
                if (method.getName().equals("save")) {
                    //先进行权限校验
                    System.out.println("校验添加权限。。。");
                    //再调用save()方法
                    method.invoke(accountService,objects);
                    return null;
                }
                //其它方法就不增强
                return method.invoke(accountService,objects);
            }
        });

        //创建代理对象
        AccountService proxyService = (AccountService) enhancer.create();

        proxyService.save();
    }
}

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类设置成父类,然后设置拦截器,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型。

参考:实战CGLib系列之proxy篇(一):方法拦截MethodInterceptor

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值