代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式; 即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法.
角色
- 主题接口: 定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法
- 真实主题: 真正实现业务逻辑的类
- 代理类: 用来代理和封装真实主题
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
在编译期确定代理对象,在程序运行前代理类的.class文件就已经存在了。
package staticproxy;
/**
* 主题接口
*/
public abstract class Subject {
public abstract void request();
}
package staticproxy;
/**
* 真实主题
*/
public class RealSubject extends Subject {
@Override
public void request() {
// TODO Auto-generated method stub
}
}
package staticproxy;
/**
* 静态代理,对具体真实对象直接引用
* 代理类,代理类需要有对真实主题的引用,
* 代理做真实主题要做的事情
*/
public class ProxySubject extends Subject {
private RealSubject realSubject = null;
/**
* 除了代理真实主题做该做的事情,代理类也可以提供附加操作,
* 如:preRequest()和postRequest()
*/
@Override
public void request() {
preRequest(); //真实主题操作前的附加操作
if(realSubject == null){
realSubject = new RealSubject();
}
realSubject.request();
postRequest(); //真实主题操作后的附加操作
}
/**
* 真实主题操作前的附加操作
*/
private void postRequest() {
// TODO Auto-generated method stub
}
/**
* 真实主题操作后的附加操作
*/
private void preRequest() {
// TODO Auto-generated method stub
}
}
package staticproxy;
/**
* 客户端调用
*/
public class Main {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request(); //代理类代替真实主题实现对应业务逻辑
}
}
优点
可以做到在不修改目标对象的功能前提下,对目标功能扩展
缺点
代理类和真实主题类实现相同的接口,同时要实现相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理
在运行时确定代理对象
特点
- 在运行期,通过反射机制创建一个实现了一组给定接口的新类
- 在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了这些 interface。该class的实 例可以当作这些interface中的任何一个来用。但是这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工 作
- 接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)
- ClassLoader loader: 指定当前目标对象使用类加载器,用null表示默认类加载器
- Class [] interfaces: 需要实现的接口数组
- InvocationHandler handler: 调用处理器,执行目标对象的方法时,会触发调用处理器的方法,从而把当前执行目标对象的方法作为参数传入
java.lang.reflect.InvocationHandler
: 这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
/**
*
* 该方法负责集中处理动态代理类上的所有方法调用。
*
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 调用参数
*/
Object invoke(Object proxy, Method method, Object[] args)
例子
package dynamicproxy;
/**
* 主题接口
*/
public interface Subject {
void request();
}
package dynamicproxy;
/**
* 真实的主题,实现主题借口
*/
public class RealSubject implements Subject {
@Override
public void request() {
// TODO Auto-generated method stub
}
}
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理, 它是在运行时生成的class,在生成它时你必须提供一组interface给它, 然后该class就宣称它实现了这些interface。
* 你当然可以把该class的实例当作这些interface中的任何一个来用。 当然啦,这个Dynamic
* Proxy其实就是一个Proxy,它不会替你作实质性的工作, 在生成它的实例时你必须提供一个handler,由它接管实际的工作。
*/
public class DynamicSubject implements InvocationHandler {
private Object sub; // 真实对象的引用
public DynamicSubject(Object sub) {
this.sub = sub;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling " + method);
method.invoke(sub,args);
System.out.println("after calling " + method);
return null;
}
}
package dynamicproxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public static void main(String[] args) {
public static void main(String[] args) throws Throwable {
RealSubject rs = new RealSubject();
InvocationHandler handler = new DynamicSubject(rs);
Class cls = rs.getClass();
//以下是分解步骤
/*
Class c = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces());
Constructor ct = c.getConstructor(new Class[]{InvocationHandler.class});
Subject subject =(Subject) ct.newInstance(new Object[]{handler});
*/
//以下是一次性生成
Subject subject = (Subject)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), handler);
subject.request();
}
优点
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
网友评论