一、代理模式
定义:代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。这种类型的设计模式属于结构型模式。
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
二、静态代理
所谓静态代理是指,在程序运行前,由程序员创建或特定工具自动生成源代码并对其编译生成.class文件。静态代理的实现只需要三步:首先,定义业务接口;其次,实现业务接口;然后,定义代理类并实现业务接口;最后便可通过客户端进行调用。例如,
1.创建接口
1 package DesignPattern.staticproxy; 2 3 public interface ITeacherDao { 4 void teach(); 5 }
2.目标类实现接口
package DesignPattern.staticproxy; public class TeacherDao implements ITeacherDao{ @Override public void teach() { System.out.println("老师正在授课……"); } }
3.代理类实现接口,对目标类添加扩展功能
1 package DesignPattern.staticproxy; 2 3 public class TeacherDaoProxy implements ITeacherDao { 4 private ITeacherDao target; 5 6 public TeacherDaoProxy(ITeacherDao target) { 7 this.target = target; 8 } 9 @Override 10 public void teach() { 11 System.out.println("开始代理,完成某些操作……"); 12 target.teach(); 13 System.out.println("提交……"); 14 } 15 }
4.客户端调用
1 package DesignPattern.staticproxy; 2 3 public class client { 4 public static void main(String[] args) { 5 // 创建目标对象(被代理对象) 6 ITeacherDao teacherDao = new TeacherDao(); 7 // TeacherDao teacherDao = new TeacherDao(); 8 // 创建代理对象,同时将被代理对象传递给代理对象 9 TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao); 10 // 通过代理对象调用被代理对象的方法 11 teacherDaoProxy.teach(); 12 } 13 }
静态代理优缺点
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
开闭原则:开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简单来说:就是为了使程序的扩展性好,易于维护和升级。
缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。
三、jdk动态代理
如何解决静态代理的缺点呢? 这里就得说说动态代理了。
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。(利用反射机制在运行时创建代理类。)
1.创建接口
1 package DesignPattern.Dynamicproxy; 2 3 public interface ITeacherDao { 4 void teach(); 5 void sayHello(String name); 6 }
2.目标类实现接口
1 package DesignPattern.Dynamicproxy; 2 3 public class TeacherDao implements ITeacherDao{ 4 @Override 5 public void teach() { 6 System.out.println("老师授课中……"); 7 } 8 9 10 @Override 11 public void sayHello(String name) { 12 System.out.println("hello!"+name); 13 } 14 }
3.动态代理
1 package DesignPattern.Dynamicproxy; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 public class ProxyFactory { 6 //维护一个目标对象,Object 7 private Object target; 8 //构造器,对target进行初始化 9 public ProxyFactory(Object target) { 10 this.target = target; 11 } 12 //给目标对象生成一个代理对象 13 public Object getProxyInstance(){ 14 //说明 15 /* 16 * public static Object newProxyInstance(ClassLoader loader, 17 Class<?>[] interfaces, 18 InvocationHandler h) 19 20 //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定 21 //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型 22 //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入 23 */ 24 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 25 target.getClass().getInterfaces(), 26 new InvocationHandler() { 27 @Override 28 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 29 System.out.println("JDK代理开始……"); 30 //反射机制调用目标对象的方法 31 Object returnVal = method.invoke(target,args); 32 System.out.println("JDK代理提交……"); 33 return returnVal; 34 } 35 }); 36 } 37 }
4..客户端调用
1 package DesignPattern.Dynamicproxy; 2 3 public class client { 4 public static void main(String[] args) { 5 //创建目标对象 6 ITeacherDao target = new TeacherDao(); 7 //给目标对象,创建代理对象,可以转成ITeacherDao 8 ITeacherDao proxyInstance =(ITeacherDao)new ProxyFactory(target).getProxyInstance(); 9 // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象 10 System.out.println("proxyInstance:"+proxyInstance.getClass()); 11 //通过代理对象调用目标对象的方法 12 proxyInstance.teach(); 13 proxyInstance.sayHello("Tom"); 14 } 15 }
优缺点:
优点:
相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。
缺点:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用JDK动态代理。