持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
简介
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
应用场景
代理模式主要用于解决直接访问对象时带来的问题。通过这个意图我们就好判断其应用场景。
按职责来划分,通常有以下使用场景:
- 远程代理。
- 虚拟代理。
- Copy-on-Write 代理。
- 保护(Protect or Access)代理。
- Cache代理。
- 防火墙(Firewall)代理。
- 同步化(Synchronization)代理。
- 智能引用(Smart Reference)代理。
分类
一. 静态代理
接口:
/**
* @author miracle
*/
public interface IWorker {
/**
* 上班
*/
void working();
}
实体类:
/**
* 真正的打工人
*
* @author miracle
*/
public class RealWorker implements IWorker {
@Override
public void working() {
System.out.println("摸鱼");
}
}
代理类-方式1:
/**
* 代理
*
* @author miracle
*/
public class WorkerProxy implements IWorker {
private IWorker IWorker;
public WorkerProxy(IWorker IWorker) {
this.IWorker = IWorker;
}
@Override
public void working() {
// 代理类可以进行增强和控制
before();
IWorker.working();
}
public void before(){
System.out.println("打工是不可能的,这辈子都不可能打工");
}
}
使用测试:
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealWorker());
proxy.working();
}
代理类-方式2:
public class WorkerProxy implements IWorker {
private RealWorker realWorker;
public WorkerProxy() {
}
@Override
public void working() {
if(null == realWorker){
realWorker = new RealWorker();
}
// 代理类可以进行增强和控制
before();
realWorker.working();
}
public void before(){
System.out.println("打工是不可能的,这辈子都不可能打工");
}
}
测试用例:
public static void main(String[] args) {
WorkerProxy proxy = new WorkerProxy();
proxy.working();
}
它们之间的关系:
静态代理的特点就是一个服务类对应一个代理类,如果服务类比较多的时候,我们就必须创建很多代理类,维护成本和开发成本太大了,因此才有了动态代理。
二. 动态代理
动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务
1. JDK代理(接口代理)
JDK代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类
代理类:
public class JDKProxy implements InvocationHandler {
/**
* 需要代理的服务对象
*/
private Object object;
public JDKProxy(Object object){
this.object = object;
}
/**
* 获取服务对象的代理
* @return
*/
public Object getProxy(){
Class<?> clazz = object.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
// 增加控制
return result;
}
}
这里接着使用上面静态代理的接口和实现类 测试用例:
public static void main(String[] args) {
JDKProxy proxy = new JDKProxy(new RealWorker());
RealWorker realWorker = (RealWorker) proxy.getProxy();
realWorker.working();
}
其核心就是利用反射。
其代理实现的是JDK自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。
JDK动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理.
2. Cglib代理
实体类(无实现接口):
public class Family {
public void reunite(){
System.out.println("家人团聚");
}
}
代理类:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author miracle
*/
public class CglibProxy implements MethodInterceptor {
/**
* 需要代理的服务对象
*/
private Object obj;
public CglibProxy(Object obj) {
this.obj = obj;
}
/**
* 获取服务对象的代理
*
* @return
*/
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
// 设置拦截器,回调对象设置为自己
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 调用前控制/扩展行为
before();
// 调用服务对象的某个方法
Object result = methodProxy.invoke(obj, objects);
// 调用后控制/扩展行为
return result;
}
public void before(){
System.out.println("明天是中秋了");
}
}
测试用例:
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy(new Family());
Family family = (Family) proxy.getProxy();
family.reunite();
}
可以看出Cglib动态代理和JDK动态代理两者相似,Cglib动态代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要。
总结
- 静态代理 前提要有接口及实现类,且一个实现类对应一个代理,太繁琐因此产生了动态代理。
- JDK代理
前提也是要有接口及实现类型,利用反射来代理,实现
InvocationHandler并重写invoke方法调用服务对象(被代理类)的方法并增加一些控制。 - Cglib代理
无需接口,通过实现
MethodInterceptor,重写intercept方法来拦截及增加服务对象(被代理类)的方法。
代理模式优缺点
优点:
- 职责清晰。
- 高扩展性。
缺点:
- 因使用了代理,存在中介,造成处理速度变慢。
- 实现较为复杂。