结构型-代理模式 Link to heading
代理模式和装饰模式很像,初学者很容易搞混,所以这里我们得紧接着来讲解一下。首先请记住,当无法直接访问某个对象或访问某个对象存在困难时,我们就可以通过一个代理对象来间接访问。
比如现在有一个目标类,但是我们现在需要通过代理来使用它:
public abstract class Subject {
public abstract void test();
}
public class SubjectImpl extends Subject{ //此类无法直接使用,需要我们进行代理
@Override
public void test() {
System.out.println("我是测试方法!");
}
}
现在我们为其建立一个代理类:
public class Proxy extends Subject{ //为了保证和Subject操作方式一样,保证透明性,也得继承
Subject target; //被代理的对象(甚至可以多重代理)
public Proxy(Subject subject){
this.target = subject;
}
@Override
public void test() { //由代理去执行被代理对象的方法,并且我们还可以在前后添油加醋
System.out.println("代理前绕方法");
target.test();
System.out.println("代理后绕方法");
}
}
对装饰器模式来说,装饰者和被装饰者都实现同一个接口/抽象类。对代理模式来说,代理类和被代理的类都实现同一个接口/抽象类,在结构上确实没有啥区别。
-
装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。
-
代理模式强调要让别人帮你去做事情,以及添加一些本身与你业务没有太多关系的事情。
装饰模式和代理模式的不同之处在于思想。
当然实现代理模式除了我们上面所说的这种方式之外,我们还可以使用JDK为我们提供的动态代理机制,我们不再需要手动编写继承关系创建代理类,它能够在运行时通过反射机制为我们自动生成代理类:
public interface Subject { //JDK提供的动态代理只支持接口
void test();
}
public class SubjectImpl implements Subject{
@Override
public void test() {
System.out.println("我是测试方法!");
}
}
接着我们需要创建一个动态代理的处理逻辑:
public class TestProxy implements InvocationHandler { //代理类,需要实现InvocationHandler接口
private final Object object; //这里需要保存一下被代理的对象,下面需要用到
public TestProxy(Object object) {
this.object = object;
}
@Override //此方法就是调用代理对象的对应方法时会进入,这里我们就需要编写如何进行代理了
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method就是调用的代理对象的哪一个方法,args是实参数组
System.out.println("代理的对象:"+proxy.getClass()); //proxy就是生成的代理对象了,我们看看是什么类型的
Object res = method.invoke(object, args); //在代理中调用被代理对象原本的方法,因为你是代理,还是得执行一下别人的业务,当然也可以不执行,但是这样就失去代理的意义了,注意要用上面的object
System.out.println("方法调用完成,返回值为:"+res); //看看返回值是什么
return res; //返回返回值
}
}
测试
public static void main(String[] args) {
SubjectImpl subject = new SubjectImpl(); //被代理的大冤种
InvocationHandler handler = new TestProxy(subject);
Subject proxy = (Subject) Proxy.newProxyInstance(
subject.getClass().getClassLoader(), //需要传入被代理的类的类加载器
subject.getClass().getInterfaces(), //需要传入被代理的类的接口列表
handler); //最后传入我们实现的代理处理逻辑实现类
proxy.test(); //比如现在我们调用代理类的test方法,那么就会进入到我们上面TestProxy中invoke方法,走我们的代理逻辑
}
// 打印值
代理的对象: class com.sun.proxy.$Proxy0
我是测试方法!
方法调用完成,返回值为: null