1.AOP简介

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志打印、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
Spring专题(六)-Spring核心概念AOP_代理类

2.AOP作用和优势

  • 作用:
    在程序运行期间,不修改源码对已有方法进行增强。
  • 优势:
    减少重复代码
    提高开发效率
    维护方便

假如没有aop,在做日志处理的时候,我们会在每个方法中添加日志处理,比如
Spring专题(六)-Spring核心概念AOP_封装_02
但大多数的日子处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。
Spring专题(六)-Spring核心概念AOP_代理类_03
但这样两个方法就是强耦合的,假如此时我们不需要这个功能了,或者想换成其他功能,那么就必须一个个修改。

通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。这样的思想,被称为面向切面编程,亦即AOP。
Spring专题(六)-Spring核心概念AOP_封装_04

3.AOP 的实现方式

使用动态代理技术

4.需求案例

现在有一张表 User,然后我们要在程序中实现对 User 表的增加和删除操作。
要求:增加和删除操作都必须要开启事务,操作完成之后要提交事务。

public class User {

private int uid;
private String uname;

public int getUid() {
return uid;
}

public void setUid(int uid) {
this.uid = uid;
}

public String getUname() {
return uname;
}

public void setUname(String uname) {
this.uname = uname;
}
}

4.1.代理设计模式

Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
Spring专题(六)-Spring核心概念AOP_动态代理_05
更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。

4.2. 解决办法1:使用静态代理

  • 第一步:创建 UserService 接口
public interface UserService {

// 添加 user
public void addUser(User user);

// 删除 user
public void deleteUser(int uid);
}
  • 第二步:创建 UserService的实现类
public class UserServiceImpl implements UserService {

public void addUser(User user) {
System.out.println("增加 User");
}

public void deleteUser(int uid) {
System.out.println("删除 User");
}
}
  • 第三步:创建事务类
public class MyTransaction {

// 开启事务
public void before() {
System.out.println("开启事务");
}

// 提交事务
public void after() {
System.out.println("提交事务");
}
}
  • 第四步:创建代理类 ProxyUser.java
public class ProxyUser implements UserService {

// 真实类
private UserService userService;
// 事务类
private MyTransaction transaction;

// 使用构造函数实例化
public ProxyUser(UserService userService, MyTransaction transaction) {
this.userService = userService;
this.transaction = transaction;
}

public void addUser(User user) {
transaction.before();
userService.addUser(user);
transaction.after();
}

public void deleteUser(int uid) {
transaction.before();
userService.deleteUser(uid);
transaction.after();
}
}
  • 测试:
public class TestUser {

@Test
public void testOne() {
MyTransaction transaction = new MyTransaction();
UserService userService = new UserServiceImpl();
// 产生静态代理对象
ProxyUser proxy = new ProxyUser(userService, transaction);
proxy.addUser(null);
proxy.deleteUser(0);
}
}

运行结果:
Spring专题(六)-Spring核心概念AOP_封装_06
这是一个很基础的静态代理,业务类UserServiceImpl 只需要关注业务逻辑本身,保证了业务的重用性,这也是代理类的优点,没什么好说的。我们主要说说这样写的缺点:
①、代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
②、如果接口增加一个方法,比如 UserService 增加修改 updateUser()方法,则除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

4.3 解决办法2:使用JDK动态代理(基于接口的动态代理)

动态代理就不要自己手动生成代理类了,我们去掉 ProxyUser.java 类,增加一个 ObjectInterceptor.java 类

public class ObjectInterceptor implements InvocationHandler {

// 目标类
private Object target;
// 切面类(这里指事务类)
private MyTransaction transaction;

// 通过构造器赋值
public ObjectInterceptor(Object target, MyTransaction transaction) {
this.target = target;
this.transaction = transaction;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 开启事务
this.transaction.before();
// 调用目标类方法
method.invoke(this.target, args);
// 提交事务
this.transaction.after();
return null;
}
}
  • 测试类
public class TestUser2 {

@Test
public void testOne() {
// 目标类
Object target = new UserServiceImpl();
// 事务类
MyTransaction transaction = new MyTransaction();
ObjectInterceptor proxyObject = new ObjectInterceptor(target, transaction);
/**
* 三个参数的含义: 1、目标类的类加载器 2、目标类所有实现的接口 3、拦截器
*/
UserService userService = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyObject);
userService.addUser(null);
userService.deleteUser(11);
}
}

运行结果:
Spring专题(六)-Spring核心概念AOP_动态代理_07
那么使用动态代理来完成这个需求就很好了,后期在 UserService 中增加业务方法,都不用更改代码就能自动给我们生成代理对象。而且将 UserService 换成别的类也是可以的。
也就是做到了代理对象能够代理多个目标类,多个目标方法。
不管是哪种方式实现动态代理。本章的主角:AOP 实现原理也是动态代理

提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。

4.4 解决办法3:使用CGLib代理器(基于子类的动态代理)

使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法 newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚.
第二个入参 interfaces就是需要代理实例实现的接口列表.
对于没有通过接口定义业务方法的类,如何动态创建代理实例呢? JDK动态代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这一空缺.
GCLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势志入横切逻辑.

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
  • 创建创建CGLib代理器
public class CglibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();

// 切面类(这里指事务类)
private MyTransaction transaction;

public CglibProxy(MyTransaction transaction) {
this.transaction=transaction;
}

// 设置被代理对象
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}

public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
transaction.before();
Object invoke = methodProxy.invokeSuper(obj, objects);
transaction.after();
return invoke;
}
}
  • 测试类
@Test
public void testOne() {
// 事务类
MyTransaction transaction = new MyTransaction();
CglibProxy cglibProxy = new CglibProxy(transaction);
UserServiceImpl userService = (UserServiceImpl) cglibProxy.getProxy(UserServiceImpl.class);
userService.addUser(null);
userService.deleteUser(11);
}

提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。