设计模式:代理模式之动态代理

本文详细介绍了代理模式的概念及其在Java中的应用。重点讲述了两种动态代理模式:JDK动态代理和CGLIB动态代理的区别及实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[b]代理是一种常用的设计模式,其目的是为其他对象提供一种代理以控制(外部对象)对这个被被代理对象的的访问。[/b]由代理类负责为委托类(即被代理类)对象做一些用户处理的操作(如权限限制)或执行完之后的后续操作。
[b]代理模式的特征是:代理类与委托类(即被代理类)有同样的接口,代理类主要负责为委托类(即被代理类)预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。[/b]
线面我们来看一下代理模式的类图:
[img]http://dl2.iteye.com/upload/attachment/0084/7996/8471ebba-2009-31f3-9a55-a21e6decbb7a.jpg[/img]
我们这里主要学习以下动态代理模式,动态代理是在运行时,运用反射机制动态创建代理类。
常用的动态代理分为JDK动态代理和CGLIB动态代理。对于这两种动态代理,[b]JDK的动态代理需要依靠接口实现,如果有些类并没有实现接口,则不能使用JDK动态代理;而CGLIB是针对类来实现代理的,其原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,故不能对final修饰的类进行代理。[/b]
就我们熟知的Spring框架中的AOP默认就是使用JDK动态代理的,如果想强制使用CGLIB实现代理,那就需要添加CGLIB库,并在Spring配置中加入:

<aop:aspectj-autoproxy proxy-target-class="true"/>

就性能而言,CGLIB动态代理要较JDK的K动态代理高一些。

[color=blue][size=large][b]JDK动态代理[/b][/size][/color]
在JDK的动态代理中,除了要实现的接口外,外特别关注java.lang.reflect包下的一个类Proxy和动态代理要实现的接口InvocationHandler。
Proxy是Java动态代理的主类,它提供了一些静态方法,用于为一组儿接口动态地生成代理类及其对象:

//获取指定的代理对象关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
//获取关联于指定类加载器和一组儿接口的动态代理类的对象
static Class getProxyClass(ClassLoader loader,Class[] interfaces)
//判断指定的类是否是一个动态代理类
static boolan isProxyClass(Class cl)
/**
* 非常重要的接口:用于为指定的类加载器、一组儿接口和嗲用处理器生成
* 动态代理累的实例
*/
static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)

再来看一下InvocationHandler接口,它主要定义了一个invoke方法,用于处理动态代理类对象上的方法调用。通常开发者需要实现该接口并在其方法内实现对委托类(被代理类)对象的代理访问:

/**
* 该方法负责处理动态代理类上的方法调用。
* @Param proxy: 代理类的实例
* @Param method: 被调用的方法对象
* @Param args: 调用参数
*/
Object invoke(Object proxy,Method method,Object[] args)

具体代码如下:

package org.pattern;

/**
* 卖货接口
* @author Jacky.Chen
*
*/
public interface ISell {

/**
* 卖货
*/
void sell();

}

[code]
package org.pattern;

/**
* 销售商,实现了ISell接口
* @author JackyChen
*
*/
public class Vendor implements ISell{

@Override
public void sell(){
System.out.println("今天,俺又卖了一件货品!");
}

}

[/code]
[code]
package org.pattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 销售商代理,比如说淘宝上的一个代理商,现有该代理商
* 在网上接到客户的订单,然后他(她)再向销售商Vendor
* 下单赚区差价
* @author JackyChen
*
*/
public class VendorProxyHandler implements InvocationHandler {

private Object objOriginal;

public VendorProxyHandler(Object obj){
this.objOriginal = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//可以在这里添加一些预处理操作,如权限控制等...
Object result = method.invoke(this.objOriginal, args);
//也可以在此处添加一些后置处理操作...
return result;
}

}
[/code]
[code]
package org.pattern;

import java.lang.reflect.Proxy;

public class JDKDynamicProxyExample{

public static void main(String[] args){
//创建销售商
Vendor vendor = new Vendor();
//创建动态代理的处理器
VendorProxyHandler handler = new VendorProxyHandler(vendor);
//创建动态代理类
ISell proxyObj = (ISell) Proxy.newProxyInstance(vendor.getClass().getClassLoader(),
Vendor.class.getInterfaces(), handler);
//卖货
proxyObj.sell();
}

}
[/code]


[color=blue][size=large][b]CGLIB动态代理[/b][/size][/color]
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
下面我们来看一下CGLIB动态代理中涉及的几个类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);
我们在这里简单举例,首先要有一个被代理的类,该类不能是final类:

package org.pattern;

/**
* 需要被代理的类(也就是父类),通过字节码技术创建这个类的子类,实现动态代理
* @author JackyChen
*
*/
public class CglibVendor {

public void sell(){
System.out.println("今天,俺又卖了一件货品!");
}
}

接下来我们来实现代理类:

package org.pattern;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* Cglib代理类
* @author JackyChen
*
*/
public class CglibVendorProxy implements MethodInterceptor{

private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz){
/设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}

/**
* 拦截所有目标类方法的调用
* @param obj: 目标类的实例
* @param method: 目标类方法的反射对象
* @param args: 方法的动态入参
* @param proxy: 代理类实例
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//可以在这里添加一些预处理操作,如权限控制等...
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
//也可以在此处添加一些后置处理操作...
return result;
}

}

调用Cglib动态代理的示例:

package org.pattern;

public class CglibDynamicProxyExample {

public static void main(String[] args){
CglibVendorProxy proxy = new CglibVendorProxy();
//通过生成子类的方式创建代理类
CglibVendor proxyImp = (CglibVendor)proxy.getProxy(CglibVendor.class);
proxyImp.sell();
}
}

从上面的代码量上来看,Cglib动态代理要少很多,并且代码较为清晰而干净。在性能上:[b]CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多[/b],所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值