类加载器
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类加载器也是一个具体的对象。
委托机制:最先找到上级(JRE/lib/rt.jar).然后逐步往下,也可以写一个加载器,然后让它指定去找。
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的.jar包中后,运行结果为ExtClassLoader的原因。
编写自己的类加载器
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class MyClassLoader extends ClassLoader { public static void main(String[] args)throws Exception { String srcPath=args[0]; String destDir=args[1]; FileInputStream fis=new FileInputStream(srcPath); String destFileName=srcPath.substring(srcPath.lastIndexOf('/')+1);//路径的File,加1是说明从盘符下面开始 String destPath=destDir+"\\"+destFileName; FileOutputStream fos=new FileOutputStream(destPath); cypher(fis,fos); fis.close(); fos.close(); } private static void cypher(InputStream ips,OutputStream ops) throws Exception{ int b=-1; while(ips.read()!=-1){ ops.write(b); ops.write(b^0xff); } } private String classDir; @Override//类加载器 protected Class<?> findClass(String name) throws ClassNotFoundException { // TODO Auto-generated method stub String classFileName=classDir+"\\"+name+".class";//通过类找出硬盘上的文件。 try { FileInputStream fis=new FileInputStream(classFileName); ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义一个字节数据流 cypher(fis,bos);//解密 fis.close(); byte[] bytes=bos.toByteArray(); return defineClass(bytes, 0, bytes.length); } catch (Exception e) {//子类不能被父类抛出 e.printStackTrace(); }//加载这个文件 return super.findClass(name);//调用父类的class } //去哪个目录下寻找那份文件 public MyClassLoader(){ } public MyClassLoader(String clasPath){ this.classDir=classDir; } }
类加载器不能加载这种非public的类
/* Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader can not access a member of class MyTest with modifiers "" */ /* class MyTest { public void test() { System.out.println("hello,www.it315.org"); } } */
AOP:
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理机制,那就将需要太多的代理类,全部采用静态代理方法,将是一件非常麻烦事,写成百上千个代理类,是不是太累!
代理类的各种方法中通常除了要调用目标和相应方法和对外返回目标返回的结果外,还可以在代理中的如下四个
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import javax.xml.ws.spi.Invoker; public class ProxyTest { public static void main(String[] args) throws Exception{ Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class); //对于clazz,我们通常认为它是字节码 System.out.println(clazzProxy1.getName()); System.out.println("begin constructors list-----:"); Constructor[] constructors=clazzProxy1.getConstructors();//得到它的构造方法 for(Constructor constructor:constructors){ String name=constructor.getName(); StringBuilder sBuilder=new StringBuilder();//用 StringBuilder效率更高一点 sBuilder.append('('); Class [] clazzParams=constructor.getParameterTypes();//得到参数的类型,返回的是一个class的数组。 for(Class clazzParam: clazzParams){//取出每个参数的名字 sBuilder.append(clazzParam.getName()).append(','); } if(clazzParams!=null&&clazzParams.length!=0) sBuilder.deleteCharAt(sBuilder.length()-1);//去掉最后一个参数 sBuilder.append(')'); System.out.println(sBuilder.toString()); } //StringBuilder与StringBuffered的区别: //在动态上,都是往字符串中添加字符,在单线程下,用StringBuilder效率要高一点,在多线程下StringBufferd要高点 System.out.println("----------begin methods list----------"); /*$Proxy0() $Proxy0(InvocationHandler,int)*/ Method[] methods = clazzProxy1.getMethods(); for(Method method : methods){ String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append('('); Class[] clazzParams = method.getParameterTypes(); for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(','); } if(clazzParams!=null && clazzParams.length != 0) sBuilder.deleteCharAt(sBuilder.length()-1); sBuilder.append(')'); System.out.println(sBuilder.toString()); } //创建动态类的实例对象用调用方法 System.out.println("-----begin create instance-----"); //Object obj=clazzProxy1.newInstance();//不能这能调用构造参数的实例化方法。 //构造方法接受一个参数,然后再去调用构造方法。 Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class); class MyInvocationHander1 implements InvocationHandler{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } Collection proxy1=(Collection) constructor.newInstance(new MyInvocationHander1()); System.out.println(proxy1); proxy1.clear();//如果不报空指针异常,就说明这个对象是有的。 //proxy1.size();//出错了,那么就判定size方法出问题了,因为size方法有返回值,clear方法没有。 Collection proxy2= (Collection) constructor.newInstance(new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); //代理对象 Collection proxy3=(Collection) Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[] {Collection.class}, new InvocationHandler(){ public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ ArrayList target=new ArrayList(); long beginTime=System.currentTimeMillis(); Object retVal=method.invoke(target, args); long endTime=System.currentTimeMillis(); System.out.println(method.getName()+" running time of: "+(endTime-beginTime)+"ms"); return retVal; } } ); proxy3.add("zxx");//每调用一个add方法,invoke就被执行 proxy3.add("lhm"); proxy3.add("hjl"); System.out.println(proxy3.size()); } }
让动态生成的类成为目标类的代理:
import java.lang.reflect.Method; public class MyAdvice implements Advice { long beginTime = 0; public void afterMethod(Method method) { // TODO Auto-generated method stub System.out.println("开始啦!"); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - beginTime)); } public void beforeMethod(Method method) { // TODO Auto-generated method stub System.out.println("结束啦!"); beginTime = System.currentTimeMillis(); } }
import java.lang.reflect.Method; public interface Advice { void beforeMethod(Method method); void afterMethod(Method method); }
object 中有三个方法交给handler,分别是hashcode,equals,toString.
其他的不委托,都有自己的实现方法。
实现类似spring的可配置的AOP框架:
import java.io.IOException; import java.io.InputStream; import java.util.Properties; import cn.itcast.day3.Advice; public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips){ try { props.load(ips); } catch (IOException e) { // TODO Auto-generated catch block 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 (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 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) { // TODO Auto-generated catch block e.printStackTrace(); } return proxy; } return bean; } }
import java.io.InputStream; import java.util.Collection; public class AopFrameworkTest { /** * @param args */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties"); Object bean = new BeanFactory(ips).getBean("xxx"); System.out.println(bean.getClass().getName()); ((Collection)bean).clear(); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactoryBean { private Advice advice; 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() { // TODO Auto-generated method stub Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - beginTime)); return retVal;*/ advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } } ); return proxy3; } }
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.day3.aopframework.ProxyFactoryBean //代理
xxx.advice=cn.day3.MyAdvice
xxx.target=java.util.ArrayList//目标