——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
代理模式
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
例如:如果已经编写好了一个类,当我们想对该类增加某一种功能是,但为了不改变已经编写好的代码,就不能够直接调用目标对象,通过代理模式,在代理类上增加我我们想要的功能,又不影响以前已经编写好的代码,通过对代理对象的调用,代理对象在再调用目标对象,这样就能达到我们想要的功能由于在客户端和真实主题之间增加了代理对象,当代理对象较多是,相应的代理类也要去增多,因此有些类型的代理模式可能会造成请求的处理速度变慢。
代理结构:
客户:代理对象或真实对象的调用者。
抽象角色:声明真实对象和代理对象的共同接口。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
代理结构图:
练习:
/*
* 代理模式
*/
public class GenericTest {
public static void main(String[] args) {
//直接调用
way(new RealSubject());
//通过代理类调用
way(new Proxy(new RealSubject()));
}
//方法对接口引用
public static void way(Subject sub)
{
sub.show();
}
}
//代理接口
interface Subject
{
void show();
}
//真实类
class RealSubject implements Subject
{
@Override
public void show() {
System.out.println("I am RealSubject");
}
}
//代理类
class Proxy implements Subject
{
RealSubject real = null;
public Proxy(RealSubject real){
this.real = real;
}
@Override
public void show() {
System.out.println("-----------");
System.out.println("I am Proxy");
System.out.println("增加的功能");
real.show();
System.out.println("-----------");
}
}
运行效果:
动态代理
观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
动态代理作为代理模式的一种扩展形式,java中提供了能够生成代理类的API,被代理对象的类要求必须实现接口。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类。
Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
Static Class getProxyClass (ClassLoader loader,Class[] interfaces):获得一个代理类,其中loader是类加载器,interfaces是真实类所拥有的全部接口的数组。
Static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。
我们通过getProxyClass方法指定有代理的接口如类加载器,并返回生成的代理类,该代理类只有一个构造函数:Proxy(InvocationHandler);该构造函数接受一个InvocationHandler接口的子类对象,所以要想生成代理类对象,就要求我们必须实现InvocationHandler接口,接口中有一个invoke(Object proxy, Method method, Object[] args)方法。
/*
* 动态代理
*/
public class GenericTest {
public static void main(String[] args) throws Exception {
//获得Collection接口的代理类
Class cla = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//得到代理类的构造函数
Constructor method = cla.getConstructor(InvocationHandler.class);
//简单实现InvocationHandler接口,生成代理对象
Collection proxy = (Collection) method.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}});
}
在这里我们并没有看到要代理的真实对象,为什么我们要求实现InvocationHandler接口并重写invoke方法呢?那我们就要看清代理类的具体工作的原理:
在代理的类中实现了接口中的所有方法,在重写的方法中调用了invoke方法,所以在代理类对象调用的方法在对象的内部只做了对invoke方法的调用,我们想要的想要增加的功能可以在invoke方法中编写,同时也在改方法中做对真实对象方法的调用,那么如和才可以做到代理对象与真实对象的方法调用一致呢:
现在我编写一个简单说明:
/*
* Method 方法的应用解析
*/
public class GenericTest {
public static void main(String[] args) {
Class cla = Collection.class;
try {
Method method = cla.getMethod("add", Object.class);
ArrayList<String> list = new ArrayList<String>();
method.invoke(list, 15);
System.out.println(list.size());
} catch ( Exception e) {
e.printStackTrace();
}
}
}
通打印结果为1,通过以上程序说明Method对象可以调用任意对象,只要该对象中的方法名、参数与Method对象得到的方法名与参数一致,都可以调用。
invoke(Object proxy, Method method, Object[] args)中的三个参数
proxy : 代理类的对象
method :代理类对象调用方法时传进相应方法的Method对象
args:代理类对象调用方法时传进相应方法的参数值
现在我们知道可以通过方法的method对象直接调用目标对象,并把参数传进,因为代理类的的方法与真实对象的方法名相同的,所以就可以达到了方法调用一致:
现在我们在上面的程序中增加被代理的对象:
/*
* 动态代理
*/
public class GenericTest {
public static void main(String[] args) throws Exception {
//获得Collection接口的代理类
Class cla = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//得到代理类的构造函数
Constructor method = cla.getConstructor(InvocationHandler.class);
//简单实现InvocationHandler接口,生成代理对象
Collection proxy = (Collection) method.newInstance(new InvocationHandler(){
//被代理的对象
ArrayList list = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("增加的功能部分");
//真实对象的方法的调用
Object obj = method.invoke(list, args);
return obj;
}});
proxy.add("abc");
proxy.add(15);
proxy.add(true);
System.out.println(proxy.size());
}
}
运行结果:
通过结果可以看到每次调用代理类的方法,都会运行一次invoke方法,说明代理类的方法中调用了invoke方法,结果为3,说明我们得到了被代理对象返回值,两对象方法的调用是一致的,这就是动态代理,通过java的API生成指定的代理对象。