代理模式:
当直接访问某些对象存在问题时候可以通过一个代理对象来间接访问,也就是使用一个代理主题角色,它内部包含了被代理的角色的一个引用,从而可以在任何时候都可以代替被代理角色.代理角色通常在客户端调用真实主题对象之前或之后还需要执行其他操作,而不仅仅是单纯的调用被代理角色的操作。
切记:代理类的每个方法要和目标类的方法要一致。代理类的每个方法去调用目标类的一个方法,通过其实例。
典型的代理类实现代码。
public class Proxy implements Subject
{
private RealSubject realSubject = new RealSubject();
public void preRequest()
{
//执行前要做的额外的事情
}
public void request()
{
preRequest();
realSubject.request();
postRequest();
}
public void postRequest()
{
//执行之后要做的额外的事情
}
}
但是,我们发现,如果按照这种方法使用代理模式,那么真实主题角色必须是实现已经存在的,这将导致系统中的类个数急剧增加。
如何在事先不知道真实主题角色的情况下使用代理主题角色,这就是动态代理。
Java提供了一个Proxy类,专门用于生成一个代理类。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
通过这个函数可以得到一个代理类的字节码文件对象。
然后使用这个字节码文件对象去创建一个代理类的实例
protected Proxy(InvocationHandler h)
也就是想要去创建一个实例的时候,必须给这个创建的代理类的构造函数传递一个参数InvocationHandler,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
也就是InvocationHandler去完成上边要完成对被代理的类的方法去调用,而且还要完成对在客户端调用真实主题对象之前或之后还需要执行其他操作。
Object invoke(Objectproxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。
实例:
package day15;
import java.lang.reflect.*;
public class DynamicProxy implements InvocationHandler
{
public static void main(String[] args) throws Exception
{
Class cls = Proxy.getProxyClass(AbstractSubject.class.getClassLoader(),AbstractSubject.class);
Constructor csr = cls.getConstructor(InvocationHandler.class);
AbstractSubject as =(AbstractSubject)csr.newInstance(new DynamicProxy(new RealSubjectA()));
as.request();
}
private AbstractSubject as;
public DynamicProxy(AbstractSubject as)
{
this.as = as;
}
public void preRequest(){
System.out.println("调用之前");
}
public void postRequest()
{
System.out.println("调用之后");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
preRequest();
method.invoke(as, args);
postRequest();
return null;
}
}
但是,上边的这种方式很麻烦
首先创建一个接口,也就是告知它要生成那些方法在这个动态类中。
然后再由创建好的字节码对象去创建一个真正的代理对象,传入一个InvocationHandler接口的实例。
然后再由这个实例去完成对方法的调用。
我们能不能将创建动态类和创建动态类的实例合二为一呢?
可以直接使用Proxy中的
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
直接返回这个代理类的实例。
等价于:
AbstractSubject as = (AbstractSubject)Proxy.newProxyInstance(AbstractSubject.class.getClassLoader(),new Class[]{AbstractSubject.class},new DynamicProxy(new RealSubjectA()));
as.request();
而这个new RealSubjectA()就是这个代理类的目标。
也就是要对那个真实的对象做一个代理操作。
当我们每次调用一个代理类的方法的时候(如reuqest(),内部就会调用InvocationHandler子类中的invoke方法。在invoke方法中完成特定的操作。也就是我们生成的那些方法(如reuqest())的内部都是在调用invoke方法。
这个方法
public Object invoke(Object proxy, Method method, Object[] args)
接收三个参数。三个参数是什么?
当调用一个代理对象的方法时候,比如as.request();
涉及到了三个参数:as为调用代理对象(也就是创建好的代理对象),request调用代理对象的哪个方法。这个方法的参数
在调用方法的时候,一定要执行的是目标中的方法。也就是创建的被代理类的方法。
而invoke返回的值,正是执行这个被代理类的方法返回的参数。
也就是返回值,参数,谁调用的,调用哪个方法都包含在了invoke方法中。
这也就是将目标返回的结果返回到了代理身上去了。
工作原理图
由于上边对于动态代理类来说
public void preRequest(){
System.out.println("调用之前");
}
public void postRequest()
{
System.out.println("调用之后");
}
preRequest();
method.invoke(as, args);
postRequest();
这些代码是硬编码到函数中的,不能动态改变,现在就要动态的改变这些代码。
我们要将这些系统功能的代码,以参数的形式传递进来。在程序运行的时候临时的去设置。
我们在写的时候,不是再去传入一个函数的形式,而是传入一个对象的形式,让这个对象去调用这些方法。(也就是给这个InvocationHandler的子类传递一个对象进去,让对象调用其上的方法。)
Obj.preRequest();
method.invoke(as, args);
Obj.postRequest();
也就是AOP中将切面的方法封装成为对象。用对象调用方法,也就相当于执行了切面的方法。
也就是InvocationHandler的子类传递两个参数,一个是目标,一个是系统功能(切面的方法)Advice参数。
创建一个Advice接口,用于指定在调用指定方法之前,代理类要做的事情
public interface Advice
{
void preRequest();
void postRequest();
}
创建一个真实的Advice,去完成代理所要加的功能
public class MyAdvice implements Advice{
@Override
public void postRequest() {
System.out.println("在调用之前");
}
@Override
public void preRequest() {
System.out.println("在调用之后");
}
}
在动态代理类中就可以这样去动态创建一个代理类所加的功能,并去调用已有的方法、
动态代理类的全部代码:
package day15;
import java.lang.reflect.*;
public class DynamicProxy implements InvocationHandler
{
public static void main(String[] args) throws Exception
{
// Class cls = Proxy.getProxyClass(AbstractSubject.class.getClassLoader(),AbstractSubject.class);
//
// Constructor csr = cls.getConstructor(InvocationHandler.class);
//
// AbstractSubject as =(AbstractSubject)csr.newInstance(new DynamicProxy(new RealSubjectA()));
//
// as.request();
AbstractSubject as = (AbstractSubject)Proxy.newProxyInstance(AbstractSubject.class.getClassLoader(),new Class[]{AbstractSubject.class},new DynamicProxy(new RealSubjectA(),new MyAdvice()));
as.request();
}
private AbstractSubject as;
private Advice advice;
public DynamicProxy(AbstractSubject as,Advice advice)
{
this.as = as;
this.advice = advice;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.postRequest();
method.invoke(as, args);
advice.preRequest();
return null;
}
}