(十九)Core Java 加载器及Spring底层AOP原理实现 (117)
目录 :
1 ) . 类加载器及其委托机制的深入分析2 ) . 自定义加载器的编写原理分析3 ) . 编写对class文件进行加密的工具类4 ) . 编写和测试自己编写的解密类加载器5 ) . 类加载器的一个高级问题的实验分析6 ) . 分析代理类的作用与原理和AOP概念7 ) . 创建动态类及查看其方法列表信息8 ) . 创建动态类的实例对象及调用其方法9 ). 完成InvocationHandle对象的内部功能10 ). 分析InvocationHandler对象的运行原理11 ). 总结分析动态代理类的设计原理与结构12 ). 编写可生成代理和插入通告的通用方法13 ). 实现类似spring的可配置的AOP框架
一. 类加载器及其委托机制的深入分析
1 ) . 明确 :
1.1 类加载器是什么?
[1] 类加载器本质也是java类,用来加载其他类,那类加载器是谁加载的呢? 他本身无需加载,嵌套在虚拟机上,底层是C++写的,名字叫 BootStrap
1.2 类加载器的作用?
[1]类加载器就是将用到的某个类的字节码文件从硬盘加载进内存,以可视化的方式展现的过程
1.3 java虚拟机中的类加载器了解?
[1] Java虚拟机中可以安装多个类加载器[2] 系统默认三个主要类加载器,每个类负责加载特定位置: BootStrap , ExtClassLoader,AppClassLoade
2 ) . Demo :package cn.ittext.day02;/*** @author winter* @Date 2018年3月29日下午9:24:11* @version 1.0* @describe 类功能描述*/public class ClassLoaderTest {public static void main(String[] args){/**** 庖丁解牛 :** 1. 三种不同的加载器** [1] Bootstrap** [2] ExtClassLoader** [3] AppClassLoader*** 优先级是 [1] <-- [2] <-- [3]*** 2.如何自定义加载器** [1] 通过classLoader类,作为类加载器的子类从而实现自定义加载器** 3.类加载器的委托机制** [1] 无论是哪个类加载器加载,最先去判断是否可加载的加载器都是 Bootstrap,他加载不了再向下尝试** 4.问题 : 当虚拟机要加载一个类时,到底先派出那个类加载器进行加载呢?** [1] 首先得有 总的加载器 ,因此 当前线程的类加载器去加载线程中的第一个类 null** [2] 然后通过委托机制依次从总的加载器向下传递的去类加载其指定的目录进行加载直到找到可加载的类 : Bootstrap --> ExtClassLoader-->AppClassLoader*** ps :** (1) 若类A引用了类B,Java虚拟机则使用加载类A的加载器去加载类B** (2) 还可通过ClassLoader.loadClass()来直接指定某个加载器去加载某各类** (3) 我们还可通过自定义类加载对指定目录进行加载**/ClassLoader systemName = System.class.getClassLoader();// sop("System系统类的类加载器是什么:"+systemName); //空代表是 顶级的类加载器是 Bootstrap ,用来加载系统类String ownName = ClassLoaderTest.class.getClassLoader().getClass().getName();// sop("ClassLoaderTest自定义类的类加载器是什么:"+ownName); //用来加载ClassPath指定的所有jar和目录 -->AppClassLoader/*** 遍历出所有的类加载器**/ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); //获取其中一个类加载器while(classLoader!=null){String name = classLoader.getClass().getName(); //获取其类加载其的名字sop(name);classLoader =classLoader.getParent(); //不断的向上获取直到获取到顶级加载器也就是Null后停止}sop(classLoader); //输出顶级加载器}public static void sop(Object obj){System.out.println(obj);}}3 ) . 类加载器图解 :
小结 :1. 了解类加载器的委托机制,明白加载器的优先级2. 记得每个不同的加载器所加载的目录文件3. 我们还可通过自定义加载器去加载我们指定的目录,这样其他人没有我们的加载器是不能加载我们的代码的
二. 自定义加载器的编写原理分析
1 ) .2 ) . 自定义加载器的须知 :
2.1 首先自定义加载器先继承ClassLoader2.3 其次覆盖findClass方法 -->用来获取字符式的字节码 : 若 需要更改委托机制的优先级, 则也可覆盖掉 loadClass方法2.4 最后编写defineClass方法 --> 用来将字符的字节码转化为类的实例
3 ) . 自定义加载器步骤:
3.1 编写一个对文件内容进行简单加密的程序3.2 编写了一个自己的类装载器,可实现对加密过的类进行装载和解密3.3 编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了使用classLoader.load方法之外,还可使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName
4 ) . 实验步骤 :
4.1 对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如 : java MyClassLoader MyTest class F:\itcast4.2 运行加载类的程序,结果能够被正常加载,但打印出来的类加载器名称为AppClassLoader: java MyClassLoader MyTest F:\itcast4.3 用加密后的类文件替换ClassPath环境下的类文件,再执行上一步操作就出现问题了,错误说明是AppClassLoader类装载器装载失败4.4 删除Class文件,重新进行生成
小结 :1. 模板方法设计模型就是 提供通用框架, 而后填充实现具体细节的过程就是模板设计方式
三. 编写对class文件进行加密的工具类
1 ) . Demo :package cn.ittext.day02;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/*** @author winter* @Date 2018年3月30日上午10:13:20* @version 1.0* @describe 自定义类加载器并实现加密*/public class MyClassLoader {public static void main(String[] args) throws IOException{/**** 庖丁解牛:** [1] 加密解密算法测试 --> 通过异或的方式进行加密,再次异或即可解密*** [2] 加密工具类的封装*///加密/* FileInputStream fis =new FileInputStream("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\day02\\Person.java");FileOutputStream fos =new FileOutputStream("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\encryptClass.file");encryptClass(fis,fos);*///解密FileInputStream fis =new FileInputStream("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\encryptClass.class");FileOutputStream fos =new FileOutputStream("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\DecodeClass.java");encryptClass(fis,fos);new entryClassUtils(fis,fos); //加密工具类的方式}public static void encryptClass(InputStream ips , OutputStream ops) throws IOException{int temp=0;while((temp=ips.read())!=-1){ops.write(temp^0xff);}}public static void sop(Object obj){System.out.println(obj);}}2 ) . 加密工具类:package cn.ittext.day02;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/*** @author winter* @Date 2018年3月30日上午11:29:22* @version 1.0* @describe 类功能描述*/public class entryClassUtils {private InputStream ips;private OutputStream ops;public entryClassUtils(InputStream ips, OutputStream ops) throws IOException{super();this.ips = ips;this.ops = ops;encryptClass(ips,ops);}public static void encryptClass(InputStream ips , OutputStream ops) throws IOException{int temp=0;while((temp=ips.read())!=-1){ops.write(temp^0xff);}}public static void sop(Object obj){System.out.println(obj);}}小结 :1. 有包名的类不可调用无包名的类
四. 编写和测试自己编写的解密类加载器 -->未能实现,以下代码不可用
1 ) . 类加密文件 :package cn.ittext.day02;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/*** @author winter* @Date 2018年3月30日上午11:29:22* @version 1.0* @describe 类加密*/public class entryClassUtils {private InputStream ips;private OutputStream ops;public entryClassUtils(InputStream ips, OutputStream ops) throws IOException{super();this.ips = ips;this.ops = ops;encryptClass(ips,ops);}public static void encryptClass(InputStream ips , OutputStream ops) throws IOException{int temp=0;while((temp=ips.read())!=-1){ops.write(temp^0xff);}}public static void sop(Object obj){System.out.println(obj);}}2 ) . Person类文件package cn.ittext.encryptClass;import java.util.Date;/*** @author winter* @Date 2018年3月29日下午5:12:32* @version 1.0* @describe 类功能描述*/public class Person extends Date{private Integer id;private String name;private Integer age;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Person(Integer id, String name, Integer age) {super();this.id = id;this.name = name;this.age = age;}@Overridepublic String toString() {return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";}}3 ) . 加载器文件package cn.ittext.day02;import java.io.ByteArrayOutputStream;import java.io.FileInputStream;import java.io.IOException;/*** @author winter* @Date 2018年3月30日下午2:04:31* @version 1.0* @describe 自定义类加载器*/public class decodeMyClassLoader extends ClassLoader{private String classDir; //传入的目录地址decodeMyClassLoader(){}decodeMyClassLoader(String classDir){this.classDir=classDir;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException{String fileName = getFileName(name); //获取文件地址try {byte[] bytes = getClassBytes(fileName); // 通过文件地址获取其中的字节码return defineClass(bytes, 0, bytes.length); //通过自定义加载器将字节码数据返回} catch (IOException e) {e.printStackTrace();}return super.findClass(name); //通过父类的加载器将字节码数据返回}private byte[] getClassBytes(String fileName) throws IOException{FileInputStream fis =new FileInputStream(fileName); //源ByteArrayOutputStream baos =new ByteArrayOutputStream(); //目标new entryClassUtils(fis, baos); //解密fis.close();byte[] byteArray = baos.toByteArray(); //换算成数组的形式return byteArray;}//获取文件private String getFileName(String name) {String pathClass = classDir+"\\"+name+".class"; //目的+文件名 = 文件地址return pathClass; //返回文件全地址}public static void sop(Object obj){System.out.println(obj);}}4 ) . 测试文件package cn.ittext.day02;import java.io.FileNotFoundException;import java.io.IOException;import java.util.Date;/*** @author winter* @Date 2018年3月30日下午2:37:19* @version 1.0* @describe 这是一个测试类*/public class entryDemo {public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{/* File source =new File("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\Person.java");File target =new File("S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\itcast\\Person.class");new entryClassUtils(new FileInputStream(source), new FileOutputStream(target));*/Class loadClass = new decodeMyClassLoader("itcast").loadClass("Person"); //传入自定义目录和加载的对象Date newInstance = (Date)loadClass.newInstance(); //实例化sop(newInstance);}public static void sop(Object obj){System.out.println(obj);}}小结 :1. 通过将自定义加载器挂到加载器的体系中,从而实现特定加载器去加载特定目录下的文件
五. 类加载器的一个高级问题的实验分析
3 ) . 小知识:
3.1 Servlet是被Tomcat提供的类加载器加载的3.2 WEB项目与Java项目中class的区别在于 , web中的class是可被 servlet 加载的class, java的class 是被虚拟机加载的class
4 ) . 结论 :
4.1 Servlet有着自己的加载器, 是 apache 旗下的,当出现500等页面找不到的问题时,也有可能是 加载器的问题
小结 :1. Tomcat是非常多的类加载器的集合,Tomcat是服务器
六. 分析代理类的作用与原理和AOP概念
1 ) . 代理 :
1.1 生活中的代理 :
[1] A客户去 B商户 购买 一件商品, 来回用时 2小时 , 商品1950+ 油费50 共计 2000元[2] A客户去 B商户 购买一件商品,全权交由代理商送货上门 , 商品1950元 + 服务费 50元 共计 2000元[3] 比较 : 代理购买方案 显然 比 传统购买方案要 省时, 时间如同金钱
1.2 程序中的代理 : -->在程序扩展方面的应用
[1] 直接方式 : A客户端 直接 调用 B 服务端 代码 进行 服务[2] 代理方式 :
(1) A客户端 直接调用 B服务端的代理类 : 创建一个代理 类 实现 B服务端的 接口 ,从而 将B服务端的 原始方法 实现 ,而后对代理类 进行 方法完善 即可(2) 采用工厂模式和配置文件的方式 进行管理 ,则无需修改客户端程序,直接在配置文件中修改是使用目标类还是代理类(3) 优势 : 灵活性,已修改,可扩展
2 ) . AOP (面向切面编程) -- > 面向代理编程 :
2.1 系统中存在交叉业务,交叉业务就是要切入到系统中的一个方面,如图所示 :
2.2 面向切面编程就是 将交叉业务 模块化,独立出来,提供公共访问的方式2.3 如何实现切面? 就是通过代理技术的方式, 因此 代理是实现AOP功能的核心关键技术
3 ) . 什么是动态代理类?
3.1 JVM可以再运行期间动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类3.2 JVM生成的动态类必须实现一个或多个接口,因此JVM生成的动态类只能用作具有相同接口的目标类的代理3.3 若一个没有实现接口的类要生成动态代理类,则通过CGLIB库动态生成一个类的子类
4 ) . 代理类各个方法中除了实现目标类的相应方法和返回相应结果之外,还可在代理方法中的如下四个位置加上系统完善功能代码:
4.1 在调用目标方法之前4.2 在调用目标方法之后4.3 在调用目标方法前后4.4 在处理目标方法异常的catch块中
小结 :1. 代理就是代替自己理解并完成任务的一种方式,优势是节省了自身时间2. 交叉业务就是 每个模块都需要的通用业务 ,将其 分离出来,以公地的形式出现,而后 贯穿到每个模块中3. 如果我们需要使用的那个类没有接口, 无法进行方法的实现, 那么就是用CGLib进行动态的创建子类
七. 创建动态类及查看其方法列表信息
1 ) . 核心内容 : 通过 JVM生成动态类 并获取其 动态类的所有 构造函数和所有方法2 ) . Demo :package cn.ittext.day03;import java.lang.reflect.Constructor;import java.lang.reflect.Executable;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Collection;/*** @author winter* @Date 2018年3月31日下午1:46:58* @version 1.0* @describe 创建collection的动态类并获取动态类中的构造函数和所有方法*/public class ProxyTest {public static void main(String[] args){/*** 庖丁解牛 :** [1] 如何 获取 collection的动态类** [2] 如何获取动态类中的构造函数 和参数列表** [3] 如何获取动态类中的所有方法 和参数列表** ps : 以上的动态类指代理类**///通过Proxy类调用获取代理类的方法getProxyClass ,其中传入 要获取的 类的 加载器 和 类的 字节码文件 ,之后返回 其代理类的字节码文件Class proxyClazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//[1]getConstructors(proxyClazz); //获取其动态类中的所有构造函数 和参数列表 //[2]getMethods(proxyClazz); //获取其动态类中的所有方法 和参数列表 //[3]}private static void getMethods(Class proxyClazz){sop("--------------------------beagin method list--------------------------");StringBuilder sbMethod =new StringBuilder();//获取collection的代理类的所有方法Method[] methods = proxyClazz.getMethods();for(Method method:methods){String conname = method.getName();sbMethod.append(conname);sbMethod.append("(");getParameter(sbMethod, method); //获取其 方法中的参数列表sbMethod.append(")");sbMethod.append("\r\n");}sop(sbMethod.toString());//输出 StringBuilder中方法的内容}private static void getConstructors(Class proxyClazz){//获取collection的代理类的所有构造函数Constructor[] constructors = proxyClazz.getConstructors();StringBuilder sbConstru =new StringBuilder();sop("--------------------------beagin constructor list--------------------------");//迭代代理类中的构造函数for(Constructor constructor:constructors){String conname = constructor.getName();sbConstru.append(conname);sbConstru.append("(");getParameter(sbConstru, constructor); //获取其 构造函数中的参数列表sbConstru.append(")");sop(sbConstru.toString()); //输出 StringBuilder中构造函数的内容}}//专门用来获取构造函数/方法的参数名 并将 参数名放入 StringBuilder的方法 -->这是公用方法private static <T> void getParameter(StringBuilder sb, T method) {Class[] classparameters = ((Executable) method).getParameterTypes();for(Class parameterclazze :classparameters){String parametername = parameterclazze.getName();sb.append(parametername+",");}if(classparameters !=null & classparameters.length !=0)sb.deleteCharAt(sb.length()-1);}public static void sop(Object obj){System.out.println(obj);}}小结 :1. 在单线程的情况下,进行动态字符串拼接用StringBuilder ,在多线程的情况下,进行动态字符串拼接用StringBuffer
八. 创建动态类的实例对象及调用其方法
1 ) . 实例化动态类 并 调用 动态类中的方法 用以实验2 ) . Demo:
package cn.ittext.day03;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;/*** @author winter* @Date 2018年3月31日下午3:10:03* @version 1.0* @describe 创建collection的动态类,并使用动态类进行相关操作*/public class ProxyTest01 {public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{//实现InvocationHandler接口的类,用来作为实例化代理类的对象参数class MyInvocationHandler implements InvocationHandler{@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}}//获取collection动态类的字节码文件@SuppressWarnings("rawtypes")Class proxyClazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//通过字节码文件获取其构造函数@SuppressWarnings({ "unchecked", "rawtypes" })Constructor constructorClazz = proxyClazz.getConstructor(InvocationHandler.class);//通过构造函数进行实例化,但构造函数有参数,需要将InvocationHandler接口作为参数传入,因此我采用了实现此接口,将其实现类传入@SuppressWarnings({ "unchecked", "rawtypes" })Collection<String> collProxy = (Collection) constructorClazz.newInstance(new MyInvocationHandler());//通过实例化ArrayList构造函数collProxy= new ArrayList();//使用collection的代理类调用方法collProxy.add("A");collProxy.add("B");collProxy.add("C");//迭代集合中的元素for(Iterator<String> it = collProxy.iterator();it.hasNext();){sop(it.next());}/***** 庖丁解牛 :** [1] 获取代理类的字节码** [2] 获取代理类的构造函数的字节码** [3] 通过构造函数字节码实例化代理类对象的方式 -->这里需要传入对象参数InvocationHandler** [4] 通过将实例化对象接口的可实例化子类进行关联,从而进行增加操作并迭代*** 小结:** [1] 我们可发现动态类中有其目标类中的所有方法,可直接拿来使用** [2] 想要实例化代理类,需要将构造函数中传入 InvocationHandler 接口的实现类才行****/}public static void sop(Object obj){System.out.println(obj);}}小结 :1. 反射获取方法的时候出入的是方法的参数类型,当实例化方法时传入的是 类型的具体值2. 当实例化一个对象将其变量打印输出返回null时, 只能说明其 对象的 toString ()方法返回的null,因为若对象为NUll则会直接报告空指针异常
九. 完成InvocationHandle对象的内部功能
1 ) . 代理类的三种实现方式 :
package cn.ittext.day03;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;/*** @author winter* @Date 2018年3月31日下午4:13:39* @tel [email protected]* @version 1.0* @describe 实现代理类的三种方式*/public class ProxyTest03 {public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{/***** 庖丁解牛 : 代理类的三种实现方式** [1] 自定义InvocationHandler的实现类的方式** [2] InvocationHandler的匿名内部类的方式** [3] 直接使用Proxy调用其方法newProxyInstance(iterfaceClassLoader,iterface,InvocationHandler) ---> 要用就用第三种****///方式一: 自定义InvocationHandler的实现类的方式 ----------------------------------------------------------------------------/* class MyInvocationHandler implements InvocationHandler{ArrayList arr =new ArrayList(); //一个实例化类型 用来让 代理类指定@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{//实例化前的代理类只是个接口,只有实例化后才是一个实力类,才能被应用Object invoke = method.invoke(arr, args); //通过一个实体类调用 代理类,也就是 为是实例化代理类return invoke; //返回实例化后的代理类}}//获取collection动态类的字节码文件Class proxyClazz1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//通过字节码文件获取其构造函数Constructor constructorClazz = proxyClazz1.getConstructor(InvocationHandler.class);//通过构造函数进行实例化,Collection<String> collProxy = (Collection) constructorClazz.newInstance(new MyInvocationHandler());collProxy.add("A");collProxy.add("B");collProxy.add("C");//迭代集合中的元素for(Iterator<String> it = collProxy.iterator();it.hasNext();){sop(it.next());} *///方式二:InvocationHandler的匿名内部类的方式 ----------------------------------------------------------------------------/*//获取collection动态类的字节码文件Class proxyClazz2 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//通过字节码文件获取其构造函数Constructor constructorClazz = proxyClazz2.getConstructor(InvocationHandler.class);//通过构造函数进行实例化,@SuppressWarnings("unchecked")Collection<String> collProxy = (Collection) constructorClazz.newInstance(new InvocationHandler(){ArrayList arr =new ArrayList();@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke = method.invoke(arr, args);return invoke;}});collProxy.add("A");collProxy.add("B");collProxy.add("C");//迭代集合中的元素for(Iterator<String> it = collProxy.iterator();it.hasNext();){sop(it.next());}*///方式三:直接使用Proxy调用其方法newProxyInstance()的方式 +InvocationHandler的匿名内部类的方式 ----------------------------------------------------------------------------//获取collection的代理类ArrayList的方式/* @SuppressWarnings("unchecked")Collection<String> collProxy3 =(Collection<String>) Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[] {Collection.class},new InvocationHandler(){ArrayList<String> arr =new ArrayList<String>();@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{Object invoke = method.invoke(arr, args);return invoke;}});collProxy3.add("A");collProxy3.add("B");collProxy3.add("C");//迭代集合中的元素for(Iterator<String> it = collProxy3.iterator();it.hasNext();){sop(it.next());}*/}public static void sop(Object obj) {System.out.println(obj);}}
小结 :1. 如若某个方法需要将接口作为参数传入的话,有两种方式 一将自定义的一个实现了此接口的实现类传入 , 二 将 此接口以匿名内部类的方式传入2. 通过proxy代理类的方法 newProxyInstance() 直接实例化一个 相关 接口的 代理类 ,并传入 InvocationHandler 对其进行管理
十. 分析InvocationHandler对象的运行原理
1 ) . InvocationHandler 对象 其中的 invoke 如同代理的切面 , 可直切 其代理对象的 方法内部或外部2 ) . Demo:package cn.ittext.day03;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;/*** @author winter* @Date 2018年3月31日下午6:10:18* @version 1.0* @describe 剖析实现代理类的第三种方式*/public class ProxyTest04 {public static void main(String[] args) {/**** 庖丁解牛: 剖析实现代理类的第三种方式 : proxy类中的 newProxyInstance()方法** invoke(Object proxy, Method method, Object[] args)** [1] proxy -->调用哪个对象** [2] method -->调用对象的哪个方法** [3] args -->传递的哪个参数*** 委托给InvocationHandler的方法有 : equals , hashCode,toString*** 小结 :** [1] 我们可在invoke () 方法中 对 add() 方法的前后添加完善代码** [2] 我们还可在invoke()方法中 对 add()方法的参数值 进行过滤等操作** [3] 总之我们可在 invoke ()方法中对 代理类所调用的任何方法进行完善改进等操作**/// 通过 InvocationHandler作为代理类ArrayList作为目标类的方式@SuppressWarnings("unchecked")Collection<String> collProxy3 =(Collection<String>) Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[] {Collection.class},new InvocationHandler() //代理类{ArrayList<String> target =new ArrayList<String>(); //目标类@Override //这里的method 就是 下边的addpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{sop("开始计时:..................");long startTime =System.currentTimeMillis();Object invoke = method.invoke(target, args); //将add()方法的运行指定到 目标类,然后运行long endTime =System.currentTimeMillis();sop("结束计时:"+(endTime-startTime));return invoke;}});collProxy3.add("A"); //在这里每add一次,上边的 invoke 就运行一次 ,这些值添加到目标类中collProxy3.add("B");collProxy3.add("C");//迭代集合中的元素for(Iterator<String> it = collProxy3.iterator();it.hasNext();){sop(it.next());}}public static void sop(Object obj) {System.out.println(obj);}}
小结 :1. 无论是代理类调用哪个方法都会走 匿名内部类InvocationHandler中的invoke()方法 ,从而指定 调用方法的 目标类是哪一个 ,因此我们可明白 invoke () 就是 对 接口对象的代理类进行操作的 一把 切面刀
十一. 总结分析动态代理类的设计原理与结构
1 ) .Demo :见下一章实现 动态代理2 ) . 描述 :
2.1 流程 : 客户端调用接口方法 --> 接口方法 转交代理类 InvocationHandler去调取--> 代理类通过invoke() 方法 将调取的方法与指定目标类 相关联,通过目标类运行并返回结果 , 并且可在 invoke 方法中 对 原有方法进行改善 --> >代理类 InvocationHandler将返回值 返回到 目标类中
2.2 关系 解析 :
[1] 接口中植入了 InvocationHandler,因此 InvocationHandler可随意访问 接口中的方法[2] 目标类也是接口 的 子类,因此 自然也具有 接口的方法[3] 客户端调用 接口 , 接口 转交 代理类 InvocationHandler , 代理类 再次转交 目标类 即可 ,然后 将值 返回到 接口实例化对象中
3 ) . 动态代理图解 :小结 :1. javaScript和python支持动态语言,java不支持动态语言2. 将代码封装进对象,将对方放入invoke即可实现 , 在调用相关方法时 将灵活调用其它方法(log())3. 动态代理的核心 在invoke方法中 , 其参数值需传入两个对象 ,一个是 目标类对象 用来执行原有方法, 一个是系统类对象封装了需要进行切面的方法(也就是通用方法)
十二. 编写可生成代理和插入通告的通用方法 --> 以下有动态代理的方法
1 ) . Demo :package cn.ittext.day03;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;/*** @author winter* @Date 2018年3月31日下午10:37:16* @version 1.0* @describe 实现AOP原理*/public class ProxyTest05 {public static void main(String[] args){/*** 庖丁解牛 :** [1] 将代理的方法抽取出 通用型的代理方法** [2] 写出插入通告的通用方法并作出测试*/// primitive();final ArrayList<String> target =new ArrayList<String>(); // 目标类@SuppressWarnings("unchecked")Collection<String> colProxy = (Collection<String>) getProxy(target,new Adtives()); //将目标类 放入代理 类中 用来 实现 AOP的操作colProxy.add("A");colProxy.add("S");colProxy.add("D");for(Iterator<String> it =colProxy.iterator(); it.hasNext();){sop(it.next());}}//用来获取代理类的方法 : 这是 将原始代理的核心代码抽取过后的代码 -->以下使用了代码重构private static Object getProxy(final Object target ,final Adtive adtive) {Object colProxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), //动态获取 接口的 加载器target.getClass().getInterfaces(), //动态的活期 接口new InvocationHandler() { //代理类的管理者@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{/** 在这里边有先后顺序的是 beforeMethod 和 afterMethod方法, invoke 方法永远都是最后执行 的**/adtive.beforeMethod();Object invoke = method.invoke(target, args);adtive.afterMethod();return invoke;}});return colProxy;}//---------------------------------------------------------------------------------------------//原始的 collection 代理类 实现核心逻辑/* public static void primitive(){Collection colProxy =(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class},new InvocationHandler() {ArrayList target =new ArrayList();@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{Object invoke = method.invoke(target, args);return invoke;}});colProxy.add("A");colProxy.add("S");colProxy.add("D");for(Iterator it =colProxy.iterator(); it.hasNext();){sop(it.next());}}*/public static void sop(Object obj){System.out.println(obj);}}2 ) . Iterfacepackage cn.ittext.day03;/*** @author winter* @Date 2018年3月31日下午11:08:54* @version 1.0* @describe 切面接口*/public interface Adtive{void beforeMethod();void afterMethod();}3 ) . IterfaceImplpackage cn.ittext.day03;/*** @author winter* @Date 2018年3月31日下午11:08:39* @version 1.0* @describe 切面接口实现类*/public class Adtives implements Adtive{@Overridepublic void beforeMethod(){System.out.println("start..................");}@Overridepublic void afterMethod(){System.out.println("end..................");}}小结 :1. AOP最重要的部分就是 动态 代理的实现 ,然后 通过 对象的方式插入通告 提供 便捷性
2. 动态代理方法的角度最重要的两部分就是 :
[1] newProxyInstance ,通过这个方法 将 目标类接口加载器 ,目标类接口 和 InvocationHandler 代理类的管理者 关联到一起[2] InvocationHandler ,通过 这个 类的invoke () 调用目标方法 和 实现 目标方法前后 的控制
十三. 实现类似spring的可配置的AOP框架
1 ) . Demo:1.1 beanFactorypackage cn.ittext.day3.aopframework;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import cn.ittext.day3.Adtive;/*** @author winter* @Date 2018年4月1日下午1:45:25* @version 1.0* @describe Bean的实例化工厂*/public class BeanFactory {Properties properties = new Properties(); //创建一个资源文件引用,用来接收 输入流的数据public BeanFactory(InputStream ips){try {properties.load(ips); //将输入流的数据 放入 properties 文件中 ,因此 properties 是键值对 ,后期好获取值} catch (IOException e) {e.printStackTrace();}}public Object getBean(String name){Object bean =null;try {String className = properties.getProperty(name); //获取 资源配置文件中传入的 键 对应的值System.out.println("start...................."+className);Class<?> clazz = Class.forName(className);System.out.println("end....................");bean = clazz.newInstance(); //通过字节码文件将其实例化} catch (Exception e) {e.printStackTrace();}/****** 若判断 是代理类 ProxyFactoryBean 则 通过代理类去实现** 若判断 不是代理类 ,则 直接返回 bean即可*****/if(bean instanceof ProxyFactoryBean) ///判断 bean 是否 是代理类实例化的{ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; //将 bean实力类 赋给 代理类Object proxy =null;try {//获取到资源配置文件中 的 adtive 切面 的 值 并 实例化Adtive adtive = (Adtive) Class.forName((String) properties.get(name+".adtive")).newInstance();//获取到资源配置文件中的 target目标类的值 并实例化Object target = Class.forName((String) properties.get(name+".target")).newInstance();//将 切面与 目标类 传给 代理类proxyFactoryBean.setAdtive(adtive);proxyFactoryBean.setTarget(target);proxy = proxyFactoryBean.getProxy(); //调用 代理方法} catch (Exception e) {e.printStackTrace();}}return bean;}}1.2 ProxyFactorypackage cn.ittext.day3.aopframework;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import cn.ittext.day3.Adtive;/*** @author winter* @Date 2018年4月1日下午1:52:50* @version 1.0* @describe 代理类的实例化工厂*///代理类的实例化工厂public class ProxyFactoryBean {private Adtive adtive ;private Object target;public Adtive getAdtive() {return adtive;}public void setAdtive(Adtive adtive) {this.adtive = adtive;}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Object getProxy() {Object colProxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), //动态获取 接口的 加载器target.getClass().getInterfaces(), //动态的活期 接口new InvocationHandler() { //代理类的管理者@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{/** 在这里边有先后顺序的是 beforeMethod 和 afterMethod方法, invoke 方法永远都是最后执行 的**/adtive.beforeMethod();Object invoke = method.invoke(target, args);adtive.afterMethod();return invoke;}});return colProxy;}}1.3 切面接口 及 切面 实现类package cn.ittext.day3;/*** @author winter* @Date 2018年3月31日下午11:08:54* @version 1.0* @describe切面接口*/public interface Adtive{void beforeMethod();void afterMethod();}package cn.ittext.day3;/*** @author winter* @Date 2018年3月31日下午11:08:39* @version 1.0* @describe 切面实现类*/public class Adtives implements Adtive{@Overridepublic void beforeMethod(){System.out.println("start..................");}@Overridepublic void afterMethod(){System.out.println("end..................");}}1.4 config.properties#xxx = cn.ittext.day3.aopframework.ProxyFactoryBeanxxx = java.util.ArrayListxxx.adtive = cn.ittext.day3.Adtivesxxx.target = java.util.ArrayList## 之前配置文件中遇到的问题 : = 前后 需要用空格 隔开一下# 注释掉 第二行,说明传入ArrayList类作为 实例化bean# 注释掉第三行,说明传入代理类作为实例化bean##1.5 Testpackage cn.ittext.day3.aopframework;import java.io.InputStream;import java.util.ArrayList;import java.util.Iterator;/*** @author winter* @Date 2018年4月1日下午2:10:54* @version 1.0* @describe 类功能描述*/public class AopFrameworkTest {public static void main(String[] args) throws InstantiationException, IllegalAccessException{/*** 庖丁解牛:** 如何自制一个Spring框架 :** 1. 需要的类** [1] BeanFactory : 用来接收 获取 资源配置文件的内容 ,并 判断是代理类的方式 还是 普通类的方式而 做出相应的反应** [2] ProxyFactoryBean : 用来接收 代理类的 目标类和 切面 , 从而实现 代理类的实例化*****///通过自身类获取一个资源配置文件,将其赋给 输入流InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");//向bean工厂内传入资源配置文件,并获取 实例化bean方法Object bean =new BeanFactory(ips).getBean("xxx");sop(bean.getClass().getName());//获取到的实例化bean方法 , 来做个测试 ,我们知道 配置文件中配置的就是 arrayList ,因此 可 转化为 arraylistArrayList<String> arr = (ArrayList<String>) bean.getClass().newInstance();arr.add("A");arr.add("S");arr.add("D");for(Iterator<String> it =arr.iterator();it.hasNext();){sop(it.next());}}public static void sop(Object obj) {System.out.println(obj);}}2 ) . 关系图:
3 ) . 小结 :
3.1 AOP框架的核心 :[1] 代理类的实例化工厂[2] 普通类的实例化工厂[3] 测试 : 需要 传入 配置文件 ,获取其 bean的名字 即可小结 : 若是代理类则 通过 其自定义的 代理方式去实例化,若是普通类 则通过 正常方式实例化
小结 :1. Aop就是 面向切面编程.所谓的切面 就是 交叉方法, 也就是我们在用代理类实现的过程当中写 通用方法的地方2. IOC就是 控制反转,就是将 实例化对象的方式通过反射交由给了配置文件,以此 实现了 控制权的反转
十四. 总结
1 ) . 加载器就是 用来加载类的 一个容器 , 不同的加载器不同的地方在于规定的加载文件的位置的不同,每一个框架也都有属于自己的加载器 ,自定义的加载器还可实现 加密解密 从而 使得代码只能自己观看2 ) . spring底层所使用的 是 代理机制 + IO流 + 反射 + 加载器
所谓的动态代理 就是 通过反射的方式 将 获取的方法与目标类相连接 ,从而实现 代理 , 框架的底层用的都是反射 ,因为 要动态获取代理的最大优势就是 可在 代码的执行前后 过滤自己想过滤的内容IO流在Spring中用来获取 资源文件并转化成 可通过 键值对获取的 properties反射在Spring中用来动态的获取对象的字节码以此来实例化对象加载器在Spring中用来配合代理类的实现