一、使用场景
- 当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口。
- 被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容。
-
Interface: 抽象接口
,声明真是主体与代理主题的共同接口方法。 -
RealSubject: 真实主题类
,定义了代理所表示的真是对象,执行具体的业务方法。客户端通过代理类来间接的调动这个真实主题中的方法。 -
ProxySubject: 代理类
,持有一个真实类的引用,在接口方法中调用真实主题相应的方法,达到代理的作用。
注释:通过代理使client和真实对象隔离,使用户不用操作真实对象只用操作代理类就可达到指定效果。
二、静态代理
public interface ILawsuit {
void submit();//提交申请
void burden();//进行举证
void defend();//开始辩护
void finish();//诉讼完成
}
public class Civilian implements ILawsuit {
@Override
public void submit() {
System.out.println("起诉");
}
@Override
public void burden() {
System.out.println("举证");
}
@Override
public void defend() {
System.out.println("辩护");
}
@Override
public void finish() {
System.out.println("胜诉");
}
}
public class Lawyer implements ILawsuit {
private ILawsuit civilian;
public Lawyer(ILawsuit civilian) {
this.civilian = civilian;
}
@Override
public void submit() {
civilian.submit();
}
@Override
public void burden() {
civilian.burden();
}
@Override
public void defend() {
civilian.defend();
}
@Override
public void finish() {
civilian.finish();
}
}
public class Client {
public static void main(String[] args) {
ILawsuit civilian = new Civilian();
ILawsuit lawyer = new Lawyer(civilian);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
缺点:扩招能力差和可维护性差。违反开闭原则。
三、动态代理
动态代理通过反射动态的生成代理者对象(代理类 ProxySubject),也就是说在写代码的时候根本不知道要代理谁,具体代理谁会在执行阶段决定。
Java提供了一个便捷的动态代理接口InvocationHandler,动态代理类只要实现这个接口就行:
public class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
看一下动态代理的用法:
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当然这里可以对方法名进行判断过滤 if(method.getName().equals("***"))
Object result = method.invoke(object,args);
return result;
}
}
客户端调用:
java
public class Main {
public static void main(String[] args) {
ILawsuit lawsuit = new Civilian();
DynamicProxy proxy = new DynamicProxy(lawsuit);
ClassLoader loader = lawsuit.getClass().getClassLoader();
//动态创建代理类,需要传入一个类加载器ClassLoader;一个你希望这个代理实现的接口列表,这里要代理ILawsuit接口;
//第一个参数是ClassLoader; 第二个参数是生成代理类指定接口的Class数组;第三个参数是InvocationHandler的实现
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[]{ILawsuit.class}, proxy);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
kotlin
object MainTest{
@JvmStatic
fun main(argc : Array<String>){
val lawsuit = Civilian()
val messageInterface = Proxy.newProxyInstance(MainTest::class.java.classLoader,
arrayOf<Class<*>>(ILawsuit ::class.java)
) { _, method, args -> method!!.invoke(lawsuit , *args) } as ILawsuit
messageInterface.setName("小米", 13)
}
}
- out 和 in 代表 extends 和 super;out T 等价于 ?extends T
;in T等价于 ? super T - Class< * >:*号等于Java的Class< ? >
- *args:等于Java中的可变参数(Object... args)。如下代码:
public class CallJavaUtils {
public static int addNumbers(String name, int... args) {
int result = 0;
for (int i = 0; i < args.length; i++) {
result += args[i];
}
return result;
}
}
//测试Kotlin传递可变长参数给Java可变参数方法
var numbers:IntArray = intArrayOf(1, 2, 3, 4, 5)
CallJavaUtils.addNumbers("add", *numbers)
代理类进一步封装
public class DynamicProxy implements InvocationHandler {
/*持有的真实对象*/
private Object factory;
public DynamicProxy(Object factory) {
this.factory = factory;
}
public Object getFactory() {
return factory;
}
public void setFactory(Object factory) {
this.factory = factory;
}
/**
* 获取代理对象
*
* @return
*/
private Object getProxyInstance() {
return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSthBefore();
Object invoke = method.invoke(factory, args);
doSthAfter();
return invoke;
}
/**
* 后置处理器
*/
private void doSthAfter() {
System.out.println("method after do something.");
}
/**
* 前置处理器
*/
private void doSthBefore() {
System.out.println("method before do something.");
}
}
缺点:运用反射技术使执行效率较差。
四、其他分类
静态代理和动态代理是从code方便进行分类的。这两个分类根据适用范围来分都可以分为下面几种:
- 远程代理:为摸个对象在不同的内存地址空间提供局部代理,是系统Server部分隐藏,以便Client不用考虑Server的存在。
- 虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
- 保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。
- 智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数
网友评论