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 stubString 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 MyClassLoadercan 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{@Overridepublic 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(){@Overridepublic 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 stubSystem.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 stubSystem.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 blocke.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 blocke.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 blocke.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 stubInputStream 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 stubObject 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学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- Java高新技术第一篇:类加载器详解
首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...
- 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...
- java classloader详解_Java类加载器(ClassLoader)详解
本文主要讲述Java ClassLoader的工作原理,这为后面将Android App代码热替换或者插件化升级做铺垫 一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class ...
- jvm之类加载双亲委托机制
岁岁金河复玉关,不辞镜里望崔山.--唐代杜甫<春望> 双亲委托机制 双亲委托机制是指在类加载器加载类时,如果一个类加载器收到了加载请求,它首先将请求委托给父类加载器,如果父类加载器仍无法找 ...
- java类加载器 架构 设计_类加载器(DexClassLoader)与插件化(动态加载)
类加载器与插件化解析 2.1 类装载器 DexClassLoader 首先,我们需要了解关于java代码本地import的一些知识: import中所引用的类有两个特点: 1.必须存在于本地,当程序运 ...
- Java核心类库之(类加载器、反射机制、模块化)
目录 1 类加载器 1.1 类加载 1.2 类加载器 2 反射 2.1 反射概述 2.2 反射获取Class类的对象 2.3 反射获取构造方法并使用 2.4 反射获取成员变量并使用 2.5 反射获取成 ...
- java log4j 热部署_JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)
首先纠正一个误区: 热部署不是我们在Eclipse里面修改了代码不用重启就可以持续调试. 详情可以参考上一篇博文: 热部署和"Hot Code Replace"的区别 htt ...
- 对于纯Java项目,JVM 各个类加载器的加载目标是什么?
Bootstrap Classloader: 加载 JVM 自带的 jar 或 zip 文件,JRE\lib 目录下的 jar 包,但不包括 JRE\lib\ext 内的 jar 包,用System. ...
- java类加载器可以从互联网加载,Java类加载器深入解析(2)
在做Java开发时了解Java类加载机制是非常好的.而对类加载机制的基本理解对Java开发人员处理类加载器(ClassLoader)相关的异常也很有帮助. 类加载器委托机制 Java类的装载是通过类加 ...
最新文章
- 使用hyperopt(Bayesian optimization)为lightGBM模型挑选最优参数进行模型构建、by Cross Validation
- 北电PBX资料_LD 11數位電話機設定
- 页面可视化搭建工具前生今世
- 【JavaScript】parseInt
- Delphi的Socket编程要分几步?
- ACM 常用算法合集
- Postgresql 直接在查询结果中生成唯一ID
- Incption 介绍 mysql_Inception使用详解
- putty连上l虚拟机中的linux要点
- 破圈了!完美日记凭什么让周迅成为全球品牌代言人
- 透视特洛伊木马程序开发技术
- 【NISP一级】6.3 社会工程学攻击
- 【软件设计师】2022年上半年软件设计师 下午试题六(原题及答案)
- win11连接共享打印机错误0x00000709
- 20190905层析分析法matlab,未通过一致性检验时,重新构造判断矩阵再计算
- 博弈论——4.1三人博弈的纳什均衡练习题
- UESTC 1607 大学生足球联赛 构造、蛇形安排赛程表
- Mysql中按某一字段去重,并查询去重后的所有字段
- 短信验证(手机号注册,绑定手机号获取验证码)
- 一些练习——武林秘籍
热门文章
- oracle数据泵功能,Oracle数据泵的使用(1)-Oracle
- 论文笔记_S2D.61_2019-CVPR-DeepLiDAR:基于稀疏激光雷达数据和单张彩色图像的户外场景的表面法线引导的深度预测
- 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示,,,
- WebService接口实际场景应用(一)
- 修改apache的网站根目录
- python之列表相关操作
- 在Linux的Terminal中显示文本文件特定行的内容
- Sql注入基础_mysql注入
- k-means算法概述
- VS 和 VAssistX 常用快捷键