引言
Spring主要有两大思想,一个是AOP,一个是IOC。对于Spring的核心AOP来说,动态代理机制是其核心,想要明白AOP原理,一定要了解动态代理机制。
代理模式
给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即操作者不直接操控原对象,而是通过代理对象简介地操控原对象。
实现
代理模式分为静态代理和动态代理:
- 静态代理:代理类是在编译时就实现好。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
- 动态代理:动态代理类的字节码是在程序运行时由Java反射机制动态生成。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
Spring静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
静态代理之前已经说过 Spring-AOP两种配置方式
Spring动态代理
JDK动态代理(对有实现接口的对象做代理)
实现方式说明
JDK动态代理中 需要了解的两个重要的类或接口 [InvocationHandler 和 Proxy]
- InvocationHandler接口
1 2 3 4 5 6 7
| public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
|
可以将InvocationHandler接口的子类想象成一个代理的最终操作类。
说明:每一个动态代理对象都必须要实现InvocationHandler这个接口,并且每个代理类(Proxy)的实例都关联到了一个handle,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为InvocationHandler这个接口的invoke方法来进行调用。同时在invoke的方法里,可以对被代理对象的方法调用做增强处理(如添加事务、日志、权限认证等操作)。
- Proxy类
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,该类常用的调用方法为newProxyInstance
newProxyInstance方法参数说明如下:
- ClassLoader loader:类加载器,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- Class<?>[] interfaces:得到被代理类全部的接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口,这样我就能调用这组接口中的方法了
- InvocationHandler h:得到InvocationHandler接口的子类实例
实现实例
一、首先定义了一个Subject类型的接口:Subject.java
1 2 3 4 5 6 7 8
| public interface Subject { void study(); String say(String words); }
|
二、接着定义一个接口的实现类,这个类就是我们示例中的被代理对象:RealSubject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class RealSubject implements Subject {
@Override public void study() { System.out.println("I am study now."); }
@Override public String say(String words) { return "I say " + words; } }
|
三、定义一个动态代理类(必须要实现 InvocationHandler 接口):DynamicProxy.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler { private Object subject;
public DynamicProxy(Object subject) { this.subject = subject; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before method");
System.out.println("Method:" + method);
Object invoke = method.invoke(subject, args);
System.out.println("after method");
return invoke; } }
|
四、代理测试类:Client.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) { Subject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxy(realSubject);
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
System.out.println(subject.getClass().getName()); subject.study(); System.out.println(); String string = subject.say("Hello World."); System.out.println(string); } }
|
运行->控制台输出结果如下
CGLib动态代理[对没有实现接口的普通类做代理]
说明
概述
CGLib(Code Generation Library)是一个优秀的动态代理框架,它的底层使用ASM(JAVA字节码处理框架)在内存中动态的生成被代理类的子类。使用CGLib即使被代理类没有实现任何接口也可以实现动态代理功能。但是不能对final修饰的类进行代理。
原理
通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
实现实例
一、定义一个没有实现接口的代理委托类:CGLibRealSubject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class CGLibRealSubject { public void study() { System.out.println("I am study now."); }
public String say(String words) { return "I say " + words; } }
|
二、定义一个CGLib动态代理类: CGLibDynamicProxy.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
public class CGLibDynamicProxy implements MethodInterceptor {
private Object target;
public Object getProxyInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before method");
Object object = proxy.invokeSuper(obj, args);
System.out.println("after method");
return object; } }
|
三、创建测试客户端类:CGLibClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class CGLibClient { public static void main(String[] args) { CGLibDynamicProxy cglib = new CGLibDynamicProxy(); CGLibRealSubject realSubject = (CGLibRealSubject) cglib.getProxyInstance(new CGLibRealSubject()); realSubject.study(); System.out.println(); System.out.println(realSubject.say("Hello World.")); } }
|
运行->控制台输出结果如下
总结
Spirng的AOP的动态代理实现机制有两种,分别是:
1)JDK动态代理
具体实现原理:
- 通过实现InvocationHandlet接口创建自己的调用处理器
- 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
- 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力
Spring通过java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2)CGLib动态代理
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程。
对比
- JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
- CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
注意
- 如果要被代理的对象不是个实现类,那么Spring会强制使用CGLib来实现动态代理。
Spring中配置动态代理方式
通过配置Spring的中**aop:config**标签来显示的指定使用动态代理机制 proxy-target-class=true表示使用CGLib代理,如果为false就是默认使用JDK动态代理。