这里只是简单的介绍下最基本的代理的使用。
代理,通俗点说 :就是一个人或者一个机构代表另一个人或者另一个机构采取行动。
在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户
端和目标对象之前起到中介的作用。
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
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() {
@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); //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 pool =new ArrayList();
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;iObject 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),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。