将.class文件从硬盘装载到内存,并进行一些处理,得到类的字节码文件,这些就是类加载器的工作。
java虚拟机中可以安装多个类加载器,系统默认有3个主要的类加载器,每个类加载器负责加载特定位置的java类:BootStrap、ExtClassLoader、AppClassLoader。
类加载器也是java类,也需要被类加载器加载,所以必然有一个类加载器不是java类,这就是BootStrap,它是用C++写的二进制代码,是嵌套到JVM内核中的,JVM启动时就存在。
类加载器之间的父子关系和管辖范围:
图解:
1. java类加载器是一个树状结构,BootStrap、ExtClassLoader、AppClassLoader之间依次有父子关系,用户还可以定义自己的类加载器。
2. 自定义类加载器通过指定一个父加载器而挂接到类加载器树上。创建空参数ClassLoader对象时,默认使用ClassLoader.getSystemClassLoader()方法返回值作为父加载器,带参数的ClassLoader构造函数可以直接接收父加载器对象。
3. 每个层次的类加载器都有自己负责加载的类所在的范围。
jre/lib/rt.jar中是java系统自带的基础类,如java.utils中的类、System类、String类和集合类等;
jre/lib/ext是java的扩展功能包,用户也可以将自定义的类放到这个目录;
CLASSPATH就是操作系统设置的CLASSPATH环境变量,一般是“.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar”,特别注意的是最前面的"."是必须的,代表当前路径,最后面不需要再加";"(末尾的分号表示在前面的目录找不到时又在当前目录找)。
注:dt.jar是java运行环境的类库,tool.jar是工具类库(编译java类是用到)。
环境变量Path中存放的是一些可执行文件的路径,这样就可以在任意目录执行这些可执行文件,Path中有%JAVA_HOME%\bin,这样可以在任何地方执行javac等命令。
/*
类加载器的一个简单演示
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
//直接运行时可以看到ClassLoaderDemo是由AppClassLoader加载,而如果用eclipse的打包工具将ClassLoaderDemo输出成jre/lib/ext目录下的itcast.jar包,再在eclipse中运行这个类,运行结果显示为ExtClassLoader.
System.out.println(ClassLoaderDemo.class.getClassLoader().getClass().getName());
//System类的类加载器是BootStrap,这个是C++类,不是java类,所以返回null
System.out.println(System.class.getClassLoader());
//可以用ClassLoader引用变量指向系统默认的3个主要类加载器对象,ClassLoader中一个抽象基类
ClassLoader loader=ClassLoaderDemo.class.getClassLoader();
//打印类加载器的继承关系
while(loader!=null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
//当前线程的类加载器
System.out.println(Thread.currentThread().getContextClassLoader().getClass().getName());
//ClassLoader对象的默认父加载器
System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
}
}
/*
运行结果:
sun.misc.Launcher$AppClassLoader
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
*/
类加载器的委托机制
1. java虚拟机加载一个类的方式:
当前线程的类加载器去加载线程中的第一个类;
如果类A中引用了类B(如A继承B),jvm将使用加载类A的类加载器去加载类B;
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
2. 每个类加载器加载类时,又先委托其上级类加载器
每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类,这就是类加载器的委托模式。
类加载器一级级委托到BootStrap类加载器,当BootStrap加载不了当前所要加载的类时,然后才一级级回退到子孙类加载器去进行真正的加载;当回退到最初的发起者类加载器时,如果它自己也不能完成类的装载,就抛出ClassNotFoundException异常。
可以通过自定义类加载器来演示委托机制,先说明下自定义类加载器的编写原理:
1. 自定义类加载器必须继承抽象类ClassLoader,ClassLoader中加载类的方法有loadClass()和findClass()。
2. loadClass(String name)方法执行原理是先找父类加载器去加载类,所有父类加载器都找不到时子类再调用findClass(String name)方法来加载类。
3. 自定义MyClassLoader时,只需要复写findClass()方法,不需要复写loadClass(),这样可以不破坏类加载器的委托机制,也可以让子类按照自己定义的方式去加载类,这也是模板方法设计模式的体现。
4. findClass()方法可以得到Class实例对象,方法内部一般是通过调用defineClass方法来实现,defineClass()方法可以将一个字节数组转换成Class实例。
findClass()可能抛出ClassNotFound异常,defineClass()抛出ClassFormatError错误。
/*
自定义类加载器,包含对.class文件进行加密的操作,加密后的类只能由MyClassLoader来加载,系统自带的类加载器无法加载
*/
import java.io.*;
public class MyClassLoader extends ClassLoader
{
public static void main(String[] args) throws IOException
{
String srcPath=args[0];
String destDir=args[1];
String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destPath=destDir+"\\"+destFileName;
System.out.println(srcPath+"..."+destPath);
FileInputStream fis=new FileInputStream(srcPath);
FileOutputStream fos=new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips,OutputStream ops) throws IOException
{
int b=-1;
//对.class文件的每个二进制位与1进行异或,实现加密
while((b=ips.read())!=-1){
ops.write(b^0xff);
}
}
//复写findClass()方法
private String classDir;
protected Class> findClass(String name) throws ClassNotFoundException{
String classFileName=classDir+"\\"+name+".class";
System.out.println(classFileName);
try{
//读取.class文件到字节数组中,并再次执行异或操作,对.class文件解密
FileInputStream fis=new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
//调用defineClass()方法,将字节数组转换成Class对象
byte[] bytes=bos.toByteArray();
return defineClass(bytes,0,bytes.length);
}
catch(Exception e){
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir=classDir;
}
}
/*
编写一个用于测试的java类,让它继承Date类,用这个类的.class文件测试自定义的MyClassLoader
*/
import java.util.Date;
public class ClassLoaderAttachment extends Date {
public String toString(){
return "hello itcast!!!";
}
}
/*
测试MyClassLoader是否可以加载特定目录下的java类。
*/
import java.util.Date;
public class MyClassLoaderTest {
public static void main(String[] args) throws Exception
{
/*
bin目录下有未加密的ClassLoaderAttachment().class文件时,下面2句可正常运行,ClassLoaderAttachment类由AppClassLoader加载器加载
System.out.println(new ClassLoaderAttachment().toString());
System.out.println(ClassLoaderAttachment.class.getClassLoader().getClass().getName());
*/
//CLASSPATH目录下,也即bin目录下有ClassLoaderAttachment.class文件时,由于类加载器的委托机制,如果class文件未加密,下面的代码可正常运行,由AppClassLoader加载,如果已用MyClassLoader加密,则运行时会报ClassFormatError.
//删除bin目录下的ClassLoaderAttachment.class文件后,运行下面的代码,仍可以正常运行,此时ClassLoaderAttachment类就是由MyClassLoader来加载的
Class clazz=new MyClassLoader("lib").loadClass("ClassLoaderAttachment");
//不能写ClassLoaderAttachment类型的引用变量,否则编译时不通过
Date d1=(Date)clazz.newInstance();
System.out.println(d1);
}
}
动态代理的基本用法演示:
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception
{
//Proxy的静态方法getProxyClass获取动态代理Class对象
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
//列出动态代理类的所有构造方法和参数签名
System.out.println(".....begin constructors list.....");
Constructor[] constructors=clazzProxy1.getConstructors();
for(Constructor constructor:constructors){
String name=constructor.getName();
StringBuilder sbu=new StringBuilder(name);
sbu.append("(");
Class[] clazzParams=constructor.getParameterTypes();
for(Class clazzParam:clazzParams){
sbu.append(clazzParam.getName()).append(",");
}
if(clazzParams!=null&&clazzParams.length!=0){
sbu.deleteCharAt(sbu.length()-1);
}
sbu.append(")");
System.out.println(sbu.toString());
}
//列出动态代理类的所有方法和参数签名
System.out.println(".....begin Methods list.....");
Method[] methods=clazzProxy1.getMethods();
for(Method method:methods){
String name=method.getName();
StringBuilder sbu=new StringBuilder(name);
sbu.append("(");
Class[] clazzParams=method.getParameterTypes();
for(Class clazzParam:clazzParams){
sbu.append(clazzParam.getName()).append(",");
}
if(clazzParams!=null&&clazzParams.length!=0){
sbu.deleteCharAt(sbu.length()-1);
}
sbu.append(")");
System.out.println(sbu.toString());
}
//创建动态代理类的实例对象
//方式一:
System.out.println(".....begin create instance object.....");
Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler1 implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHandler1());
//代理类中从java.lang.Object类继承的方法中,只有hashCode(),equals()和toString()方法会委托给Invocationhandler执行,运行时会执行InvocationHandler中的invoke()方法,其它从Object类中继承的方法不会委托给Invocationhandle.
System.out.println(proxy1);
//没有返回值的clear()方法可正常执行,因为InvocationHandler返回null,与clear()原先的返回类型void不矛盾。
proxy1.clear();
//运行时报错,因为size()应该返回整数值,但现在委托给InvocationHandler后返回的是null,不匹配
//System.out.println(proxy1.size());
//方式二:匿名内部类方式
Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//方式三:直接使用Proxy的静态方法newProxyInstance()方法
Collection proxy3=(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
//创建目标类实例对象
ArrayList target=new ArrayList();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//在目标类的基础功能前进行功能扩展
long beginTime=System.currentTimeMillis();
//执行目标对象的method方法
Object retVal=method.invoke(target, args);
//在目标类的基础功能后进行功能扩展
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+" running time of "+(endTime-beginTime));
return retVal;
}
}
);
//每次调用proxy3的add方法,其实都是执行InvocationHandler子类中的invoke()方法,将proxy3赋给invoke()方法的第一个参数,add代表的Method对象赋经第从此参数,add()的参数"sss"赋给invoke()的第3个参数args
proxy3.add("sss");
proxy3.add("wtet");
Object retVal=(Object)proxy3.add("gfdh");
System.out.println(retVal);
System.out.println(proxy3.size());
//动态代理类的getClass()方法不会委托给InvocationHandler执行,所以仍打印是的$Proxy0,而不是ArrayList
System.out.println(proxy3.getClass().getName());
}
}
/*运行结果:
com.sun.proxy.$Proxy0
.....begin constructors list.....
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
.....begin Methods list.....
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
contains(java.lang.Object)
isEmpty()
size()
toArray()
toArray([Ljava.lang.Object;)
addAll(java.util.Collection)
iterator()
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getInvocationHandler(java.lang.Object)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait(long,int)
wait(long)
wait()
getClass()
notify()
notifyAll()
.....begin create instance object.....
null
add running time of 1
add running time of 0
add running time of 0
true
size running time of 0
3
com.sun.proxy.$Proxy0
*/
/*
代理类的通用方法
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest2 {
public static void main(String[] args) {
final ArrayList target=new ArrayList();
Collection proxy3 =(Collection)getProxy(target,new MyAdvice());
proxy3.add("sss");
proxy3.add("wtet");
Object retVal=(Object)proxy3.add("gfdh");
System.out.println(retVal);
System.out.println(proxy3.size());
}
//将目标类实例对象的final引用变量target作为参数注入,供Invocation实现类访问
//将代理类要实现的扩展功能抽取到一个对象,并把这个对象作为参数传递,Invocation实现类中调用了该对象的方法,就相当于执行了外界提供的扩展功能
private static Object getProxy(final Object target,final Advice advice) {
Object proxy3=Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//在目标类的基础功能前进行功能扩展
advice.beforeMethod(method);;
//执行目标对象的method方法
Object retVal=method.invoke(target, args);
//在目标类的基础功能后进行功能扩展
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
/*
将代理类实现的系统功能抽取到Advice接口中
*/
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
/*
实现具体的扩展功能类MyAdvice
*/
import java.lang.reflect.Method;
public class MyAdvice implements Advice
{
long beginTime=0;
@Override
public void beforeMethod(Method method) {
beginTime=System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+" running time of "+(endTime-beginTime));
}
}
/*
采用工厂模式和配置文件实现类似spring的AOP框架。
类中的getBean()方法,根据配置文件可以选择获取javaBean类还是其代理类
*/
package aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
Properties props=new Properties();
public BeanFactory(InputStream ips){
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getBean(String name){
String className=props.getProperty(name);
Object bean=null;
try {
Class clazz=Class.forName(className);
bean = clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//如果Bean
if(bean instanceof ProxyFactoryBean){
Object proxy=null;
ProxyFactoryBean proxyFactoryBean=(ProxyFactoryBean)bean;
try {
Advice advice=(Advice)Class.forName(props.getProperty(name+".advice")).newInstance();
Object target=Class.forName(props.getProperty(name+".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy=proxyFactoryBean.getProxy();
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
/*
*定义通用代理类,由成员变量advice决定代理类的扩展功能,由target代表目标类实例
*/
package aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean {
private Advice advice;//有包名的类不能调用无包名的类,需要把Advice.java文件复制或移到当前包中
private Object target;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
Object proxy3=Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//在目标类的基础功能前进行功能扩展
advice.beforeMethod(method);
//执行目标对象的method方法
Object retVal=method.invoke(target, args);
//在目标类的基础功能后进行功能扩展
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
/*
测试实现的AOP框架
*/
package aopframework;
import java.io.InputStream;
public class AopFrameworkTest {
public static void main(String[] args) throws Exception
{
InputStream ips=AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean=new BeanFactory(ips).getBean("sss");
System.out.println(bean.hashCode());
}
}