一、概念
通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象
例如:潘金莲(目标对象)、王婆(代理对象)、西门庆(调用对象)
二、分类
分为静态代理、动态代理(JDK内置代理和cglib代理)
三、静态代理
静态代理前提是目标对象和代理对象实现或者继承同一个接口或者父类。
代码示例
//定义一类女性
public interface KindWomen {
//这种类型的女人能做什么事情呢?
public void makeEyesWithMan(); //抛媚眼
}
//定义潘金莲
public class PanJinLian implements KindWomen {
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼");
}
}
//定义王婆代理类
public class WangPo implements KindWomen {
private KindWomen kindWomen;
public WangPo(){ //默认的话,是潘金莲的代理
this.kindWomen = new PanJinLian();
}
//她可以是KindWomen的任何一个女人的代理,只要你是这一类型
public WangPo(KindWomen kindWomen){
this.kindWomen = kindWomen;
}
public void makeEyesWithMan() {
this.kindWomen.makeEyesWithMan(); //王婆这么大年龄了,谁看她抛媚眼?!
}
}
然后就是实际的调用者西门庆
public class XiMenQing {
public static void main(String[] args) {
//把王婆叫出来
WangPo wangPo = new WangPo();
//然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那出戏:
wangPo.makeEyesWithMan(); //看到没,虽然表面上时王婆在做,实际上爽的是潘金莲
}
其他这样一类的 女人也可以找王婆做代理
如贾氏
public class JiaShi implements KindWomen {
public void makeEyesWithMan() {
System.out.println("贾氏抛媚眼");
}
那么西门庆同样可以通过王婆勾搭贾氏
public class XiMenQing {
public static void main(String[] args) {
//改编一下历史,贾氏被西门庆勾走:
JiaShi jiaShi = new JiaShi();
WangPo wangPo = new WangPo(jiaShi); //让王婆作为贾氏的代理人
wangPo.makeEyesWithMan();
}
}
总结:代理模式主要使用了java的多态,干活的是被代理类,代理类主要是接活。那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚的很,同一个接口呗。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
四、动态代理
1、JDK代理
特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代码示例
/**
* 创建动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/
public class ProxyFactory {
private Object target;//目标对象
public ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object returnValue = method.invoke(target,objects);
return returnValue;
}
});
}
}
调用者
public class XiMenQing {
public static void main(String[] args) {
//目标对象
KindWomen KindWomen = new PanJinLian();
//给目标对象创建代理对象
PanJinLian proxy = (PanJinLian) new ProxyFactory(KindWomen).getProxyInstance();
proxy.makeEyesWithMan();
}
}
总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
2、Cglib代理
也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
代码示例
/**
* Cglib子类代理工厂
* 对KindWomen在内存中动态构建一个子类对象
*/
public class CglibFactory implements MethodInterceptor {
//目标对象
private Object target;
public CglibFactory(Object target){
this.target = target;
}
//给目标对象创建代理对象
public Object getProxyInstance(){
//工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类(代理对象)
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行目标对象的方法
Object returnValue = method.invoke(target,objects);
return returnValue;
}
}
调用测试
public class XiMenQing {
public static void main(String[] args) {
//目标对象
PanJinLian PanJinLian = new PanJinLian();
//代理对象
PanJinLian proxy = (PanJinLian) new CglibFactory(PanJinLian).getProxyInstance();
//执行代理对象的方法
proxy.makeEyesWithMan();
}
}