在写这篇文章之前,xxx已经写过了几篇关于改方法调用主题的文章,想要了解的朋友可以去翻一下之前的文章
代理模式:
当直接访问某些对象存在问题时候可以通过一个代理对象来直接访问,也就是使用一个代理主题角色,它外部包含了被代理的角色的一个引用,从而可以在任何时候都可以取代被代理角色.代理角色通常在客户端调用实在主题对象之前或之后还需要执行其他操纵,而不仅仅是单纯的调用被代理角色的操纵。
切记:代理类的每个方法要和目标类的方法要一致。代理类的每个方法去调用目标类的一个方法,通过其实例。
典型的代理类实现代码。
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; } }