在了解动态代理之前,我们首先要理解什么是代理模式,动态代理究竟要解决什么问题。因此,在介绍动态代理之前,先介绍下代理模式(静态代理)
静态代理
充分利用了Java的多态性,通过对接口的多个实现,另代理类与被代理类实现同一个接口,通过代理类调用接口方法,实现了调用被代理类(另一个实现类)的方法。
具体实现:在编写代理类时,在代理类中声明父类接口,构造方法入参即为父类接口。在主方法中创建被代理类对象时,会调用被代理类的构造器,此时通过java的多态性,将接口的实现类声明为父接口,并将其作为参数传入被代理类,这样就可以实现静态代理。
对上边的实现方式,我们举个栗子说明下,我们先简单的创建一个对象和接口:
//被代理类所需实现的接口
public interface DemoInterface {
void doSomeThing();
}
//被代理类
public class Demo implements DemoInterface{
@Override
public void doSomeThing() {
System.out.println("被代理对象方法执行!!");
}
}
然后创建一个代理类:
public class DemoProxy {
private DemoInterface demo;
public DemoProxy(DemoInterface demo) {
this.demo = demo;
}
public void doSomeThing(){
System.out.println("静态代理前置操作。");
demo.doSomeThing();
System.out.println("静态代理后置操作。");
}
}
这样,我们就可以通过代理对象的操作,在被代理对象方法执行之前或之后做一些我们自己的操作了。
//执行结果
静态代理前置操作。
被代理对象方法执行!!
静态代理后置操作。
当然,这种设计模式的简单使用,也会有一些弊端存在,比如:在父接口中如果声明了不需要实现的方法,无论实现类还是被实现类都需要强行实现该方法,代码累赘,实现困难,因此伟大的程序猿们研究了进阶的代理模式,动态代理。
动态代理
动态代理就是为了解决上述代理模式的弊端的,动态代理通过自己的方式创建出代理对象,实际操作时候直接操作代理对象即可,解决了代码冗余的弊端。比如说市面上常见的两种,jdk动态代理和cglib动态代理。
JDK动态代理
JDK动态代理是jdk自带的,是通过java.lang.reflect.Proxy
创建的代理对象,它的创建方式是通过传入java.lang.reflect.InvocationHandler
匿名内部类的方式,在通过反射创建代理对象时让代理对象同时实现被代理的接口和java.lang.reflect.InvocationHandler
接口,从而达到一个动态代理的目的。因此,jdk动态代理需要被代理对象实现一个接口。
使用方式如下:
/**
* JDK动态代理委托类(Demo)必须实现接口只有这样才能生成一个代理类,而且代理类也会默认是该接口的实现
*
*/
public class JDKProxyFactory {
//维护一个目标对象
private Object target;
public JDKProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成一个代理对象。
public Object getProxyInstance(){
/**
* 说明:
* 1:loader 制定当前目标对象使用的类加载器。获取加载器的方法固定。
* 02; In
* ClassLoader loader, :指定当前目标对象使用的类加载器。获取加载器的方法固定。
* Class<?>[] interfaces, 目标对象实现的接口类型,使用泛型方法确认类型
* InvocationHandler h) 事件处理,执行目标对象方法时,会触发事件处理器的方法。会把当前执行的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk代理开始。");
Object invoke = method.invoke(target, args);
System.out.println("jdk代理结束。");
return invoke;
}
});
}
}
//实际调用
DemoInterface jdkProxyInstance = (DemoInterface)new JDKProxyFactory(demo).getProxyInstance();
jdkProxyInstance.doSomeThing();
Cglib动态代理
Cglib并非sun公司官方的代理技术,所以需要Jar包支持。需要cglib、asm等Jar包支持。(Maven依赖如下)
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-commons</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-tree</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
cglib的创建方式完全与JDK不同,他是利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。由于是通过继承被代理类的方式进行的代理模式,因此,在使用cglib时被代理类一定不能被final
修饰。
具体使用如下:
/**
* Cglib动态代理并不需要实现一个接口,它生成的代理类默认继承委托类,所以,委托类不能被final修饰。
*
*/
public class CglibProxyFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成一个代理对象。
public Object getProxyInstance(){
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理开始。");
Object invoke = method.invoke(target, args);
System.out.println("Cglib代理结束。");
return invoke;
}
}
总结
静态代理为代理模式的使用,需要代理类实现很多无用方法,编码上的硬伤让我们在实际应用时候很难会选择它。
JDK动态代理:JDK自带的动态代理,通过反射创建代理对象,会和被代理对象实现同一个接口,在使用aop等技术时是默认的动态代理技术,弊端是需要被代理对象实现一个接口。
Cglib动态代理:非自带的动态代理模式,因此需要引入一些其他的依赖,而且,在使用其他技术(比如Aop)引入动态代理模式时,需要做一些额外的配置。而且,由于是创建子类的实现模式,要求被代理类不能被final
修饰。
性能对比:由于是直接修改class文件的方式,所以在jdk1.7及以前,cglib的效率是略高于jdk动态代理的。但是在jdk的逐版本优化下,在1.8及以后的版本,jdk的效率是略优于cglib的。