一、简介
代理模式就是给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,其中的代理对象就类似于中介。
代理模式可以分为静态代理和动态代理,静态代理就是在程序运行之前就为每一个委托类分别创建一个代理类,工作量大,不易管理,同时接口一旦发生变化,代理类也得相应修改。而动态代理,是在程序运行期间,通过反射机制动态的创建代理类,会方便很多。
目前实现动态代理主要有两种方法,一是JDK自带的动态代理,但要求代理类和委托类实现共同接口,在接口中定义业务方法。这样当代理对象调用某业务方法时,才能在委托类中找到对应的方法。
对于没有实现任何接口的委托类,可以使用CGLib,它的原理是通过字节码技术为一个类创建子类并在子类中通过方法拦截的技术拦截所有父类方法的调用,并织入横切逻辑,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
二、使用场景
之所以使用代理对象,一是有时出于安全原因需要屏蔽用户对真实对象的访问; 二是还可以在满足开闭原则的基础上,通过给代理类增加额外的功能来扩展委托类的功能,其中核心的业务功能还是由委托类来实现,而代理类只是在业务功能执行的前后加入一些公共的服务,如权限控制、缓存、日志功能。
三、举例
(1)静态代理
/*
* 静态代理:聚合复用
*/
//第一步:创建服务类接口
interface HouseService{
void buyHouse();
void rentHouse();
}
//第二步:创建委托类,实现服务接口中的业务功能
class HouseServiceImpl implements HouseService{
@Override
public void buyHouse() {
System.out.println("执行买房业务");
}
@Override
public void rentHouse() {
System.out.println("执行租房业务");
}
}
//第三步:创建代理类,同样让它实现相同的服务接口,只不过内部的业务功能由被代理对象实现
class HouseServiceProxy implements HouseService{
private HouseService service;//被代理对象
public HouseServiceProxy(HouseService service){
this.service=service;
}
@Override
public void buyHouse() {
System.out.println("买房前准备");
service.buyHouse();
System.out.println("买房后装饰");
}
@Override
public void rentHouse() {
System.out.println("租房前准备");
service.rentHouse();
System.out.println("租房后入住");
}
}
//测试
public class 代理模式 {
public static void main(String[] args) {
//静态代理测试
System.out.println("---------------JDK实现静态代理-------------------");
HouseServiceProxy proxy=new HouseServiceProxy(new HouseServiceImpl());
proxy.buyHouse();
proxy.rentHouse();
}
}
(2)JDK实现的动态代理
/*
* JDK实现动态代理
*/
//第一步:创建服务类接口
interface HouseService_D{
void buyHouse();
void rentHouse();
}
//第二步:创建委托类,实现服务接口中的业务功能
class HouseServiceImpl_D implements HouseService_D{
@Override
public void buyHouse() {
System.out.println("执行买房业务");
}
@Override
public void rentHouse() {
System.out.println("执行租房业务");
}
}
//第三步:实现调用处理器
class MyInvocationHandler implements InvocationHandler{
private Object object;//被代理对象
public MyInvocationHandler(Object obj){
this.object=obj;
}
//代理对象调用的所有方法都将被派遣给该方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object res=null;
if("buyHouse".equals(method.getName())){
System.out.println("买房前准备");
res=method.invoke(object, args);
System.out.println("买房后装饰");
}else if("rentHouse".equals(method.getName())){
System.out.println("租房前准备");
res=method.invoke(object, args);
System.out.println("租房后入住");
}
return res;
}
}
//测试
public class 代理模式 {
public static void main(String[] args) {
//JDK动态代理测试
System.out.println("---------------JDK实现动态代理-------------------");
//创建被代理对象
HouseServiceImpl_D houseServiceImpl=new HouseServiceImpl_D();
//动态创建代理类及代理对象
HouseService_D proxyD=(HouseService_D)Proxy.newProxyInstance(houseServiceImpl.getClass().getClassLoader(),
houseServiceImpl.getClass().getInterfaces(), new MyInvocationHandler(houseServiceImpl));
proxyD.buyHouse();
proxyD.rentHouse();
}
}
(3)CGLIB实现的动态代理
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//实现方法拦截器
public class CglibProxy implements MethodInterceptor {
private Object target;//被代理对象
//获得代理对象
public Object getInstance(Object target) {
this.target = target;
//创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(this.target.getClass());
// 设置回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
/**
*回调方法
* @param obj 表示代理对象
* @param method 表示拦截的方法
* @param args 数组表示参数列表
* @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
* @return 执行结果
*/
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object res=null;
if("buyHouse".equals(method.getName())){
// 注意这里是调用invokeSuper而不是invoke,否则死循环;
// methodProxy.invokeSuper执行的是原始类的方法;
// method.invoke执行的是子类的方法;
System.out.println("买房前准备");
res=methodProxy.invokeSuper(object, args);
System.out.println("买房后装饰");
}else if("rentHouse".equals(method.getName())){
System.out.println("租房前准备");
res=methodProxy.invokeSuper(object, args);
System.out.println("租房后入住");
}
return res;
}
}
//测试
public class 代理模式 {
public static void main(String[] args) {
//JDK动态代理测试
System.out.println("-----------CGLib实现动态代理----------");
HouseService houseService = new HouseServiceImpl();
CglibProxy cglibProxy = new CglibProxy();
HouseServiceImpl proxy = (HouseServiceImpl)cglibProxy.getInstance(houseService);
proxy.buyHosue();
}