最近在慕课网上看了一下代理模式的课程,这里进行一些总结
代理模式
1.什么是代理模式?
为其他对象提供一种代理以控制对这个被代理对象的访问,代理对象起到一个中介的作用,可以去掉功能服务或增加额外的服务
其实简单点说,就是代理对象帮被代理对象处理业务,同时也加上一些自己的业务
2.代理模式分类:
1.远程代理(Remote Proxy):为不同地理的对象提供局域网代表对象
例:通过远程代理监控各个连锁店铺,使其能直观的了解店内信息
2.虚拟代理(virtual Proxy):根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建
例:浏览页面,图片展示部分使用虚拟代理显示,当真实的图片加载完成后显示真实的图片
3.保护代理(Protect Proxy/Access Proxy):控制对象的访问权限
例:网上有些内容或模块必须要注册登录之后才能浏览或操作
4.智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作
例:火车票代售处
3.静态代理和动态代理
1.静态代理
静态代理:代理和被代理对象在【代理之前】都是【确定】的。他们都实现【相同的接口或者继承相同的抽象类】
示例:
这里定义一个接口
package com.hxkj.proxy;
/**
* 行驶的接口
* @author Administrator
*
*/
public interface Moveable {
//move方法
void move();
}
创建一个Car类实现接口
package com.hxkj.proxy;
import java.util.Random;
/**
* 汽车类实现行驶的接口
* @author wanbin
*
*/
public class Car implements Moveable {
@Override
public void move() {
//汽车行驶0-1000以内毫秒数的时间后开始行驶
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("汽车行驶中......");
}
}
创建一个代理类
package com.hxkj.proxy;
/**
* 通过聚合完成静态代理
* 什么是聚合? 聚合就是在一个类中调用另一个类
* @author wangbin
*
*/
public class CarLogProxy implements Moveable {
private Moveable moveable;
public CarLogProxy(Moveable moveable) {
this.moveable = moveable;
}
@Override
public void move() {
// TODO Auto-generated method stub
System.out.println("汽车日志开始......");
moveable.move();
System.out.println("汽车日志结束.......");
}
}
创建一个测视类
package com.hxkj.proxy;
/**
* 测试类
* @author wangbin
*
*/
public class Test {
public static void main(String[] args) {
// Car car = new Car();
// car.move();
//通过继承的方式完成静态代理
// Car2 car2 = new Car2();
// car2.move();
//通过聚合的方式完成静态代理
// Car car = new Car();
// CarTimeProxy car3 = new CarTimeProxy(car);
// car3.move();
//通过聚合方式实现不同代理类功能的叠加,还可以交换顺序
Car car = new Car();
CarTimeProxy ctp = new CarTimeProxy(car);
CarLogProxy clp = new CarLogProxy(ctp);
clp.move();
}
}
实现静态代理有两种方式:一种是继承,一种是聚合
不推荐使用继承的的方式,因为每次要添加新的业务功能就需要写写的代理类,这样很容易造成类膨胀
使用聚合的方式可以将代理类进行复用进行业务功能的增强,而且还可以调整业务顺序
2.动态代理
JDK动态代理:代理对象和被代理对象不确定的,但是都实现了相同的接口或者继承了相同的抽象类,动态产生代理,实现对【不同类】,【不同方法】的代理
java动态代理类,位于java.lang.reflect包下,一般涉及两个类:(1)Interface InvocationHandler:该接口中仅定义了一个方法public object invoke(obj,method,args):实际使用中,obj指代理的对象,method代理对象的方法,
args为该方法参数数组。这个抽象方法在代理类中动态实现
(2)Proxy:该类即为动态代理类:static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h):返回代理类的一个实例,返回后的代理类可以被当作代理类使用(可使用被代理类的在【接口中】声明过的方法)。
· 第一个参数loader为被代理类的加载器,通过被代理类.getClass().getClassLoader()得到
· 第二个参数interfaces为被代理类实现的所有接口,同样通过getClass().getInterface()得到
· 第三个参数handler就是自己实现的InvocationHandler的实现类的对象,(时间、日志等拦截器)
3、***实现:
· 声明一个代理h实现InvocationHandler接口,通过【构造方法接受被代理类】,并实现invoke方法,添加业务逻辑(实现原有功能并添加额外功能)
· 在测试类中,通过共同实现接口的实例获得代理对象,并实现方法,如Interface1 i = (Interface1)Proxy.newProxyInstance(classLoader,classInterfaces,h);
· 通过动态代理对象m,代用其方法i.fun();
注意:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
System.out.println("在被代理对象方法被调用前需要执行的代码")
method.invoke(obj);
System.out.println("在被代理对象方法被调用后需要执行的代码")
代码示例:
同样是这个接口
package com.hxkj.proxy;
/**
* 行驶的接口
* @author Administrator
*
*/
public interface Moveable {
//move方法
void move();
}
Car类实现接口
package com.hxkj.proxy;
import java.util.Random;
/**
* 汽车类实现行驶的接口
* @author wanbin
*
*/
public class Car implements Moveable {
@Override
public void move() {
//汽车行驶0-1000以内毫秒数的时间后开始行驶
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("汽车行驶中......");
}
}
创建时间拦截器package com.hxkj.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* JDK的动态代理
* 这是一个时间处理器,实现InvocationHandler接口
* @author wangbin
*
*/
public class TimeHandler implements InvocationHandler {
private Object obj;//被代理对象
//传入被代理对象构造这个业务处理器
public TimeHandler(Object obj) {
this.obj = obj;
}
/**
* 这里用的是反射原理
* 参数:
* proxy:代理对象
* method:被代理对象的方法
* args:方法的参数
* 返回值:
* Object 方法的返回值
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
/**
* 在代理类中增加新的业务功能
*/
System.out.println("在被代理对象方法被调用前新增的业务逻辑");
/**
* 通过反射调用被代理对象的方法
*/
method.invoke(obj);
System.out.println("在被代理对象方法被调后前新增的业务逻辑");
return null;
}
}
创建日志拦截器
package com.hxkj.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogHandler implements InvocationHandler {
private Object obj;//被代理对象
//传入被代理对象构造业务处理器对象
public LogHandler(Object obj) {
this.obj = obj;
}
/**
* 这里用的是反射原理
* 参数:
* proxy:代理对象
* method:代理对象的方法
* args:方法的参数
* 返回值:
* Object 方法的返回值
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("调用代理对象方法前记录日志");
method.invoke(obj);
System.out.println("调用代理对象方法后记录日志");
return null;
}
}
书写测试类:
package com.hxkj.jdkproxy;
import java.lang.reflect.Proxy;
import com.hxkj.proxy.Car;
import com.hxkj.proxy.Moveable;
/**
* 测试动态代理
* @author wangbin
*
*/
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
/**
* 创建被代理对象
*/
Car car = new Car();
/**
* 向时间处理器传入被代理对象生成被代理对象的时间处理器对象
*/
TimeHandler timeHandler = new TimeHandler(car);
/**
* 参数:
* loader:被代理对象的类加载器
* interfaces:被代理对象的接口
* h InvocationHandler:代理对象关联的被代理对象的各种(时间,日志。。。。等)处理器
*/
Moveable m=(Moveable) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), timeHandler);
LogHandler logHandler = new LogHandler(m);
Moveable m2=(Moveable) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), logHandler);
m2.move();
}
}
Proxy类的设计用到代理模式的设计思想,Proxy类对象实现了代理目标的所有接口,并代替目标对象进行实际的操作。但这种替代不是一种简单的替代,这样没有任何意义,代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截。所以,Proxy应该包括一个方法拦截器,来指示当拦截到方法调用时作何种处理。InvocationHandler就是拦截器的接口
总结:随着业务逻辑的不断增多,功能的增加,使用继承方式增加功能会导致类膨胀。
代理和继承都是为了拓展业务功能,使用代理实现的话,更加灵活,易于拓展