动态代理
代理对象存在的价值:主要用于拦截对真实业务对象的访问。
代理对象有什么方法?
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。
如何编写生成代理对象的类,两个要素:
代理谁
如何生成代理对象
代理谁?
设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
如何生成代理对象?
设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
1.生成代理对象使用哪个类装载器
2.生成哪个对象的代理对象,通过接口指定
3.生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。
初学者必须必须记住的2件事情:
Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。
AOP:面向切面的编程
AOP的底层实现就是通过动态代理来做到的。
动态代理
在代理模式基础上发展的,它不在是对单一的类型进行代理,而是可以对任意的一个实现了接口的类的对象做代理。
动态代理实现
两种方式:
1.通过jdk中提供的Proxy类来实现
这种方式要求,被代理类必须实现接口,即只能为接口做代理.
2.通过cglib来实现,它不要求实现接口。
第一种方式的代码实现:
Proxy类中有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);
参数:
loader: 要求,传递的是被代理类的类加载器ClassLoader
类加载器怎样获取:
得到其Class对象,在Class类中提供一个方法getClassLoader();
interfaces: 要求:得到被代理对象所实现的接口的所有Class对象。
怎样获取所有实现接口的Class对象?
得到其Class对象,在Class类中提供一个方法 getInterfaces();它返回的是Class[],就代表所实现接口的所有Class对象。
h: 它的类型是InvocationHandler,这是一个接口。
InvocationHandler 是代理实例的调用处理程序 实现的接口。
InvocationHandler接口中有一个方法invoke;
参数 proxy 就是代理对象
参数 method 就是调用方法
参数 args 就是调用的方法的参数
返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.
public Object invoke(Object proxy, Method method, Object[] args);
演示类加载器
问题:类加载器如果获取?
在Class类中有一个方法 getClassLoader()它返回的就是一个类加载器.
1.获取引导类加载器
ClassLoader cl = String.class.getClassLoader();
System.out.println(cl);
结果是null.
原因:引导类加载器特殊,它根本就不是java实现。所有在得到引导类回载器是结果就是null.
2.扩展类加载器
ClassLoader cl = AccessBridge.class.getClassLoader();
System.out.println(cl); //sun.misc.Launcher$ExtClassLoader@9cb0f4
3.应用类加载器
ClassLoader cl = this.getClass().getClassLoader();
System.out.println(cl); //sun.misc.Launcher$AppClassLoader@164dbd5
全盘负责委托机制
全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。
委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找。
类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。
自定义类加载器
创建了一个类 javax.activation.MimeType,在这个类中有一个方法show();当jvm加载这个类时,因为在rt.jar包下也存在一个MimeType类,并且包名都一样,这时jvm就会使用引导类加载器加载这个类,而我们想得到的其实是应该由应用类加载器加载的Class.
解决方案:自定义类加载器.
1.创建一个类,去继承自ClassLoader
2.重写findClass方法,在这个方法中通过defineClass将一个.class文件转换成Class对象.