在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
静态代理
实现同一个接口
public interface Emp {
public void work();
}
代理对象
public class Coder implements Emp {
public void work() {
System.out.println("Coder 码代码。。。");
}
}
代理类
public class Manager implements Emp {
private Coder coder;
public Manager(Coder coder) {
this.coder = coder;
}
public void work() {
System.out.println("Manager布置任务。。。");
coder.work();
System.out.println("Manager。。。");
}
}
测试:
public class Main {
public static void main(String[] args) {
//传入代理对象
Emp manager=new Manager(new Coder());
manager.work();
}
}
结果
Manager布置任务。。。
Coder 码代码。。。
Manager。。。
jdk动态代理
准备一个产生代理的工厂类
public class ProxyFactory {
// 被代理的目标对象
private Object target;
// 通过构造方法对目标对象进行初始化
public ProxyFactory(Object target){
this.target = target;
}
// 申明一个获取对象的方法
public Object getProxyInstance(){
//通过JDK提供的Proxy类的静态方法获取一个代理对象
//参数1:classLoader 目标对象的类加载器
//参数2:interfaces 目标对象所实现的所有的接口的类型数组
//参数3:InvocationHandler 调用处理器
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler()
);
}
// 自定义一个InvocationHandler类
class MyInvocationHandler implements InvocationHandler{
/**
*
* @param proxy 代理对象
* @param method 被增强的方法对象
* @param args 被增强的方法的参数对象
* @return 被增强的方法执行的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
//增强处理
System.out.println("动态代理的前置增强处理...给程序员安排任务");
// 调用真实的方法
returnValue = method.invoke(target,args);
System.out.println("动态代理的前后置强处理...检查程序的任务进度");
return returnValue;
}
}
}
测试:
public class Test {
public static void main(String[] args) {
Emp emp = new Coder();
Emp obj = (Emp) new ProxyFactory(emp).getProxyInstance();
obj.work();
}
}
JDK实现的动态代理是基于接口的。JDK动态的生成了一个代理类,这个代理类和目标类实现了同样的接口。就好像我们前面的静态代理一样,Manager和Coder实现了同样的接口,最后我们是按照接口中的API调用代理对象的。
那么这里就会有一个问题,如果我们在Coder中扩展了方法, 则Coder扩展的方法是无法被代理的。
JDK实现的动态代理只能代理接口中定义的方法。
CGLib实现的动态代理
/**
* @author 戴着假发的程序
*/
public class CGLibProxyFactory {
// 不需要具体的对象,只要传入对应的class即可。
// 返回代理对象的
public Object getObject(Class clazz){
// 准备一个增强处理器
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(clazz);
// 设置回调处理器
enhancer.setCallback(new MethodInterceptor() {
//参数1:反射生成的目标对象
// 参数2:目标方法
// 参数3:方法的参数列表
// 参数4:代理方法对象
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
//增强处理
System.out.println("动态代理的前置增强处理...给程序员安排任务");
// 调用真实的方法
returnValue = methodProxy.invokeSuper(target,args);
System.out.println("动态代理的前后置强处理...检查程序的任务进度");
return returnValue;
}
});
//生成代理对象,并且返回
return enhancer.create();
}
}
测试:
public class Test {
public static void main(String[] args) {
// Emp emp = (Emp) new CGLibProxyFactory().getObject(Coder.class);
// emp.work();
Coder coder = (Coder) new CGLibProxyFactory().getObject(Coder.class);
coder.work();
coder.tocao();
}
}
CGLib的动态代理的实现是基于继承的。
CGLib会动态的给代理目标类实现一个子类,并且重写代理目标类的所有方法,进行动态增强。
所以,目标类是否有接口根本就无所谓。
面试题:为什么CGLib实现的动态代理的目标对象可以有接口,也可以没接口?
因为CGLib是基于继承实现的。