代理模式
-- 什么是代理模式?
-- 代理模式应用场景?
-- 静态代理
-- JDK动态代理
-- CGLIB动态代理
-- 强制代理
定义
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。
应用场景
访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
三种代理模式
新建一个业务类和业务接口
Subject.java
public interface Subject {
void request();
}
RealSubject.java
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("访问真实主题方法...");
}
}
静态代理
ProxySubject.java
public class ProxySubject implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if(realSubject == null){
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
@Override
public Subject getProxy() {
return this;
}
public void preRequest(){
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest(){
System.out.println("访问真实主题之后的后续处理。");
}
}
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject();
proxy.request();
}
}
总结: 通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,这个“包含”其实也说明是可以扩展的,向上面代码所示,在调用request的前后都加上了处理,在一定程度上降低了系统的耦合度。但如果接口层发生了变化,代理对象的代码也要进行维护。接下来来了解下动态代理。动态代理是在实现阶段不用关心代理谁,而在运行阶段 才指定代理哪一个对象。
JDK动态代理
DynamicProxy.java
public class DynamicProxy implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
//我要代理谁
public DynamicProxy(Object _obj){
this.obj = _obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("访问真实主题之前的预处理。");
Object result = method.invoke(this.obj, args);
System.out.println("访问真实主题之后的后续处理。");
return result;
}
}
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxy(realSubject);
ClassLoader cl = realSubject.getClass().getClassLoader();
Subject proxy1 = (Subject) Proxy.newProxyInstance(cl, new Class[]{Subject.class}, handler);
proxy1.request();
}
}
总结: JDK动态代理就是用JDK原有的API传入目标对象的类加载器和接口,再重写代理方法,返回一个代理对象。不需要重写接口了,也不用维护代理类了,但还是需要传入接口。
CGLIB动态代理
public class ProxyFactory implements MethodInterceptor {
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance() {
//1.工具类(它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,
// 和JDK动态代理不一样的是不管是接口还是类它都能正常工作。)
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;
}
}
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
RealSubject target1 = new RealSubject();
RealSubject realSubject1 = (RealSubject) new ProxyFactory(target1).getProxyInstance();
realSubject1.request();
}
}
总结: CGlib底层是动态地在内存中生成了目标对象的子类的字节码,并生成相应的对象,JDK动态代理实现则是使用的反射机制。CGlib实现动态代理能满足没有接口的目标对象的代理对象实现。
应用
在Spring中,实现AOP的AspectJ框架底层,会对无接口的目标对象采用CGlib,有接口的目标对象则采用JDK动态代理。
下面再拓展一种代理模式:
强制代理
强制代理即是必须通过真实角色找到代理角色。就好像你直接拨打了某个公司老总的电话,然后老总说,你先去我的秘书那里预约一下先吧。
RealSubject2.java
public class RealSubject2 implements Subject {
private Subject proxy = null;
@Override
public Subject getProxy(){
this.proxy = new ProxySubject();
return proxy;
}
@Override
public void request() {
if(this.isProxy()){
System.out.println("访问真实主题方法...");
}else{
System.out.println("请使用指定的代理访问");
}
}
private boolean isProxy() {
if(this.proxy == null) {
return false;
}else{
return true;
}
}
}
ProxyTest.java
public class ProxyTest {
public static void main(String[] args) {
RealSubject2 realSubject2 = new RealSubject2();
realSubject2.request();
System.out.println("-----分割线------");
ProxySubject proxySubject = (ProxySubject) realSubject2.getProxy();
proxySubject.request();
}
}
输出结果:
请使用指定的代理访问
-----分割线-----
访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。
总结:强制代理模式使得高层模块与真实角色的业务场景耦合度极地;真实角色的修改,在高层模块中完全不用修改。俩者只通过真实角色提供的一个getProxy()接口来交互。在整个实现过程,采用回调思想,在真实角色中调用代理对象的方法,代理对象通过回调来调用真实角色的方法。