Spring AOP是什么?你都拿它做什么?

为什么会有面向切面编程(AOP)?我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限验证、事务等功能时,只能在在每个对象里引用公共行为。这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

为了阐述清楚Spring AOP,我们从将以下方面进行讨论:

1.代理模式。

2.静态代理原理及实践。

3.动态代理原理及实践。

4.Spring AOP原理及实战。

1.代理模式。

代理模式:为其他对象提供一种代理以控制对这个对象的访问。这段话比较官方,但我更倾向于用自己的语言理解:比如A对象要做一件事情,在没有代理前,自己来做,在对A代理后,由A的代理类B来做。代理其实是在原实例前后加了一层处理,这也是AOP的初级轮廓。

2.静态代理原理及实践。

静态代理模式:静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。废话不多说,我们看一下代码,为了方便阅读,博主把单独的class文件合并到接口中,读者可以直接复制代码运行:

package test.staticProxy;
// 接口
public interface IUserDao {
  void save();
  void find();
}
//目标对象
class UserDao implements IUserDao{
  @Override
  public void save() {
    System.out.println("模拟:保存用户!");
  }
  @Override
  public void find() {
    System.out.println("模拟:查询用户");
  }
}
/**
    静态代理
          特点:
  1. 目标对象必须要实现接口
  2. 代理对象,要实现与目标对象一样的接口
 */
class UserDaoProxy implements IUserDao{
  // 代理对象,需要维护一个目标对象
  private IUserDao target = new UserDao();
  @Override
  public void save() {
    System.out.println("代理操作: 开启事务...");
    target.save();   // 执行目标对象的方法
    System.out.println("代理操作:提交事务...");
  }
  @Override
  public void find() {
    target.find();
  }
}

测试结果:
在这里插入图片描述
静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。

3.动态代理原理及实践。

动态代理模式:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。实例如下:

package test.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
public interface IUserDao {
  void save();
  void find();
}
//目标对象
 class UserDao implements IUserDao{
  @Override
  public void save() {
    System.out.println("模拟: 保存用户!");
  }
  @Override
  public void find() {
    System.out.println("查询");
  }
}
/**
 * 动态代理:
 *    代理工厂,给多个目标对象生成代理对象!
 *
 */
class ProxyFactory {
  // 接收一个目标对象
  private Object target;
  public ProxyFactory(Object target) {
    this.target = target;
  }
  // 返回对目标对象(target)代理后的对象(proxy)
  public Object getProxyInstance() {
    Object proxy = Proxy.newProxyInstance(
      target.getClass().getClassLoader(),  // 目标对象使用的类加载器
      target.getClass().getInterfaces(),   // 目标对象实现的所有接口
      new InvocationHandler() {      // 执行代理对象方法时候触发
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
      
      // 获取当前执行的方法的方法名
      String methodName = method.getName();
      // 方法返回值
      Object result = null;
      if ("find".equals(methodName)) {
        // 直接调用目标对象方法
        result = method.invoke(target, args);
      } else {
        System.out.println("开启事务...");
        // 执行目标对象方法
        result = method.invoke(target, args);
        System.out.println("提交事务...");
      }
      return result;
    }
  }
);
    return proxy;
  }
}

测试结果如下:

在这里插入图片描述
在运行测试类中创建测试类对象代码中
IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();

其实是JDK动态生成了一个类去实现接口,隐藏了这个过程:
class $jdkProxy implements IUserDao{}
使用jdk生成的动态代理的前提是目标类必须有实现的接口。但这里又引入一个问题,如果某个类没有实现接口,就不能使用JDK动态代理,所以Cglib代理就是解决这个问题的。

Cglib是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类,如下:

public class UserDao{}
//Cglib是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class extends UserDao{}

Cglib使用的前提是目标类不能为final修饰。因为final修饰的类不能被继承。

现在,我们可以看看AOP的定义:面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑。

通过定义和前面代码我们可以发现3点:

1.AOP是基于动态代理模式。

2.AOP是方法级别的。

3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。

4.spring AOP原理及实战。

前文提到JDK代理和Cglib代理两种动态代理,优秀的Spring框架把两种方式在底层都集成了进去,我们无需担心自己去实现动态生成代理。那么,Spring是如何生成代理对象的?:

1.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。

2.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法。通过查看Spring源码,我们在DefaultAopProxyFactory类中,找到这样一段话。

在这里插入图片描述

到这里,我们已经全部介绍完Spring AOP,回到开篇的问题,我们拿它做什么?

     1.Spring声明式事务管理配置

     2.Controller层的参数校验。ref:Spring AOP拦截Controller做参数校验

     3.使用Spring AOP实现MySQL数据库读写分离案例分析

     4.在执行方法前,判断是否具有权限。

     5.,对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。

     6.信息过滤,页面转发等等功能, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值