------- android培训、java培训、期待与您交流! ----------
类加载器的作用就是将class文件加载到内存中,以实现对其的调用。这是Java中一种特有的机制,所有的类都是通过加载器加载进内存中的,这种方式保证了安全性。
类加载器同时也是一种类,那么它也需要被类加载器加载,这么一直推算下来,总一个有所加载器的父加载器,那么它是如何加载进内存中的呢?这个类就是BootStrap类,它是用C++写的一段程序,不需要使用Java中的类加载器机制,存在于jvm中,当jvm启动的时候,就会自动加载。
可以看出来,加载器一共有三个,父类到子类的关系。分别是:
BootStrap(用C++写的,打印为null),ExtClassLoader,AppClassLoader
验证类加载器机制:
- public class LoadClassDemo {
- public static void main(String[] args) {
- //得到这个类的类加载器
- ClassLoader loader = LoadClassDemo.class.getClassLoader();
- //如果父类不为空,就打印父类加载器的名字
- while (loader != null) {
- System.out.println(loader.getClass().getName());
- loader = loader.getParent();
- }
- //打印最父类的加载器
- System.out.println(loader);
- }
- }
代理
生活中的代理
武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?
程序中的代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易
代理架构图
交叉业务的编程问题即为面向方面的编程AOP(Object Oriented Programming),AOP的目标就是要交叉业务模块化,可以采用将切面代码移动到原始原始方法的周围,这与直接在方法中编写切面代码的运行效果一样。代理是实现AOP功能的核心和关键技术
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
代理类的建立,必须得到与需要使用类有关联的代理类的Class文件对象。
通过Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces);
它必须要接收一个类加载器,和要实现代理接口的Class对象。
既然要使用Proxy这个类,就必须查看其中的构造方法、函数方法。
既然能得到Proxy的Class对象,不妨用反射列出其中的方法,可用如下方法做:
代理类的建立,必须得到与需要使用类有关联的代理类的Class文件对象。
通过Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces);
它必须要接收一个类加载器,和要实现代理接口的Class对象。
既然要使用Proxy这个类,就必须查看其中的构造方法、函数方法。
既然能得到Proxy的Class对象,不妨用反射列出其中的方法,可用如下方法做:
- System.out.println("-------------------Proxy Method-----------------------------");
- //通过反射返回类中方法数组
- Method[] methods = clazzProxy.getMethods();
- for(Method method: methods){
- String name = method.getName();
- StringBuilder sb = new StringBuilder(name);
- sb.append("(");
- //得到每个方法的参数,通过 getParameterTypes() 方法
- Class[] clazzParams = method.getParameterTypes();
- //将参数加入一个StringBuilder,并按照格式打印
- for(Class clazzParam: clazzParams){
- sb.append(clazzParam.getName()).append(",");
- }
- if(clazzParams.length!=0)
- sb.deleteCharAt(sb.lastIndexOf(","));
- sb.append(")");
- System.out.println(sb.toString());
- }
同理得到构造方法,打印结构发现Proxy没有无参构造方法,只有一个有参数构造方法:
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
Proxy类的方法有(包括object类中的方法):
- -------------------Proxy Method list -----------------------------
- hashCode()
- equals(java.lang.Object)
- toString()
- add(java.lang.Object)
- contains(java.lang.Object)
- isEmpty()
- size()
- toArray()
- toArray([Ljava.lang.Object;)
- addAll(java.util.Collection)
- iterator()
- remove(java.lang.Object)
- clear()
- containsAll(java.util.Collection)
- removeAll(java.util.Collection)
- retainAll(java.util.Collection)
- isProxyClass(java.lang.Class)
- getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
- newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
- getInvocationHandler(java.lang.Object)
- getClass()
- notify()
- notifyAll()
- wait(long)
- wait(long,int)
- wait()
动态代理的工作原理图