15 Java的反射
反射的概念
先从人的正向思考分析,比如你看到一个物品,你马上就想到了这个物品的名字,就比如下面的例子:
反射就是正向思考的相反,给一个名字,然后你想象,这个名字的具体信息,如下
把反射概念引入Java,就比如下面的例子:
通过类名去寻找该类的详细信息,这个过程称之为”反射“。
反射
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制提供的功能:
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理
反射机制的研究及应用
Class类
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头, 实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
1. Class本身也是一个类
2. Class 对象只能由系统建立对象
3. 一个类在 JVM 中只会有一个Class实例
4. 一个Class对象对应的是一个加载到JVM中的一个.class文件
5. 每个类的实例都会记得自己是由哪个 Class 实例所生成
6. 通过Class可以完整地得到一个类中的完整结构
Java内存分析
类加载过程
补充类的生命周期:加载->链接->初始化->使用->卸载。
类加载与ClassLoader的理解
类初始化时机
类加载器的作用
类加载器分类
加载器例子:
/** * 类加载器 * @author leak * */ public class Test9 { public static void main(String[] args) { //获取系统类的加载器, ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //获取系统类加载器的父类加载器-》扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //获取扩展类加载器的父类加载器-》根加载器(c/c++写的,获取不到) //所以类加载器层次关系:系统类加载器->扩展类加载器->根加载器 ClassLoader parent1 = parent.getParent(); System.out.println(parent1);//返回null,因为根加载器获取不到 //测试当前类是哪个加载器加载的 try { //自定义的类是哪个类加载器加载 ClassLoader classLoader = Class.forName("org.reflaction.day14.Test9").getClassLoader(); System.out.println(classLoader); //jdk自带的类是哪个类加载器加载 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader);//返回null,因为根加载器获取不到 //如果获取到系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path")); //扩展:双亲委派机制 //java.lang.String比如你写了一个String类,但是这个机制会让你这个类跑不起来,该机制检测多层加载器,保证安全性,以最顶级加载器为基础 //如果你写了一个类和其他加载器的类重名,那么按照优先顺序,使用该类,保证底层核心类不被破坏 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
总结:类加载器分为三类,从大到小排序:系统类加载器->扩展类加载器->根加载器(引导类加载器)。这三个加载器都是继承关系,系统类加载器一般是加载我们自己写的类,扩展类加载器是加载一些额外需求的类,根加载器是加载项目基本的类(比如,String,Object,Integer等)。
额外:双亲委派机制,该机制作用保证类加载器的安全,比如你写了一个同名String类,但是最顶级跟加载器java.lang.String类已经存在,为了保证核心类不被破坏,导致崩溃,所以自己写的String类根本不会别加载。
Class类的常用方法
实例化Class类对象
例子:
//Person类 public class Person { public String name; int age; @Override public String toString() { // TODO Auto-generated method stub return "已经获取到了该类的实例对象"; } } //测试类 /** 反射 * 四种方法创建对应类的Class实例 * @author leak */ public class Test { public static void main(String[] args) { Person p = new Person(); // c对象包含了对象p所属的Person类的所有信息 // 1. getClass()方法通过 实例对象p获取实例对象 的类,创建该类的实例对象返回 Class c = p.getClass(); System.out.println("1 :"+c); // 2. 通过类名.class创建指定类的Class实例 Class c1 = Person.class; System.out.println("2 :"+c1); // 3通过Class.forName("全类名") 全类名:包名+类名,不过需要捕捉异常,可能找不到该类 try { // 通过Class的静态方法forName()来获取一个类的class实例 // 方法3是最常用获取类的实例对象的方法 Class c2 = Class.forName("org.chen.day14.Person"); System.out.println("3 :"+c2); } catch (Exception e) { e.printStackTrace(); } // 调用4方法实现获取Class对应的实例对象 Test t = new Test(); //输出获取到的实例对象 System.out.println("4 :"+t.classLoaderGetClass()); } public Class classLoaderGetClass() { // 4通过类加载器,获取对应类的Class实例 //根据当前类,然后获取当前类的加载器,然后根据类加载器获取需要的Class的实例对象 ClassLoader c3 = this.getClass().getClassLoader(); Class c4 = null; try { c4 = c3.loadClass("org.chen.day14.Person"); } catch (Exception e) { e.printStackTrace(); } return c4; } }
最常用的方法就是forName方法,只需要一个全类名就可以获取对应类的实例对象,其他方法都比较麻烦。
Class类和Class类实例区别
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。
对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?
人 -> Person
Java类 -> Class
对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等;一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的;一个类在虚拟机中只有一份字节码;
获得Class对象
每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,3种方式:
1.使用Class类的forName(String className)静态方法,className表示全限定名;如String的全限定名:java.lang.String;
2.调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);
3.调用某个对象的getClass()方法。该方法属于Object类;Class<?> clz = new Date().getClass();
总结:Class实例对象是一个类,属于包含关系:Class类 <- Class类的实例对象(也是一个类,比如String类),平常的类(String,Object,Person,Date)是一个类,但是对于Class类,他就是一个Class类的实例对象。
例子:创建Class实例对象(相对Class是类)和平常类的对象,还有根据Class类的实例对象,调用指定的构造器,下面的例子是通过反射调用指定构造方法。
//Person类,接口,Student类上面的例子有 //测试类 import java.lang.reflect.Constructor; /** * * @author leak * Student类有toString方法,所以打印对象就知道是创建了Student对象,还是创建了Student类 */ public class Test2 { public static void main(String[] args) { // 使用反射的构造方法创建对象 try { Class student = Class.forName("org.chen.day14.Student"); System.out.println("这里的student是一个类,但是对于Class类,student是一个Class类的实例对象:"+student); System.out.println("----------------"); // newInstance相当于调用无参公有构造方法创建对象 Student students = (Student) student.newInstance(); System.out.println("这里才是创建Student对象:"+students); System.out.println("--------------"); //通过Class实例student获取指定构造器,这里调用了两个参数的构造器,形参要对应指定的构造方法的形参,接收.class类型参数 //注意:getDeclaredConstructor和getConstructor区别,一个是获取所有构造,一个是公有构造 Constructor sc = student.getDeclaredConstructor(String.class,int.class); //上面获取了私有的构造方法,所以要解除封装 sc.setAccessible(true);//解除封装 //解除封装才可以使用该构造创建Student对象。 Student stu = (Student) sc.newInstance("猪头",22); stu.school= "第一中学"; System.out.println(stu); } catch (Exception e) { e.printStackTrace(); } } }
补充:Class的实例对象和平常类(String,Date,Object)在使用层面是指同一个,但是从理论上,Class类的实例对象是一个对象,平常类(Class类的实例对象)是一个类。看不懂看上面的红字。
反射获取类的全部结构
Field属性、Method方法、Constructor构造器、Superclass父类、Interface接口、Annotation注解
1.实现的全部接口
2.所继承的父类
3.全部的构造器
4.全部的方法
5.全部的Field
6.获取注解
注意:获取上面的6种类型,只要获取的getXX方法带有Declared在里面,则获取Class实例的所有,但是不包含父类;
平常的getXX方法只获取public修饰的结构(上面6种),但是包含父类的public修饰的。如果被private修饰的属性/方法/类构造器,需要setAccessible(true)方法解除封装性,
setAccessible(true)除了可以解除封装性,还可以关闭反射的访问检查,如果要提高反射效率,就关闭反射的访问检查。
还有反射获取的属性/方法,进行赋值时,要指定为哪个对象进行赋值。
强调:区分好类的结构关系,比如类里面有属性,类有注解,属性也有注解,如果直接通过类去获取注解,只会获取到类的注解,不会获取到类里面属性的注解。
所以区分不清类结构之间的关系就直接获取,如果该结构(结构:上面6中类型)不存在,直接返回空指针异常。
使用反射可以取得:
实现的全部接口
public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。
所继承的父类
public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
全部的构造器
public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。
Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();
例子:
//Person类,接口,Student类代码,上一个例子有 //测试类 import java.lang.reflect.Constructor; /** * 反射获取该类的实例对象的父类/接口/构造器(方法)构造器的参数,类型 * @author leak */ public class Test1 { public static void main(String[] args) { try { //通过全类名,调用Class.forName方法获取指定类的实例对象 Class student = Class.forName("org.chen.day14.Student"); System.out.println("student类:"+student); //获取该实例继承的父类 Class superclass = student.getSuperclass(); System.out.println("student类的父类:"+superclass); System.out.println(); //获取该实例实现的接口 Class[] interfaces = student.getInterfaces(); for(Class interfac : interfaces) { //打印实现的所有接口 System.out.println("student类的接口:"+interfac); } System.out.println(); //获取该实例的构造器(public) //获取到类的公有构造方法 Constructor[] cs = student.getConstructors(); for(Constructor c : cs) { //getName获取构造方法名称 System.out.println("student类的公有public构造方法:"+c.getName()); //getModifers获取修饰符 //返回的数字1代表public,2代表是private System.out.println("student类的公有public构造方法:"+c.getName()+"的修饰符:"+c.getModifiers()); //获取每个构造器的参数类型 Class[] parameterTypes = c.getParameterTypes(); for(Class s : parameterTypes) { System.out.println("student类的公有public构造方法:"+c.getName()+", 参数类型:"+s.getName()); } } System.out.println(); //获取该实例的构造器(所有类型) //获取到类的所有构造方法 Constructor[] css = student.getDeclaredConstructors(); System.out.println("获取到类的所有类型的构造方法"); int i = 1,j = 1;//i是第几个构造方法,j是构造方法的第几个形参 for(Constructor c : css) { System.out.println("第"+i+"个构造方法----------------------"); //getModifers获取构造方法的修饰符 System.out.println("student类的构造方法:"+c.getName()+"的修饰符:"+c.getModifiers()); //获取构造器的参数类型 //有几个参数数组的 元素就有几个 // default是0 , public是1 ,private是 2 ,protected是 4,static是 8 ,final是 16。 Class[] parameterTypes = c.getParameterTypes(); for(Class s : parameterTypes) { System.out.println("student类的构造方法:"+c.getName()+"的第"+j+"个参数, 参数类型:"+s.getName()); j++; } i++; System.out.println("------------------------"); } System.out.println(); } catch (Exception e) { e.printStackTrace(); } } }
全部的方法
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法,不包含父类
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法,包含父类
Method类中:
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
例子
//Person类,接口从上上面的例子拿 //Student类,添加了private方法,区别getMethods和getDeclaresMethods的区别 // public class Student extends Person implements Move,Study{ String school; public Student() { System.out.println("创建Student对象,无参构造"); }//无参构造 public Student(String school) { this.school = school; System.out.println("创建Student对象,有参构造,1个参数"); }//有参构造 //私有构造 private Student(String name,int age) { this.name = name; this.age = age; System.out.println("创建Student对象,私有构造,2个参数"); } private void privateMethod() { System.out.println("私有方法"); } public void showInfo() { System.out.println("学校:"+school); } @Override public void studyInfo() { System.out.println("学习中文"); } @Override public void moveType() { System.out.println("走路"); } @Override public String toString() { // TODO Auto-generated method stub return "姓名:"+name+",年龄:"+age+",学校:"+school; } } //测试类 import java.lang.reflect.Method; public class Test3 { public static void main(String[] args) { try { //反射获取Class的实例对象(Student类) Class student = Class.forName("org.chen.day14.Student"); //获取所有公有public的方法,包括父类 // Method[] methods = student.getMethods(); //获取所有的方法,包括私有,但是不包含父类 Method[] methods = student.getDeclaredMethods(); for(Method meth : methods) { System.out.println("方法--------------------"); System.out.println("方法的名称:"+meth.getName()); System.out.println("方法的修饰符:"+ meth.getModifiers()); System.out.println("方法的返回值:"+meth.getReturnType()); //获取方法的参数类型,一个方法有几个形参(数组),就返回形参的类型,返回的是数组 //为什么使用Class[]数组接收呢,getParameterTypes()返回的方法形参类型,不是返回类型,而是返回参数类型的类,比如java.lang.String Class[] types = meth.getParameterTypes(); if(types != null && types.length >0) { for(Class c : types) { System.out.println("方法的形参类型:"+c); } } System.out.println("---------------------"); System.out.println(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
全部的属性
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。
Field方法中:
public int getModifiers() 以整数形式返回此Field的修饰符
public Class<?> getType() 得到Field的属性类型
public String getName() 返回Field的名称。
补充:获取Class实例对象的包,类.getPackage()
例子
//Student类,Person类,接口从上面的例子拿 //测试类 import java.lang.reflect.Field; /** * 通过反射返回Class实例的属性,包名,包对象 * @author leak * */ public class Test4 { public static void main(String[] args) { try { Class student = Class.forName("org.chen.day14.Student"); //获取student类的所有公有public属性,包含父类 // Field[] fields = student.getFields(); //获取student类的所有属性,包含私有,不包含父类 Field[] fields = student.getDeclaredFields(); for(Field f : fields) { System.out.println("------------------------"); System.out.println("属性的名字:"+f.getName()); System.out.println("属性的类型:"+f.getType()); System.out.println("属性的修饰符:"+f.getModifiers()); System.out.println("------------------------"); } //获取包对象 Package package1 = student.getPackage(); System.out.println(package1); //获取包名 System.out.println(student.getPackageName()); }catch(Exception e) { e.printStackTrace(); } } }
调用指定方法
1.调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
Object invoke(Object obj, Object … args)方法
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null,这里的Object对象是调用方法的对象
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法解除封装,将可访问private的方法。
//Person类,接口上面例子拿 //Student类 public class Student extends Person implements Move,Study{ public String school; private String privateField; public Student() { System.out.println("创建Student对象,无参构造"); }//无参构造 public Student(String school) { this.school = school; System.out.println("创建Student对象,有参构造,1个参数"); }//有参构造 //私有构造 private Student(String name,int age) { this.name = name; this.age = age; System.out.println("创建Student对象,私有构造,2个参数"); } private void privateMethod() { System.out.println("私有方法"); } public void showInfo() { System.out.println("学校:"+school); } public String getSchool() { return school; } @Override public void studyInfo() { System.out.println("学习中文"); } @Override public void moveType() { System.out.println("走路"); } private void test(String name) { System.out.println("这是私有方法private void test(String name)"); } public void setInfo(String name,String school) { this.name = name; this.school = school; System.out.println("这个是setInfo(String name,String school)方法"); } public void setInfo(int age) { this.age = age; System.out.println("这个是setInfo(int age)重载方法"); } @Override public String toString() { // TODO Auto-generated method stub return "姓名:"+name+",年龄:"+age+",学校:"+school; } } //测试类 import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * 反射调用指定方法 * @author leak * */ public class Test5 { public static void main(String[] args) { try { //创建Class实例对象 student (类) Class<Student> student = (Class<Student>) Class.forName("org.chen.day14.Student"); //getMethod(方法名,方法的形参类型(类)...)参数类型个数根据方法的形参决定 //getMethod只能获取公有public方法,包含父类 Method method = student.getMethod("setInfo",String.class,String.class); /** * 注意:下面不论是反射调用setInfo还是test方法 * 都是使用student1实例对象来调用的 */ //通过反射创建对象,调用方法 Constructor con = student.getConstructor(); Student student1 =(Student) con.newInstance(); //获取到方法后,需要一个对象调用它,参数1是实例化对象,参数2调用当前的方法的实际参数 method.invoke(student1,"猪头","第一中学"); //如果想调用私有方法呢? //getDeclaredMethod调用本类的任意方法包含私有,不包含父类 Method method2 = student.getDeclaredMethod("test",String.class); //调用私有方法,需要解除封装 method2.setAccessible(true); method2.invoke(student1,"XXX"); //调用重载方法 Method method3 = student.getMethod("setInfo",int.class); method3.invoke(student1, 33); //调用有返回值的方法 Method method4 = student.getMethod("getSchool"); //调用方法后,接收方法的返回值 Object returnValue = (String)method4.invoke(student1); System.out.println(returnValue); }catch(Exception e) { e.printStackTrace(); } } }
调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问,解除封装。
public void setAccessible(true)访问私有属性时,让这个属性可见。
例子
//student类,接口,person类上面例子找 //测试类 import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** * 通过反射调用指定属性 * @author leak * */ public class Test6 { public static void main(String[] args) { try { //通过反射,创建Class实例对象(类) Class<?> student = Class.forName("org.chen.day14.Student"); //想调用指定属性,首先要创建Student实例对象去调用,因为每个对象的属性都不一致 //通常创建对象,是调用构造方法,这里先获取构造方法 Constructor<?> constructor = student.getConstructor(); //然后通过构造方法,创建student的实例对象 Student student1 =(Student) constructor.newInstance(); //获取指定属性,getField(属性名字),getField方法是获取公有public的属性,包含父类 //一个Field类就一个属性 Field field = student.getField("school"); Field field1 = student.getField("name"); //set(对象,形参) 对象:给哪个对象赋值,形参:赋值的参数 //给每个相同的对象,不同的属性,赋值 field.set(student1, "第二中学"); field1.set(student1, "猪头"); //Field类的实例对象的get()方法 获取当前field属性对象的值 String mess =(String) field1.get(student1);//获取field1对象的值打印 System.out.println(mess); //或者打印student1对象的school属性 检查field对象是否已经赋值 System.out.println(student1.school);//这里的输出仅限public的属性 //获取指定的属性,包含私有的,其他的,getDeclaredField("属性名字")方法是获取本类所有类型的属性,不包含父类 Field field2 = student.getDeclaredField("privateField"); field2.setAccessible(true);//解除封装 field2.set(student1, "私有的属性");//给哪个对象设置value String mess2 =(String) field2.get(student1);//获取field2对象的值打印 System.out.println(mess2); }catch(Exception e) { e.printStackTrace(); } } }
获取各种结构的注解
注解修饰符只有public类型,而且默认是public类型的,所以getXXX方法范围就比getDeclaredXXX范围大了,因为getXXX可以搜索到父类,但是getDeclared只允许本类,还可以访问private,default,protected,但是注解只允许public,所以getXX方法范围大。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) 返回public修饰的注解,包含父类。
public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)返回所有修饰符的注解,不包含父类。
注意:区分好结构之间的关系,比如类的属性的注解和类的注解不是同一个getAnnotation方法就可以获取到,想获取属性的注解,要先获取属性,再获取属性的注解,不可以直接通过类去获取属性的注解。
例子
//所有类型的注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** 统一说明下面的注解类的属性和方法的属性是不同的东西,虽然调用的方法是同一个, * 但是如果是方法的属性,则要通过反射先获取方法类,再通过方法类去获取方法类的属性 * 类class,方法method,构造器constructor,接口interface,属性Field * */ /** * 测试用于类的注解 * @author leak */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotationClass { String className() default "";//默认类名null } /** * 测试用于方法的注解 * @author leak * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotationMethod{ String methodName() default "";//默认方法名Null } //测试用于构造器的注解 @Target(ElementType.CONSTRUCTOR) @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotationConstructor{ String constructorName(); } //测试用于属性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotationField{ String FieldName(); String FieldType(); } //测试用于局部变量的注解 @Target(ElementType.LOCAL_VARIABLE) @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotationLocalV{ String localVariable(); String VariableType(); } //测试用于接口的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TestAnnotationInterface{ String InterfaceName(); } //使用注解的类 //1接口注解 @TestAnnotationInterface(InterfaceName = "AA") interface AA{ @TestAnnotationMethod(methodName = "接口的空方法") void kong(); } //2类注解 @Deprecated @TestAnnotationClass(className = "TestClass") public class TestClass { //3属性注解 @TestAnnotationField(FieldName = "Age",FieldType = "int") int age; //4构造器注解 @TestAnnotationConstructor(constructorName = "constructor") public TestClass() { } //5方法注解 @TestAnnotationMethod(methodName = "Test") public void test(String name) { name = "zhutou"; //6局部变量注解,暂时找不到获取局部变量的方法 @TestAnnotationLocalV(localVariable = "l",VariableType = "boolean") boolean l; } } //测试获取注解的值的类 import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; /** * 注意区分某些注解是属于类/接口/方法里面的,如果分不清就可能返回空指针异常 * @author leak * */ public class Test8 { public static void main(String[] args) { try { /**下面的注解对象都是是通过动态代理生成的,注解在运行时,会生成 * 1关于类的注解 */ //反射获取Class类的实例对象(还是类)相对Class是对象 Class<?> classAnnotation = Class.forName("org.annotaion.day11.TestClass"); //获取类的注解对象1 TestAnnotationClass annotation = classAnnotation.getAnnotation(TestAnnotationClass.class); //获取类的注解对象2 Deprecated annotation4 = classAnnotation.getAnnotation(Deprecated.class); //获取类的注解对象的值 System.out.println("类注解的注解名字:"+annotation.className()+",其他注解:"+annotation4); System.out.println("----------------------------"); /** * 1.1关于类的属性的注解 */ //因为是类里面的属性,所以可以使用上面反射获取到的Class对象 Field field = classAnnotation.getDeclaredField("age"); //获取类里面的属性的注解 TestAnnotationField annotationField = field.getAnnotation(TestAnnotationField.class); System.out.println("属性注解的属性名字:"+annotationField.FieldName()+",属性注解的属性类型"+annotationField.FieldType()); System.out.println("-----------------------------"); /** * 1.2关于类的方法的注解 */ //因为是类里面的方法,所以可以使用上面反射获取到的Class对象 //获取类里面的方法对象 Method classMethod = classAnnotation.getMethod("test",String.class); //获取方法的注解 TestAnnotationMethod methodAnnotation = classMethod.getDeclaredAnnotation(TestAnnotationMethod.class); //获取方法注解的值,这里不包含方法属性的注解 System.out.println(methodAnnotation.methodName()); //获取方法的形参 Parameter[] parameters = classMethod.getParameters(); for(Parameter p : parameters) { System.out.println(p); } //暂时找不到获取方法里面局部变量的注解的值,待解决 System.out.println("---------------------------"); /** * 1.3关于类里面的构造器的注解 */ //因为是类里面的构造器,所以可以使用上面反射获取到的Class对象 //获取类里面的构造器 Constructor<?> constructor = classAnnotation.getConstructor(); //获取构造器里面的注解 TestAnnotationConstructor constructorAnnotation = constructor.getDeclaredAnnotation(TestAnnotationConstructor.class); //获取构造器注解里面的值 System.out.println(constructorAnnotation.constructorName()); /** * 关于接口的注解 */ //反射获取Class类的实例对象(还是类)相对Class是接口对象 Class<?> InterfaceAnnotation = Class.forName("org.annotaion.day11.AA"); //获取接口类的注解 TestAnnotationInterface annotation2 = InterfaceAnnotation.getAnnotation(TestAnnotationInterface.class); //获取接口注解对象的值 System.out.println(annotation2.InterfaceName()); //获取接口类里面的方法对象 Method method = InterfaceAnnotation.getMethod("kong"); //获取接口里面的方法的注解 TestAnnotationMethod annotation3 = method.getAnnotation(TestAnnotationMethod.class); System.out.println(annotation3.methodName()); System.out.println("----------------------------"); } catch (Exception e) { e.printStackTrace(); } } }
反射和注解的一个练习,编写一个类似SpringIOC容器
这个练习,实现了IOC容器,和Spring里面的Bean注解和AutoWire注解的实现方法
//三层 //实体类 import java.io.Serializable; /** * * @author leak * 实体类 * 序列化可以通过IO流进行网络传输 */ public class User implements Serializable{ private int id; private String username; private String password; private int age; public User() {} public User(int id, String username, String password, int age) { this.id = id; this.username = username; this.password = password; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + "]"; } } //dao层 import java.util.List; import org.chen.entity.User; public interface IUserDao { User findUserById(int id); List<User> findAllUsers(); List<User> findUsersByUserName(); void saveUser(User user); } //dao层实现类 import java.util.ArrayList; import java.util.List; import org.chen.annotation.Bean; import org.chen.dao.IUserDao; import org.chen.entity.User; @Bean public class UserDaoImpl implements IUserDao{ @Override public User findUserById(int id) { System.out.println("这里是dao-findUserById"); return null; } @Override public List<User> findAllUsers() { System.out.println("这里是dao-findAllUsers"); List<User> list = new ArrayList<User>(); return list; } @Override public List<User> findUsersByUserName() { System.out.println("这里是dao-findUsersByUserName"); return null; } @Override public void saveUser(User user) { System.out.println("service层把对象传过来,注意,只有dao层有对象,才可以调用这里的方法,因为方法的调用,要先创建对象,才可以使用"); System.out.println("这里是dao-saveUser"); } } //service层 //接口 public interface IUserService { void login(); void regist(); } //service层实现类 import org.chen.annotation.AutoWire; import org.chen.annotation.Bean; import org.chen.dao.IUserDao; import org.chen.dao.impl.UserDaoImpl; import org.chen.entity.User; import org.chen.service.IUserService; @Bean public class UserServiceImpl implements IUserService{ @AutoWire private IUserDao userDao; @Override public void login() { userDao.findUsersByUserName(); System.out.println("登录业务的实现方法"); } @Override public void regist() { System.out.println("调用userDao的方法,看看userDao属性是否已经注入对象了,只有存在对象,才可以调用方法"); userDao.saveUser(new User(1,"猪头","123",22)); System.out.println("注册业务的实现方法"); } } //注解 //bean注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Bean { } //AutoWire注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动装配,把对象装配到属性里面 * @author leak * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoWire { } //核心配置类,ioc容器的bean和autowire在这里实现 import java.io.File; import java.io.InputStream; import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Properties; import java.util.Set; import org.chen.annotation.AutoWire; import org.chen.annotation.Bean; /** * 配置类,包含反射创建对象,通过Bean注解的类放入beanFacotry的方法, * 通过AutoWire注解的类把beanFacotry的对象取出注入该属性的方法 * @author leak * 比如dao层和service层不想直接new对象来使用方法 而是直接一次创建对象后,直接拿到同一个对象 */ public class ApplicationContext<T> { // Class就是接口/类,Object就是类创建的对象 private HashMap<Class, Object> beanFacotry = new HashMap<>(); // 项目的编译后的路径,比如当前编译后的路径是/E:/workspace-sts/IOC/bin/,注意本项目的src是源文件目录,不是编译后的路径 private String filePath; // bean就是一个个对象,dao层对象,service层对象 // 这个方法测试,是否已经把bean放入了beanFacotry里面,根据接口/类,去获取beanFacotry里面的对象 public T getBean(Class clazz) {// 可以拿,那么需要一个东西存 // 根据接口/类,去拿对应的对象 return (T) beanFacotry.get(clazz); } //load方法作用:实现ioc容器初始化,把Bean注解的类放入ioc容器中,AutoWire注解的属性就注入对象 public void load() { // getPath仅仅返回路径的字符串,getFile返回的是文件,如果路径一样,就是后面没有?xx=xx就返回和getPath一样的结果 // getResource()先将搜索父类加载器的资源; 如果父级是null ,则会搜索内置到虚拟机的类加载器的路径,返回一个URL对象 // substring(1)截取第一个字符/ // 获取当前项目的编译后的项目路径,也就是存放.class的项目路径 filePath = ApplicationContext.class.getClassLoader().getResource("").getFile().substring(1); // System.out.println(filePath); //下面就是实现ioc核心方法的两个方法,一个是bean,一个自动装配 // loadOne方法已经把bean注解的类创建对象放入IOC容器中(BeanFactory) loadOne(new File(filePath)); // 接下来要从ioc容器中把对象拿出来,注入到属性中 assembly(); } // 自动装配 private void assembly() { //遍历beanFacotry容器把全部对象拿出来 Collection<Object> objs = beanFacotry.values(); //遍历每个对对象 for(Object obj : objs) { //获取当前对象的类 Class class1 = obj.getClass(); // System.out.println(class1); //获取该类的所有属性 Field[] fields = class1.getDeclaredFields(); //判断该类是否存在属性, Collections.emptyList().toString()就是一个[]空数组而已 if(fields.length > 0 || fields.toString() != Collections.emptyList().toString()) { //如果有属性,就遍历 for(Field field : fields) { //有可能属性是私有类型,解除封装 field.setAccessible(true); //如果当前的类的属性有AutoWire注解 AutoWire autoWire = field.getDeclaredAnnotation(AutoWire.class); if(autoWire !=null) { try { //beanFacotry.get(field.getType())的field.getType()获取的是接口/类,然后get(接口/类)获取对象 //field属性的set方法(调用该属性的对象,注入该对象) System.out.println("正在给:"+class1.getName()+"的属性:"+field.getName()+"注入对象:"+beanFacotry.get(field.getType())); field.set(obj, beanFacotry.get(field.getType())); }catch(Exception e) { e.printStackTrace(); } } } }//结束if判断 } } // 递归遍历编译后路径,找到所有的class文件 public void loadOne(File file) { // 判断是否目录,如果是目录,继续里面找文件 if (file.isDirectory()) { // 把该目录底下的所有文件和目录存放到file数组 File[] listFiles = file.listFiles(); // 如果数组为空,直接停止方法 if (listFiles != null && listFiles.length == 0) { return; } // 否则遍历file数组,把数组里面的文件传进自己方法本身,继续循环判断 else { for (File child : listFiles) { loadOne(child); } } } // 如果是文件 else { // 如果一开始就是文件,就截取掉编译后的项目路径,只保留全类名 // 比如 该文件路径是 E:\workspace-sts\IOC\bin\org\chen\annoation\Bean.class // 然后只截取后面的org\chen\annoation\Bean.class String pathWithClass = file.getAbsolutePath().substring(filePath.length()); // System.out.println(pathWithClass); // 先判断拿到的是不是class类型的文件 if (pathWithClass.contains(".class")) { // 然后把org\chen\annoation\Bean.class的\换成.因为反射创建对象需要通过全类名 // //这里使用双反斜杆\\是一个\反斜杠的意思,前面\进行转义 // 把路径的\换成. 然后把后面的.class去掉,就成了全类名,substring(截取起点,截取结束点)不包含结束点 // 注意:substring里面的结束是pathWithClass.indexOf("."),pathWithClass里面的内容是org\chen\annoation\Bean.class // 所以\换位.后,字符数还是不变的,所以直接截取到了全类名 String AllclassName = pathWithClass.replace("\\", ".").substring(0, pathWithClass.indexOf(".")); // System.out.println(AllclassName); // 拿到全类名后,反射创建对象 try { Class<?> class1 = Class.forName(AllclassName); // 判断全类名获取到的Class对象是不是接口类型 if (!class1.isInterface()) { // 优化,下面不能把所有的bean都放进beanFacotry中,必须要通过注解放入 // 判断该类是否存在bean注解 Bean bean = class1.getAnnotation(Bean.class); if (bean != null) { // 不是接口就可以创建对象 Object obj = (Object) class1.getConstructor().newInstance(); // 如果有接口就用多态 if (class1.getInterfaces().length > 0) { //装配的日志 System.out.println("正在加载接口【"+ class1.getInterfaces()[0] +"】,实例对象是:" + obj.getClass().getName()); beanFacotry.put(class1.getInterfaces()[0], obj); } else { //装配的日志 System.out.println("正在加载类【"+ class1.getName() +"】,实例对象是:" + obj.getClass().getName()); // 没有接口就用本类装 beanFacotry.put(class1, class1.getDeclaredConstructor().newInstance()); } } } else { // System.out.println("不能把接口放入IOC容器,都实例化不了"); } } catch (Exception e) { e.printStackTrace(); } } } } } //测试类 /** * 启动测试类,作用:测试标有Bean注解的类,是否已经放进beanFactory工厂;标有AutoWire注解的属性,是否已经注入对象 * * @author leak * */ @Bean public class Bootstrap { public static void main(String[] args) { //创建配置类 ApplicationContext application = new ApplicationContext(); //初始化配置类 application.load(); //试试根据接口/类名,去获取beanFactory里面对象,看看Bean和AutoWire注解是否生效 IUserService bean =(IUserService) application.getBean(IUserService.class); //获取到了,才可以调用方法,因为非静态方法,需要通过对象才能调用 bean.regist(); } }
这个练习,可以把三层去掉,只保留注解类和配置类,然后把保留的部分,打包成jar包,这就是一个轻量级的框架了,哈哈。
Java动态代理
动态代理其实可以分为两个概念,”动态“和”代理“,首先了解设计模式中的”代理模式“。
代理模式
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。
目的:(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;(2)通过代理对象对原有的业务增强。
接下来用一个例子说明代理模式的作用:
比如张三需要买一个美国进口的剃须刀,他有两种方式,一种直接跑去美国买;一种通过代购李四去买。如果是自己去美国购买,要签证,办理机票,到了美国,也有语言障碍,还需要找该产品的销售地方,这里很好的解释了代理模式的 目的(1),直接访问给系统带来了不必要复杂性。经历这么多步骤,张三肯定是选择第二种方式,通过代购李四直接购买,省事多了,而且代购李四对剃须刀的市场很了解,只需告诉李四对产品的习惯,类型。李四提供一条龙的服务,直接给你买到(购买前的业务增强),买到之后还提供售后服务(购买后面的业务增强)。这里也解释了代理模式的 目的(2),通过代理对象对原有的业务增强,你需求有多个,只要告诉代理对象需求就行,代理对象对你的需求进行增强。
解释动态代理前,先说明静态代理,因为静态代理是有缺陷的,动态代理是对静态代理的一个完善。
静态代理模式类图
静态代理例子:
//剃须刀接口 /** * * @author leak * 剃须刀的接口类 */ public interface ShaverFacotry { //销售剃须刀的方法,接收价格参数 void saleShaver(String price); } //生产剃须刀的具体工厂对象 /** * * @author leak * AboradShaverFactory具体的国外剃须刀工厂 * 真实类 */ public class AboradShaverFactory implements ShaverFacotry{ @Override public void saleShaver(String price) { System.out.println("根据你的价格: "+price+",给你推荐普通剃须刀。"); } } //代理类 /** * 代理类ProxyLisi * 代理类作用:对真实类AboradShaverFactroy业务增强 * @author leak * */ public class ProxyLisi implements ShaverFacotry{ //代理人不会生产产品,所以要包含工厂,通过工厂拿产品 //代理类需要包含真实类,产生真实对象 public AboradShaverFactory factory; //构造方法传入真实工厂对象给代理人李四 public ProxyLisi(AboradShaverFactory factory) { this.factory = factory; } /** * 代理人李四的销售方法,根据价格,然后到工厂拿货, * 然后对销售方法进行业务增强,提供更好服务 */ @Override public void saleShaver(String price) { dosomeThingBefore();//前置增强 //代理人李四的购买方法:把价格给工厂,选择对应的剃须刀 factory.saleShaver(price); dosomeThingEnd();//后置增强 } //售前服务 private void dosomeThingBefore() { System.out.println("根据你的需求,进行市场调研和产品分析"); } //售后服务 private void dosomeThingEnd() { System.out.println("你购买产品后,给你的产品进行精美包装,快递一条龙服务!"); } } //测试类 /** * 测试类 * @author leak * 顾客张三通过代理对象李四,进行购买产品 */ public class Test { public static void main(String[] args) { //1.首先要创建工厂对象,传给李四代理对象 AboradShaverFactory factory = new AboradShaverFactory(); //2.创建李四代理对象,然后把工厂传给李四,李四根据工厂拿货 ProxyLisi lisi = new ProxyLisi(factory); //3.然后有顾客张三要购买剃须刀,然后代理对象李四谈好价格后, //根据价格张三的价格,去工厂拿货 lisi.saleShaver("1000");//假设张三给的价格是1000元 } }
静态代理应用场景:当业务比较简单,实现类不是很多,需求的变化不是很频繁,但是又想增强真实对象的业务能力,但是又不想修改真实对象的内部代码。
静态代理缺陷:在一些复杂的情况,静态代理就有局限性。
接下来举一个例子说明静态代理的缺陷:
王五听说代理人李四,经常去美国给别人代购,于是想拜托李四从美国购买别的产品,但是李四的业务只是购买剃须刀,但是有钱赚,肯定不会拒绝顾客了,所以代理对象李四就需要对自己的业务进行扩展。
按照静态代理的流程,扩展业务需要创建新的接口,然后创建具体的产品类实现该接口,并且代理类需要多实现新业务的接口,还要重写接口方法,并且添加新的工厂类。
代码:
//新的产品接口 //其他产品工厂接口 public interface AFactory { void saleProduct(); } //真实类 //真实类,产生具体产品 public class AAFactory implements AFactory{ @Override public void saleProduct() { System.out.println("销售AA产品。。"); } } //原代理类的增强业务 /** * 代理类ProxyLisi * 代理类作用:对真实类AboradShaverFactroy业务增强 * @author leak * */ public class ProxyLisi implements ShaverFacotry,AFactory{ //代理人不会生产产品,所以要包含工厂,通过工厂拿产品 //代理类需要包含真实类,产生真实对象 public AboradShaverFactory factory; //产品2工厂 public AFactory facotry1; //构造方法传入真实工厂对象给代理人李四 public ProxyLisi(AboradShaverFactory factory) { this.factory = factory; } //传进新的工厂给李四 public ProxyLisi(AFactory facotry) { this.facotry1 = facotry; } /** * 代理人李四的销售方法,根据价格,然后到工厂拿货, * 然后对销售方法进行业务增强,提供更好服务 */ @Override public void saleShaver(String price) { dosomeThingBefore();//前置增强 //代理人李四的购买方法:把价格给工厂,选择对应的剃须刀 factory.saleShaver(price); dosomeThingEnd();//后置增强 } //售前服务 private void dosomeThingBefore() { System.out.println("根据你的需求,进行市场调研和产品分析"); } //售后服务 private void dosomeThingEnd() { System.out.println("你购买产品后,给你的产品进行精美包装,快递一条龙服务!"); } //产品2的销售方法 @Override public void saleProduct() { dosomeThingBefore();//产品2的前置增强 facotry1.saleProduct(); dosomeThingEnd();//产品2的后置增强 } } //测试类 /** * 测试类 * @author leak * 顾客张三通过代理对象李四,进行购买产品 */ public class Test { public static void main(String[] args) { //1.首先要创建工厂对象,传给李四代理对象 AboradShaverFactory factory = new AboradShaverFactory(); //2.创建李四代理对象,然后把工厂传给李四,李四根据工厂拿货 ProxyLisi lisi = new ProxyLisi(factory); //3.然后有顾客张三要购买剃须刀,然后代理对象李四谈好价格后, //根据价格张三的价格,去工厂拿货 lisi.saleShaver("1000");//假设张三给的价格是1000元 System.out.println("-----------------------"); //新需求 //创建不同工厂对象 AFactory factory1 = new AAFactory(); //传给李四 lisi = new ProxyLisi(factory1); lisi.saleProduct(); } }
可以发现业务扩展需要修改原有代码,而且如果业务扩展量比较大的情况,按照静态代理模式,就需要多个产品接口,真实产品类,代理类还需要根据业务扩展的类,进行多次修改,这相当麻烦,而且违反了设计模式的 ”开闭原则“ 。
违反开闭原则的带来的后果有两个:
一个是代理类扩展性变差了,想象一下,如果每次有新需求,就需要在原有代理类增加实现的接口,而且属性和方法也会变多,那么代理类就会变得越来越大,代理类的可扩展性就变差,可读性差;
一个是可维护性变差,可维护性差体现在两个方面,一个方面是编译期,比如接口的需求发生改变,之前接口的形参的数据类型需要改变,变为整型,那么实现接口的真实类也要跟着改变,而且代理类也需要改变,所谓的牵一发而动全身,接口发生改变,真实类和代理类全部报错;一个方面是运行期,代理类的一个接口报错,那么其他的接口也提供不了服务了。
这里谈谈设计模式的几个原则:
单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;
依赖倒转原则:高层模块不应该依赖底层模块具体实现,解耦高层与低层。既面向接口编程,当实现发生变化时,只需提供新的实现类,不需修改高层模块代码;
开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原有的实现代码来满足新需求;
了解静态代理的缺陷后,接下来引入动态代理。
动态代理
还是用一个例子说明动态代理的概念,接着上面剃须刀的例子,自从听说了李四可以前往美国帮别人代购,于是越来越多人来找李四代购各种产品,可是李四只对剃须刀的市场比较理解,其他产品没接触过,而且去调研其他产品市场,还需要时间,但是客户可没时间等。这时李四突然想到一句话,”专业的人做专业的事",代购这个圈子很少,李四认识很多其他代购的人,而且李四手上有优质的客户资源,李四可不可以把代购这个圈子整合在一起呢?整合在一起后,李四就成立一间代购公司。以前的静态代理就是李四自己亲自服务,现在的动态代理就是李四的公司根据不同客户的需求,让不同的员工去服务客户。
动态代理的实现方式和静态代理实现的几个点是一样的,动态代理也需要实现接口,还有包含真实类。不同的地方在于动态代理的扩展性非常强,可以同时代理多个真实类,可以实现多个接口。
动态代理类图
动态代理其实就是一个代理工厂,根据用户需求,自动生成对应的代理类,这个代理类去实现接口和包含真实类。
所以动态代理(代理工厂)完全符合单一原则和开闭原则。
动态代理内部情况
动态代理究竟是怎么样实现扩展性强的呢?接下来用例子解析动态代理内部情况。
李四的代购公司就是一个动态代理例子,李四根据不同客户的需求(InvocationHandler),然后根据需求分发需求给对应的代购员工(Proxy)。
而且动态代理符号单一职责原则,因为一个员工只做一种代购。
接下来用代码演示,动态代理如何符合开闭原则和单一原则的。
代码:
//其他产品工厂接口 public interface AFactory { void saleProduct(String employer,int size); } /** * * @author leak * 剃须刀的接口类 */ public interface ShaverFacotry { //销售剃须刀的方法,接收价格参数 void saleShaver(String employer,String price); } //真实类,产生具体产品 public class AAFactory implements AFactory{ @Override public void saleProduct(String employer, int size) { System.out.println("你好我是lisi公司的"+employer+"员工,根据的你需求大小:"+size+",推荐你购买AA产品。"); } } /** * * @author leak AboradShaverFactory具体的国外剃须刀工厂 真实类 */ public class AboradShaverFactory implements ShaverFacotry { @Override public void saleShaver(String employer, String price) { System.out.println("你好我是lisi公司的" + employer + "员工,根据的你需求价格:" + price + ",推荐你购买普通剃须刀。"); } } //代理类 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理类 * * @author leak 代理工厂,制定员工的工作流程 */ public class LisiCompany implements InvocationHandler { // 包含真实类(被代理的类) //为了提高代码的复用,所以工厂的类型需要同一,不然要写多个工厂属性和方法 private Object factory; public Object getFactroy() { return factory; } public void setFactory(Object factory) { this.factory = factory; } // 通过Proxy获取动态代理的对象 //newProxyInstance(类加载器,接口,LisiCompany实例对象) //动态代理之所以能扩展性强,可维护性好,是因为代理类Proxy的newProxyInstance方法封装了实现接口的类,这个类根据不同需求自动生成 //Proxy代理类是负责实现接口,生成对应的类;InvocationHandler是负责生成的类执行的工作流程 //Proxy和InvocationHandler是通过newProxyInstance方法进行合作的,newProxyInstance前面的两个参数是通过传递的工厂反射获取到对应的方法 //最后的this参数是指LisiCompany实例后的对象,这个对象可以调用当前类的所有东西(类的工作流程) //所以getProxyInstance方法是返回一个(根据传递的工厂生成对应的类,这个类还有工作的流程(业务增强))对象 public Object getProxyInstance() { return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this); } //通过动态代理对象对方法进行增强 //是通过反射获取被代理类的方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { dosomeThingBefore();//前置增强 Object result = method.invoke(factory, args); dosomeThingEnd();//后置增强 return result; } // 售前服务 private void dosomeThingBefore() { System.out.println("根据你的需求,进行市场调研和产品分析"); } // 售后服务 private void dosomeThingEnd() { System.out.println("你购买产品后,给你的产品进行精美包装,快递一条龙服务!"); } } //测试类 /** * 测试类 * @author leak * 顾客张三通过代理对象李四,进行购买产品 */ public class Test { public static void main(String[] args) { //张三需要的产品 ShaverFacotry factory1 = new AboradShaverFactory(); //王五需要的产品 AFactory factory2 = new AAFactory(); //创建李四代理工厂 LisiCompany lisiCompany = new LisiCompany(); //根据张三的需求,确定所需的产品 lisiCompany.setFactory(factory1); //创建一个符合张三需求的产品的代购员工 ShaverFacotry lisi1 =(ShaverFacotry) lisiCompany.getProxyInstance(); //代理员工根据张三的需求购买产品,lisi1.getClass().getName()获取当前的类名,可以发现不同的需求,有不同的类 //代理工厂就是通过这些自动生成的封装类实现接口,从而保持动态代理的可扩展性和可维护性 lisi1.saleShaver(lisi1.getClass().getName(), "120"); System.out.println("--------------------------------"); //根据王五的需求,确定所需的产品 lisiCompany.setFactory(factory2); //然后创建一个符合王五需要的产品的代理员工 AFactory lisi2 =(AFactory) lisiCompany.getProxyInstance(); //根据王五需求购买产品 lisi2.saleProduct(lisi2.getClass().getName(),30); } }
总结:动态代理的单一原则和开闭原则主要是依赖InvocationHandler接口和Proxy代理类,InvocationHandler作用是制定每个不同需求的工作流程(业务增强),Proxy代理类的作用是根据不同需求生成动态代理对象。InvocationHandler和Proxy是通过Proxy代理类下的newProxyInstance(类加载器,接口,当前需求类的实例对象) 进行合作的。
newProxyInstance方法根据不同的需求(传递进来的工厂),通过反射获取该需求的需要的接口和实现类动态生成一个代理类(代理类(静态代理的lisi)本来就需要实现接口和包含真实类,动态代理的动态就是通过自动生成代理类,然后代理工厂根据需求生成的代理类实例化对象),然后这个代理类根据InvocationHandler实现类制定的业务增强(工作流程),动态生成一个代理对象(不同领域的代购lisi)。
补充:静态代理和动态代理都有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?推荐使用CGLib动态代理,CGLib是避免了实现接口的方式,采用了继承的形式,代理类继承真实类,然后代理类有一个方法拦截器,业务增强就在方法拦截器里面添加。大概流程:客户需求->代理类继承客户需求的真实类->代理类调用真实类->被方法拦截器拦截进行业务增强->调用增强后的方法。