关于程序员:还不懂Spring-AOP一文带你搞懂动态代理

4次阅读

共计 4541 个字符,预计需要花费 12 分钟才能阅读完成。

  1. Spring AOP
    Spring 是一个轻型容器,Spring 整个系列的最最外围的概念当属 IoC、AOP。可见 AOP 是 Spring 框架中的外围之一,在利用中具备十分重要的作用,也是 Spring 其余组件的根底。AOP(Aspect Oriented Programming),即面向切面编程,能够说是 OOP(Object Oriented Programming,面向对象编程)的补充和欠缺。OOP 引入封装、继承、多态等概念来建设一种对象层次结构,用于模仿公共行为的一个汇合。不过 OOP 容许开发者定义纵向的关系,但并不适宜定义横向的关系,例如日志性能。

对于 AOP 的基础知识,并不是本文的重点,咱们次要来看下 AOP 的外围性能的底层实现机制:动静代理的实现原理。AOP 的拦挡性能是由 java 中的动静代理来实现的。在指标类的根底上减少切面逻辑,生成加强的指标类(该切面逻辑或者在指标类函数执行之前,或者指标类函数执行之后,或者在指标类函数抛出异样时候执行。不同的切入机会对应不同的 Interceptor 的品种,如 BeforeAdviseInterceptor,AfterAdviseInterceptor 以及 ThrowsAdviseInterceptor 等)。

那么动静代理是如何实现将切面逻辑(advise)织入到指标类办法中去的呢?上面咱们就来具体介绍并实现 AOP 中用到的两种动静代理。

AOP 的源码中用到了两种动静代理来实现拦挡切入性能:jdk 动静代理和 cglib 动静代理。两种办法同时存在,各有优劣。jdk 动静代理是由 java 外部的反射机制来实现的,cglib 动静代理底层则是借助 asm 来实现的。总的来说,反射机制在生成类的过程中比拟高效,而 asm 在生成类之后的相干执行过程中比拟高效(能够通过将 asm 生成的类进行缓存,这样解决 asm 生成类过程低效问题)。

上面咱们别离来示例实现这两种办法。

  1. JDK 动静代理
    2.1 定义接口与实现类
    public interface OrderService {
    public void save(UUID orderId, String name);

    public void update(UUID orderId, String name);

    public String getByName(String name);
    }
    下面代码定义了一个被拦挡对象接口,即横切关注点。上面代码实现被拦挡对象接口。

public class OrderServiceImpl implements OrderService {

private String user = null;

public OrderServiceImpl() {}

public OrderServiceImpl(String user) {this.setUser(user);
}

//…

@Override
public void save(UUID orderId, String name) {System.out.println("call save()办法,save:" + name);
}

@Override
public void update(UUID orderId, String name) {System.out.println("call update()办法");
}

@Override
public String getByName(String name) {System.out.println("call getByName()办法");
    return "aoho";
}

}
2.2 JDK 动静代理类
public class JDKProxy implements InvocationHandler {
// 须要代理的指标对象

private Object targetObject;

public Object createProxyInstance(Object targetObject) {
    this.targetObject = targetObject;
    return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
            this.targetObject.getClass().getInterfaces(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 // 被代理对象
    OrderServiceImpl bean = (OrderServiceImpl) this.targetObject;
    Object result = null;
    // 切面逻辑(advise),此处是在指标类代码执行之前
    System.out.println("---before invoke----");
    if (bean.getUser() != null) {result = method.invoke(targetObject, args);
    }
    System.out.println("---after invoke----");
    return result;
}

//…

}
上述代码实现了动静代理类 JDKProxy,实现 InvocationHandler 接口,并且实现接口中的 invoke 办法。当客户端调用代理对象的业务办法时,代理对象执行 invoke 办法,invoke 办法把调用委派给 targetObject,相当于调用指标对象的办法,在 invoke 办法委派前判断权限,实现办法的拦挡。

2.3 测试
public class AOPTest {

public static void main(String[] args) {JDKProxy factory = new JDKProxy();
    //Proxy 为 InvocationHandler 实现类动态创建一个合乎某一接口的代理实例  
    OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl("aoho"));

// 由动静生成的代理对象来 orderService 代理执行程序

    orderService.save(UUID.randomUUID(), "aoho");
}

}
后果如下:

—before invoke—-
call save()办法,save:aoho
—after invoke—-

  1. CGLIB 字节码生成
    3.1 要代理的类
    CGLIB 既能够对接口的类生成代理,也能够针对类生成代理。示例中,实现对类的代理。

public class OrderManager {

private String user = null;

public OrderManager() {}

public OrderManager(String user) {this.setUser(user);
}

//…

public void save(UUID orderId, String name) {System.out.println("call save()办法,save:" + name);
}

public void update(UUID orderId, String name) {System.out.println("call update()办法");
}

public String getByName(String name) {System.out.println("call getByName()办法");
    return "aoho";
}

}
该类的实现和下面的接口实现一样,为了放弃对立。

3.2 CGLIB 动静代理类
public class CGLibProxy implements MethodInterceptor {

 // CGLib 须要代理的指标对象
 private Object targetObject;

   public Object createProxyObject(Object obj) {
    this.targetObject = obj;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(obj.getClass());
    // 回调办法的参数为代理类对象 CglibProxy,最初加强指标类调用的是代理类对象 CglibProxy 中的 intercept 办法 
    enhancer.setCallback(this);
    // 加强后的指标类
    Object proxyObj = enhancer.create();
    // 返回代理对象
    return proxyObj;
}

@Override
public Object intercept(Object proxy, Method method, Object[] args,
                        MethodProxy methodProxy) throws Throwable {
    Object obj = null;
    // 切面逻辑(advise),此处是在指标类代码执行之前
    System.out.println("---before intercept----");
    obj = method.invoke(targetObject, args);
    System.out.println("---after intercept----");
    return obj;
}

}
上述实现了创立子类的办法与代理的办法。getProxy(SuperClass.class)办法通过入参即父类的字节码,扩大父类的 class 来创立代理对象。intercept()办法拦挡所有指标类办法的调用,obj 示意指标类的实例,method 为指标类办法的反射对象,args 为办法的动静入参,methodProxy 为代理类实例。method.invoke(targetObject, args)通过代理类调用父类中的办法。

3.3 测试
public class AOPTest {

public static void main(String[] args) {OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager("aoho"));
    order.save(UUID.randomUUID(), "aoho");
}

后果如下:

—before intercept—-
call save()办法,save:aoho
—after intercept—-

  1. 总结
    本文次要讲了 Spring Aop 动静代理实现的两种形式,并别离介绍了其优缺点。jdk 动静代理的利用前提是指标类基于对立的接口。如果没有该前提,jdk 动静代理不能利用。由此能够看出,jdk 动静代理有肯定的局限性,cglib 这种第三方类库实现的动静代理利用更加宽泛,且在效率上更有劣势。

JDK 动静代理机制是委托机制,不须要以来第三方的库,只有要 JDK 环境就能够进行代理,动静实现接口类,在动静生成的实现类外面委托为 hanlder 去调用原始实现类办法;CGLib 必须依赖于 CGLib 的类库,应用的是继承机制,是被代理类和代理类继承的关系,所以代理类是能够赋值给被代理类的,如果被代理类有接口,那么代理类也能够赋值给接口。

正文完
 0