代理模式
给某一个对象提供一个代理对象,并由代理对象控制原对象的引用。
有些时候,一个客户不想或者不能直接引用一个对象,可以通过代理对象在客户端和目标对象之前起到中介作用。代理模式的角色:
1.抽象对象角色
声明了目标对象和代理对象共同的接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
2.目标对象角色
定义了代理对象所代表的目标对象
3.代理对象角色
代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象,代理对象提供一个目标对象相同的接口,以便可以在任何时候替代目标对象。
静态代理实例
假如一个接口里面有一个方法,想在调用这个接口的前后加东西,就可以使用代理模式。静态代理是代理模式最简单的实现,定义一个静态代理接口
1 public interface StaticHelloWorld { 2 void print(); 3 }
实现类
1 public class StaticHelloWorldImpl implements StaticHelloWorld{ 2 3 @Override 4 public void print() { 5 System.out.println("hello world"); 6 } 7 8 }
代理对象,重点在于代理对象和实际对象实现的是同一个接口,希望在任何时候让代理对象替代实际对象
1 public class StaticProxy implements StaticHelloWorld{ 2 3 private StaticHelloWorldImpl staticHelloWorldImpl; 4 5 public StaticProxy(){ 6 if(staticHelloWorldImpl == null){ 7 staticHelloWorldImpl = new StaticHelloWorldImpl(); 8 } 9 } 10 11 public void print() { 12 System.out.println("before HelloWorld"); 13 staticHelloWorldImpl.print(); 14 System.out.println("after HelloWorld"); 15 } 16 17 }
main
1 public static void main(String[] args) { 2 StaticProxy proxy = new StaticProxy(); 3 proxy.print(); 4 }
结果是
1 before HelloWorld 2 hello world 3 after HelloWorld
静态代理的缺点
静态代理的特点是静态代理的代理类是程序员创建的,程序运行前静态代理的.class文件已经存在了。这种模式在代理量比较小时还可以,但是代理量大了就有很大的缺点:
1.代理内容无法复用,如果想换一种代理内容,比如在helloworld前后不想输入before和after..,想输出别的东西,那么就必须重新写一个代理对象。这样容易造成代理对象的膨胀。
2.接口里面新增了一个方法,实际对象实现了这个方法,代理对象也必须新增内容,去给这个新增方法增加代理内容。
动态代理实例
动态代理接口
1 public interface DynamicHelloWorld { 2 String print(); 3 }
动态代理接口的实现
1 public class DynamicHelloWorldImpl implements DynamicHelloWorld{ 2 3 public String print() { 4 System.out.println("hello world"); 5 return "DynamicHelloWorldImpl"; 6 } 7 8 }
动态代理类,在java中动态代理需要实现InvocationHandler接口。该接口只有一个invoke方法。
第一种
1 public class DynamicProxy implements InvocationHandler 2 { 3 private Object target; 4 5 public Object newInstance(Object target) 6 { 7 this.target = target; 8 9 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 10 target.getClass().getInterfaces(), this); 11 } 12 13 public Object invoke(Object proxy, Method method, Object[] args) 14 throws Throwable 15 { 16 System.out.println("Before DynamicProxy"); 17 method.invoke(target, args); 18 System.out.println("After DynamicProxy"); 19 return null; 20 } 21 }
第二种
1 public class DynamicProxy implements InvocationHandler 2 { 3 private Object target; 4 5 public DynamicProxy(Object target) 6 { 7 this.target = target; 8 } 9 10 public Object invoke(Object proxy, Method method, Object[] args) 11 throws Throwable 12 { 13 System.out.println("Before DynamicProxy"); 14 method.invoke(target, args); 15 System.out.println("After DynamicProxy"); 16 return null; 17 } 18 }
第一种main函数
1 public class DynamicTestMain 2 { 3 public static void main(String[] args) throws Exception 4 { 5 DynamicProxy dp = new DynamicProxy(); 6 DynamicHelloWorld dhwi = new DynamicHelloWorldImpl(); 7 DynamicHelloWorld dhw = (DynamicHelloWorld)dp.newInstance(dhwi); 8 dhw.print(); 9 } 10 }
第二种main函数
1 public class DynamicTestMain 2 { 3 public static void main(String[] args) throws Exception 4 { 5 DynamicHelloWorld dhwi = new DynamicHelloWorldImpl(); 6 InvocationHandler ih = new DynamicProxy(dhwi); 7 DynamicHelloWorld dhw = 8 (DynamicHelloWorld)Proxy. 9 newProxyInstance(DynamicHelloWorld.class.getClassLoader(), 10 new Class<?>[]{DynamicHelloWorld.class}, ih); 11 dhw.print(); 12 } 13 }
不管哪种写法,运行结果都是一样的:
Before DynamicProxy Enter DynamicHelloWorldImpl.print() After DynamicProxy
动态代理,利用动态编译+反射技术,把实际对象的方法调用转换成对传入的InvocationHandler接口实现类的invoke方法的调用。
动态代理的优缺点
优点:
1.类少了
2.代理内容的实现类可以复用,可以给A接口用,也可以给B接口用。
缺点:
只能针对于接口生成代理,不能针对一个类生成代理。如果想要为某一个单独的类实现一个代理可以考虑GGLIB技术。