一、概念
代理模式是设计模式的一种,提供了对目标对象的另外访问模式,即通过代理对象访问目标对象,特点是,可以在目标对象实现的基础上增强额外功能操作,扩展目标对象功能或者改变目标对象的功能操作,代理模式中,用户不直接操作目标对象,而是通过访问代理对象,在代理方法中,由代理对象进行相关处理。

二、分类
Java代理可以分为静态代理和动态代理。
动态代理又细分为两个不同的实现类库,一是jdk动态代理。二是cglib动态代理。
1)静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
// 枪接口
interface Gun {
void fire();
}
目标类(被代理类)
// 定义手枪类(被代理目标)
class Pistol implements Gun{
@Override
public void fire(){
System.out.println("biu~");
}
}
代理类:
//代理类(代理对象)
class ProxyFactory implements Gun{
//接收保存目标对象
Gun gun;
//创建代理类的对象时,实际传入一个被代理类的对象
public ProxyFactory(Gun gun){
this.gun = gun;
}
@Override
public void fire() {
System.out.println("开火之前需要先进行瞄准");
gun.fire();
System.out.println("验枪,退子弹");
}
}
测试:
public class Test {
public static void main(String[] args) {
Pistol pistol = new Pistol();
ProxyFactory pro = new ProxyFactory(pistol);
pro.fire();
}
}
输出:
开火之前需要先进行瞄准
biu~
验枪,退子弹
特点:
① 在不修改目标对象的情况下,对目标功能进行扩展。
② 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要增加,随着项目的变迁,会导致代码极为臃肿且难以维护。
2)jdk动态代理
不需要代理类实现接口或者继承类,通过Proxy类的静态方法newProxyInstance生成代理类实例,static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler );
方法接收三个参数:
1、代理类的类加载器
2、代理类实现的接口列表
3、代理类(implements InvocationHandler)实例
接口:
public interface Gun {
void fire();
void firefire();
}
目标类,实现Gun接口:
public class Pistol implements Gun {
@Override
public void fire() {
System.out.println("biu~");
}
@Override
public void firefire() {
System.out.println("biu~biu~");
}
}
代理类,实现InvocationHandler接口:
public class GunProxy implements InvocationHandler {
private Gun gun;
public GunProxy(Gun gun) {
this.gun = gun;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开火之前需要先进行瞄准");
Object result = method.invoke(gun, args);
System.out.println("验枪,退子弹");
return result;
}
}
测试:
@Test
public void test0() {
Gun gun = new Pistol();
GunProxy gunProxy = new GunProxy(gun);
gun = (Gun)(Proxy.newProxyInstance(GunProxy.class.getClassLoader(), new Class[]{Gun.class}, gunProxy));
gun.fire();
gun.firefire();
}
输出:
开火之前需要先进行瞄准
biu~
验枪,退子弹
开火之前需要先进行瞄准
biu~biu~
验枪,退子弹
两个方法fire()和firefire() 代理类无需增加方法,即可实现代理操作。
jdk动态代理特点:
① 无惧接口实现类的增多而导致的代理类跟进维护。jdk动态代理帮我们做了代理类和被代理类的绑定操作。
② 代理类无需继承和实现目标类继承和实现的类、接口,代理类只需实现InvocationHandler接口即可。
③ 在静态代理中,目标代理类的方法一旦增加或者修改,需要我们手动维护代理类,太过繁琐。而动态代理则无须手工干预。
3)cglib动态代理
使用cglib代理有几个前提:
① 目标类不能为final
② 目标对象的方法如果为final/static,不会被拦截
③ 需要额外的jar包
手枪类:
public class Pistol {
public void fire() {
System.out.println("biu~");
}
public void firefire() {
System.out.println("biu~biu~");
}
}
代理类或拦截器类:
public class GunProxy implements MethodInterceptor {
private Object target;
public GunProxy(Object target) {
this.target = target;
}
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开火之前需要先进行瞄准");
//执行目标对象的方法
Object returnValue = method.invoke(target, objects);
System.out.println("验枪,退子弹");
return returnValue;
}
}
测试:
@Test
public void test0() {
Pistol pistol = new Pistol();
Pistol gunProxy = (Pistol) new GunProxy(pistol).getProxyInstance();
gunProxy.fire();
gunProxy.firefire();
}
输出:
开火之前需要先进行瞄准
biu~
验枪,退子弹
开火之前需要先进行瞄准
biu~biu~
验枪,退子弹
两个方法fire()和firefire() 代理类无需增加方法,即可实现代理操作。
cglib动态代理特点:
① 目标对象可以是类,使用字节码技术,类不能被final关键字修饰,因为产生的代理对象是继承目标类的,而被final修饰的类,是不能被继承的。
② 目标对象的方法如果为final/static,不会被拦截
③ 目标类无需实现接口,爽!
三、结论
正常情况下推荐使用jdk动态代理和cglib动态代理,减少手工干预,降低代码复杂度。另外提一嘴,Spring 同时利用jdk动态代理和cglib动态代理,MyBatis核心Dao实例返回的就是通过jdk动态代理生成的代理类实例。
网友评论