Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架
类加载器
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//目标
Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架的更多相关文章
- JAVA基础知识之JVM-——自定义类加载器
JVM中除了根加载器之外其他加载器都是ClassLoader的子类实例, 可以通过扩展ClassLoader的子类,通过重写方法来实现自定义的类加载器. ClassLoader中有两个关键的方法如下, ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- Java内存管理-掌握自定义类加载器的实现(七)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...
- [转载] Java高新技术第一篇:类加载器详解
本文转载自: http://blog.csdn.net/jiangwei0910410003/article/details/17733153 首先来了解一下字节码和class文件的区别: 我们知道, ...
- Java高新技术第一篇:类加载器详解
首先来了解一下字节码和class文件的区别: 我们知道,新建一个Java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...
- 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...
- java 类加载器的委托机制
l 当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢? 1.首先当前线程的类加载器去加载线程中的第一个类. 2.如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B. 3 ...
- Java内存管理-掌握虚拟机类加载器(五)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载.连接(验证.准 ...
- 【Java虚拟机8】自定义类加载器、类加载器命名空间、类的卸载
前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起. 自定义类加载器 例1 一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的clas ...
随机推荐
- Codeforces Round #430 B. Gleb And Pizza
Gleb ordered pizza home. When the courier delivered the pizza, he was very upset, because several pi ...
- 【Tensorflow系列】使用Inception_resnet_v2训练自己的数据集并用Tensorboard监控
[写在前面] 用Tensorflow(TF)已实现好的卷积神经网络(CNN)模型来训练自己的数据集,验证目前较成熟模型在不同数据集上的准确度,如Inception_V3, VGG16,Inceptio ...
- Mysql锁机制--写锁
Mysql 系列文章主页 =============== 1 准备数据 1.1 建表 1.1.1 建立 Employee表 DROP TABLE IF EXISTS employee; CREATE ...
- struts2中action的class属性值意义
整合了spring就不同了,orz struts2单独使用时action由struts2自己负责创建:与spring集成时,action实例由spring负责创建(依赖注入).这导致在两种情况下str ...
- SVN错误:SVN Working copy XXX is too old
出错原因: 这是因为使用了低版本的svn生成了.svn内文件内容,但是,使用高版本svn同步时便出现该问题. 解决方法: 找到报错对应的文件夹,里面有个名为.svn的文件夹,删除这个文件夹(这是svn ...
- Servlet生命周期与工作原理(转载)
Servlet生命周期分为三个阶段: 1,初始化阶段 调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...
- 利用 Win32 启动和检测 UWP App 的方法
一种启动和检测 UWP 应用的方法 背景 我们发布过多款 UWP 平台的同类型 App ,最近有一个需求:用传统 Win32 程序启动我们的 UWP 程序.因为我们的每一个UWP App在客户机器上都 ...
- JSP运行过程 JSP脚本 静态动态包含 jsp指令 jsp内置对象jsp四大作用域 jsp动作元素 EL表达式 JSTL 设计模式 JSP开发模式 EL内置对象
Day38 JSP JSP的运行过程具体如下: (1)客户端发出请求,请求访问JSP文件. (2)JSP容器先将JSP文件转换成一个Java源文件(Java Servlet源程序),在转换过程中,如果 ...
- pip: unsupported locale setting
在终端里输入 $ export LC_ALL=C 可解决 http://stackoverflow.com/questions/36394101/pip-install-locale-error-un ...
- SQL SERVER Management Studio
1. 实验目的 熟悉SQL SERVER Management Studio的部分操作 数据SQL SERVER简化版和完整版数据库设计 2. 实验内容 2.1. 熟悉简化版SQL ...