这里只是简单的介绍下最基本的代理的使用。
代理,通俗点说 :就是一个人或者一个机构代表另一个人或者另一个机构采取行动。
在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户
端和目标对象之前起到中介的作用。
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
UML图
从上面的图我们能看到代理涉及的角色:
抽象对象角色:声明了目标对象和代理对象的共同接口,
这样一来在任何可以使用目标对象的地方都可以使用代理对象。
目标对象角色:定义了代理对象所代表的目标对象。(也就是要代理的是谁)
代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候
操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。
代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用
传递给目标对象。
我们在这里演示一个房东,中介和客户之间的关系(中介就是代理)
1,我们需要一个抽象的对象角色:(接口)
public interface IRenter { public abstract void rent(); }
2,我们需要的目标对象:(就是实现的接口的类)
public class Renter implements IRenter{ @Override public void rent() { System.out.println("我是房东,开始收费咯咯"); } }
3,代理对象的角色(就是我们的代理)
我们需要用到这个Java中代理使用的类
Object o =Proxy.newProxyInstance(loader, interfaces, h)
public class Client { @Test public void Test2(){ final Renter r =new Renter();//被代理的对象 //o是中介代理之后的对象 Object o =Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IRenter.class},new InvocationHandler() <span style="white-space:pre"> </span>{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//从这里开始就是开始拦截要控制的内容了,如if(method.getName.equals('rent')){将方法名为rent的函数,做出你想要的事情} // System.out.println("aaaa");//这里只是简单的每个方法之前输出 System.out.println("收点费用"); return method.invoke(r, args); //<span style="font-family: 'ms shell dlg';">return method.invoke(要代理的对象, 参数传进来什么参数就放出什么参数,这里相当于全部放开); } } ); IRenter ir =(IRenter) o;//最后在使用的时候需要 将对象 强转为接口类型的 ir.rent(); } }
上面的只是一个简单的介绍例子,下面介绍一个在实际中的使用例子。
我们在连接数据库的时候,都是采用的链接池的方式,但是链接池里面的连接都是有限个的,
所以我们需要每个用完之后就将其放回池中,但是单独去写一个函数将其放回池中,不太好用,
所以,我们就将 con.close()关闭连接的时候,不去关,而是直接的放回池中,
让其他的线程来调用,也就是需要拦截con里面的close方法。
所需要的三个角色:
1,接口对象 connection接口
2,实现对象 Connectioncon=DriverManager.getConnection(url, user, password);//在进行连接的时候可以。
3,需要我们的代理类。
下面这是整个连接池的代码:
public class hibernateFactory2 { private static final int NUM=3; private static List<Connection> pool =new ArrayList<Connection>(); static{ //读取配置文件 Properties p =new Properties(); try { p.load(hibernateFactory.class.getClassLoader().getResourceAsStream("jdbc.properties")); //读取里面的值,一直修改配置文件即可 String driver=p.getProperty("driver"); String url=p.getProperty("url"); String user=p.getProperty("username"); String password=p.getProperty("password"); System.out.println(driver+url+user+password); Class.forName(driver); for(int i=0;i<NUM;i++){ final Connection con=DriverManager.getConnection(url, user, password); //采用动态代理开始进行对connection接口实现代理,对con.close,实现换回去 </span><span style="color:#ff0000;">Object o =Proxy.newProxyInstance(hibernateFactory2.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("close")){ //拦截close方法 pool.add((Connection)(proxy));//将连接还回池 System.out.println("换回来了。。。"); return null; } return method.invoke(con, args);//其余的全部放行 } }); pool.add((Connection)o); } // System.out.println("初始化完毕"+con); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //下面是获取连接。 public static synchronized Connection getCon() throws Exception{ if(pool.size()<=0){ System.out.println("pool中已经没有连接"); return getCon() ; } System.out.println("使用一个。。"); return pool.remove(0); //两种方法都是可以的 // while(pool.size()<=0){ // System.out.println("池中已经乜有连接"); // Thread.sleep(1000); // } // return pool.remove(0); // } }}
在上面的基础上,我们写了一个通用的版本
public class proxyUtils { public static Object getProxy(final Object srcobj){ Object o = Proxy.newProxyInstance(proxyUtils.class.getClassLoader(), srcobj.getClass().getInterfaces(), //这句可以修改 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("被拦击了。。。。");//这里采用拦截 return method.invoke(srcobj, args);//返回值我们也可以进行一些列的操作 } } ); return o; } }但是我们在调用的这个工具的时候,必须传进来一个实例对象,也就是实现了的类
如 IA 为接口 A 为实现类
A a =new A();
IA ia =(IA)proxyUtils.getProxy(a);
通过ia 的对象调用方法就可以。
上面方法的另一种实现形式
public class proxyUtils2 implements InvocationHandler{ private Object srcobj; public proxyUtils2(Object srcobj) { this.srcobj=srcobj; } public static Object getProxy(final Object srcobj){ Object o = Proxy.newProxyInstance(proxyUtils2.class.getClassLoader(), srcobj.getClass().getInterfaces(), //这句可以修改 ,在不知道的情况下,这种方法最好 new proxyUtils2(srcobj)); return o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("在这里采用拦截"); Object obj =method.invoke(srcobj, args);//在这里我们还可以进行对返回的值进行操作,这也是代理带来的好处 return obj; } }
代理应用方面(从别人哪里看到的)
代理模式的应用形式
(1)远程代理(Remote Proxy) -可以隐藏一个对象存在于不同地址空间的事实。也使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
(2)虚拟代理(Virtual Proxy) – 允许内存开销较大的对象在需要的时候创建。只有我们真正需要这个对象的时候才创建。
(3)写入时复制代理(Copy-On-Write Proxy) – 用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。是虚拟代理的一个变体。
(4)保护代理(Protection (Access)Proxy) – 为不同的客户提供不同级别的目标对象访问权限
(5)缓存代理(Cache Proxy) – 为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。
(6)防火墙代理(Firewall Proxy) – 控制网络资源的访问,保护主题免于恶意客户的侵害。
(7)同步代理(SynchronizationProxy) – 在多线程的情况下为主题提供安全的访问。
(8)智能引用代理(Smart ReferenceProxy) - 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
(9)复杂隐藏代理(Complexity HidingProxy) – 用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Façade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。