H__D  

一、反射概念

  Reflection(反射)是被视为动态语言的关键,反射机制允许程序在运行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

  通过下面的代码初步理解反射机制,通过这个大概理解反射机制,不需要每行代码都理解,下文会细讲。创建了一个Person类。

 1 public class Person {
 2 
 3     private String name;
 4     public int age;
 5 
 6     @Override
 7     public String toString() {
 8         return "Person{" +
 9                 "name='" + name + '\'' +
10                 ", age=" + age +
11                 '}';
12     }
13 
14     public String getName() {
15         return name;
16     }
17 
18     public void setName(String name) {
19         this.name = name;
20     }
21 
22     public int getAge() {
23         return age;
24     }
25 
26     public void setAge(int age) {
27         this.age = age;
28     }
29 
30     public Person(String name, int age) {
31 
32         this.name = name;
33         this.age = age;
34     }
35 
36     private Person(String name) {
37         this.name = name;
38     }
39 
40     public Person() {
41         System.out.println("Person()");
42     }
43 
44     public void show(){
45         System.out.println("你好,我是一个人");
46     }
47 
48     private String showNation(String nation){
49         System.out.println("我的国籍是:" + nation);
50         return nation;
51     }
52 }
Person.java
 1 public class ReflectionTest {
 2 
 3     //反射之前,对于Person的操作
 4     @Test
 5     public void test1(){
 6         //1.创建Person类的对象
 7         Person p1 = new Person("Tom", 12);
 8 
 9         //2.通过对象,调用其内部的属性、方法
10         p1.age = 10;
11         System.out.println(p1.toString());
12 
13         p1.show();
14 
15         //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
16         //比如:name、showNation()以及私有的构造器
17     }
18 
19     //反射之后,对于Person的操作
20     @Test
21     public void test2() throws Exception{
22 
23         Class clazz = Person.class;
24         //1.通过反射,创建Person类的对象
25 //        Person p1 = (Person) clazz.newInstance();
26         Constructor constructor = clazz.getConstructor(String.class, int.class);
27         Person p1 = (Person) constructor.newInstance("Tom", 12);
28         System.out.println(p1.toString());
29 
30         //2.通过反射,调用对象指定的属性、方法
31         //调用属性
32         Field age = clazz.getDeclaredField("age");
33         age.set(p1, 12);
34         System.out.println(p1.toString());
35 
36         //调用方法
37         Method show = clazz.getDeclaredMethod("show");
38         show.invoke(p1);
39 
40         System.out.println("*******************************");
41 
42         //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
43         //调用私有的构造器
44         Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
45         constructor1.setAccessible(true);
46         Person p2 = (Person) constructor1.newInstance("Jerry");
47         System.out.println(p2.toString());
48 
49         //调用私有的属性
50         Field name = clazz.getDeclaredField("name");
51         name.setAccessible(true);
52         name.set(p2, "HaiMeimei");
53         System.out.println(p2.toString());
54 
55         //调用私有的方法
56         Method showNation = clazz.getDeclaredMethod("showNation", String.class);
57         showNation.setAccessible(true);
58         String nation = (String) showNation.invoke(p2, "中国");
59         System.out.println(nation);
60     }
61 }

  从上述代码可以大概了解到Java反射机制提供的功能有

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时获取泛型信息
  5. 在运行时调用任意一个对象的成员变量和方法
  6. 在运行时处理注解
  7. 生成动态代理

  反射相关的主要API

  1. java.lang.Class:代表一个类
  2. java.lang.reflect.Method:代表类的方法
  3. java.lang.reflect.Field:代表类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造器

二、 Class类的理解

  1.类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

  2.换句话说,Class的实例就对应着一个运行时类。

  3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

  

三、获取Class实例的几种方式:(前三种方式需要掌握)

 1     @Test
 2     public void test3() throws ClassNotFoundException {
 3         //方式一:调用运行时类的属性:.class
 4         Class clazz1 = Person.class;
 5         System.out.println(clazz1);
 6 
 7         //方式二:通过运行时类的对象,调用getClass()
 8         Person p = new Person();
 9         Class clazz2 = p.getClass();
10         System.out.println(clazz2);
11 
12         //方式三:调用Class的静态方法:forName(String classPath)
13         Class clazz3 = Class.forName("com.test.java.Person");
14         System.out.println(clazz3);
15 
16         System.out.println(clazz1 == clazz2);
17         System.out.println(clazz1 == clazz3);
18 
19         //方式四:使用类的加载器:ClassLoader  (了解)
20         ClassLoader classLoader = ReflectionTest.class.getClassLoader();
21         Class clazz4 = classLoader.loadClass("com.test.java.Person");
22         System.out.println(clazz4);
23 
24         System.out.println(clazz1 == clazz4);
25     }

 

创建运行时类的对象

  创建类的对象:调用Class对象的newInstance()方法

  要求:

  1)类必须有一个无参数的构造器。

  2)类的构造器的访问权限需要足够。

  代码举例:

1 Class<Person> clazz = Person.class;
2 Person obj = clazz.newInstance();
3 System.out.println(obj);

 

  难道没有无参的构造器就不能创建对象了吗?不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

  步骤如下:

  1)通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器

  2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

  3)通过Constructor实例化对象。

1 //1.获取对应的Class对象
2 Class clazz= Class.forName(Person);
3 
4 //2.调用指定参数结构的构造器,生成Constructor的实例
5 Constructor con = clazz.getDeclaredConstructor(String.class,Integer.class);
6 
7 //3.通过Constructor的实例创建对应类的对象,并初始化类属性
8 Person p2 = (Person) con.newInstance("Peter",20);
9 System.out.println(p2)

五、获取运行时类的完整结构

  我们可以通过反射,获取对应的运行时类中所有的属性(Field)、方法(Method)、构造器(Constructor)、父类(Superclass)、接口(Interface)、父类的泛型、包(package)、注解(Annotation)、异常等。。。。

  获取属性:  

 1 /**
 2  * 获取当前运行时类的属性结构
 3  *
 4  */
 5 public class FieldTest {
 6 
 7     @Test
 8     public void test1(){
 9 
10         Class clazz = Person.class;
11 
12         //获取属性结构
13         //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
14         Field[] fields = clazz.getFields();
15         for(Field f : fields){
16             System.out.println(f);
17         }
18         System.out.println();
19 
20         //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
21         Field[] declaredFields = clazz.getDeclaredFields();
22         for(Field f : declaredFields){
23             System.out.println(f);
24         }
25     }
26 
27     //权限修饰符  数据类型 变量名
28     @Test
29     public void test2(){
30         Class clazz = Person.class;
31         Field[] declaredFields = clazz.getDeclaredFields();
32         for(Field f : declaredFields){
33             //1.权限修饰符
34             int modifier = f.getModifiers();
35             System.out.print(Modifier.toString(modifier) + "\t");
36 
37             //2.数据类型
38             Class type = f.getType();
39             System.out.print(type.getName() + "\t");
40 
41             //3.变量名
42             String fName = f.getName();
43             System.out.print(fName);
44 
45             System.out.println();
46         }
47 
48     }
49 
50 }
View Code

 

  获取方法: 

 1 /**
 2  * 获取运行时类的方法结构
 3  *
 4  */
 5 public class MethodTest {
 6 
 7     @Test
 8     public void test1(){
 9 
10         Class clazz = Person.class;
11 
12         //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
13         Method[] methods = clazz.getMethods();
14         for(Method m : methods){
15             System.out.println(m);
16         }
17         System.out.println();
18         //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
19         Method[] declaredMethods = clazz.getDeclaredMethods();
20         for(Method m : declaredMethods){
21             System.out.println(m);
22         }
23     }
24 
25     /*
26     @Xxxx
27     权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
28      */
29     @Test
30     public void test2(){
31         Class clazz = Person.class;
32         Method[] declaredMethods = clazz.getDeclaredMethods();
33         for(Method m : declaredMethods){
34             //1.获取方法声明的注解
35             Annotation[] annos = m.getAnnotations();
36             for(Annotation a : annos){
37                 System.out.println(a);
38             }
39 
40             //2.权限修饰符
41             System.out.print(Modifier.toString(m.getModifiers()) + "\t");
42 
43             //3.返回值类型
44             System.out.print(m.getReturnType().getName() + "\t");
45 
46             //4.方法名
47             System.out.print(m.getName());
48             System.out.print("(");
49             //5.形参列表
50             Class[] parameterTypes = m.getParameterTypes();
51             if(!(parameterTypes == null && parameterTypes.length == 0)){
52                 for(int i = 0;i < parameterTypes.length;i++){
53 
54                     if(i == parameterTypes.length - 1){
55                         System.out.print(parameterTypes[i].getName() + " args_" + i);
56                         break;
57                     }
58 
59                     System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
60                 }
61             }
62 
63             System.out.print(")");
64 
65             //6.抛出的异常
66             Class[] exceptionTypes = m.getExceptionTypes();
67             if(exceptionTypes.length > 0){
68                 System.out.print("throws ");
69                 for(int i = 0;i < exceptionTypes.length;i++){
70                     if(i == exceptionTypes.length - 1){
71                         System.out.print(exceptionTypes[i].getName());
72                         break;
73                     }
74 
75                     System.out.print(exceptionTypes[i].getName() + ",");
76                 }
77             }
78 
79 
80             System.out.println();
81         }
82 
83     }
84 }
View Code

  获取其他结构: 

  1 /**
  2  */
  3 public class OtherTest {
  4 
  5     /*
  6     获取构造器结构
  7      */
  8     @Test
  9     public void test1(){
 10 
 11         Class clazz = Person.class;
 12         //getConstructors():获取当前运行时类中声明为public的构造器
 13         Constructor[] constructors = clazz.getConstructors();
 14         for(Constructor c : constructors){
 15             System.out.println(c);
 16         }
 17 
 18         System.out.println();
 19         //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
 20         Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
 21         for(Constructor c : declaredConstructors){
 22             System.out.println(c);
 23         }
 24 
 25     }
 26 
 27     /*
 28     获取运行时类的父类
 29 
 30      */
 31     @Test
 32     public void test2(){
 33         Class clazz = Person.class;
 34 
 35         Class superclass = clazz.getSuperclass();
 36         System.out.println(superclass);
 37     }
 38 
 39     /*
 40     获取运行时类的带泛型的父类
 41 
 42      */
 43     @Test
 44     public void test3(){
 45         Class clazz = Person.class;
 46 
 47         Type genericSuperclass = clazz.getGenericSuperclass();
 48         System.out.println(genericSuperclass);
 49     }
 50 
 51     /*
 52     获取运行时类的带泛型的父类的泛型
 53 
 54 
 55     代码:逻辑性代码  vs 功能性代码
 56      */
 57     @Test
 58     public void test4(){
 59         Class clazz = Person.class;
 60 
 61         Type genericSuperclass = clazz.getGenericSuperclass();
 62         ParameterizedType paramType = (ParameterizedType) genericSuperclass;
 63         //获取泛型类型
 64         Type[] actualTypeArguments = paramType.getActualTypeArguments();
 65 //        System.out.println(actualTypeArguments[0].getTypeName());
 66         System.out.println(((Class)actualTypeArguments[0]).getName());
 67     }
 68 
 69     /*
 70     获取运行时类实现的接口
 71      */
 72     @Test
 73     public void test5(){
 74         Class clazz = Person.class;
 75 
 76         Class[] interfaces = clazz.getInterfaces();
 77         for(Class c : interfaces){
 78             System.out.println(c);
 79         }
 80 
 81         System.out.println();
 82         //获取运行时类的父类实现的接口
 83         Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
 84         for(Class c : interfaces1){
 85             System.out.println(c);
 86         }
 87 
 88     }
 89     /*
 90         获取运行时类所在的包
 91 
 92      */
 93     @Test
 94     public void test6(){
 95         Class clazz = Person.class;
 96 
 97         Package pack = clazz.getPackage();
 98         System.out.println(pack);
 99     }
100 
101     /*
102         获取运行时类声明的注解
103 
104      */
105     @Test
106     public void test7(){
107         Class clazz = Person.class;
108 
109         Annotation[] annotations = clazz.getAnnotations();
110         for(Annotation annos : annotations){
111             System.out.println(annos);
112         }
113     }
114 
115 }
View Code 

六、调用运行类的指定结构

  

  1 public class ReflectionTest {
  2 
  3     @Test
  4     public void testField() throws Exception {
  5         Class clazz = Person.class;
  6 
  7         //创建运行时类的对象
  8         Person p = (Person) clazz.newInstance();
  9 
 10         //获取指定的属性:要求运行时类中属性声明为public
 11         //通常不采用此方法
 12         Field id = clazz.getField("id");
 13 
 14         /*
 15         设置当前属性的值
 16 
 17         set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
 18          */
 19         id.set(p, 1001);
 20 
 21         /*
 22         获取当前属性的值
 23         get():参数1:获取哪个对象的当前属性值
 24          */
 25         Integer pId = (Integer) id.get(p);
 26         System.out.println(pId);
 27     }
 28 
 29     /*
 30     如何操作运行时类中的指定的属性 -- 需要掌握
 31      */
 32     @Test
 33     public void testField1() throws Exception {
 34         Class clazz = Person.class;
 35 
 36         //创建运行时类的对象
 37         Person p = (Person) clazz.newInstance();
 38 
 39         //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
 40         Field name = clazz.getDeclaredField("name");
 41 
 42         //2.保证当前属性是可访问的
 43         name.setAccessible(true);
 44 
 45         //3.获取、设置指定对象的此属性值
 46         name.set(p, "Tom");
 47 
 48         System.out.println(name.get(p));
 49     }
 50 
 51     /*
 52     如何操作运行时类中的指定的方法 -- 需要掌握
 53      */
 54     @Test
 55     public void testMethod() throws Exception {
 56         Class clazz = Person.class;
 57 
 58         //创建运行时类的对象
 59         Person p = (Person) clazz.newInstance();
 60 
 61         /*
 62         1.获取指定的某个方法
 63         getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
 64          */
 65         Method show = clazz.getDeclaredMethod("show", String.class);
 66         //2.保证当前方法是可访问的
 67         show.setAccessible(true);
 68 
 69         /*
 70         3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
 71         invoke()的返回值即为对应类中调用的方法的返回值。
 72          */
 73         Object returnValue = show.invoke(p, "CHN");
 74         System.out.println(returnValue);
 75 
 76         System.out.println("*************如何调用静态方法*****************");
 77 
 78 
 79         Method showDesc = clazz.getDeclaredMethod("showDesc");
 80         showDesc.setAccessible(true);
 81         //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
 82 //        Object returnVal = showDesc.invoke(null);
 83         Object returnVal = showDesc.invoke(Person.class);
 84         System.out.println(returnVal);
 85     }
 86 
 87     /*
 88     如何调用运行时类中的指定的构造器
 89      */
 90     @Test
 91     public void testConstructor() throws Exception {
 92         Class clazz = Person.class;
 93 
 94         /*
 95         1.获取指定的构造器
 96         getDeclaredConstructor():参数:指明构造器的参数列表
 97          */
 98         Constructor constructor = clazz.getDeclaredConstructor(String.class);
 99         //2.保证此构造器是可访问的
100         constructor.setAccessible(true);
101         //3.调用此构造器创建运行时类的对象
102         Person tom = (Person) constructor.newInstance("Tom");
103         System.out.println(tom);
104     }
105 }

  关于setAccessible方法的使用

  1. Method和Field、Constructor对象都有setAccessible()方法。
  2. setAccessible启动和禁用访问安全检查的开关。
  3. 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
  4. 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
  5. 使得原本无法访问的私有成员也可以访问参数值为false则指示反射的对象应该实施Java语言访问检查。

七、反射的应用:动态代理

  代理设计模式的原理

  使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原 始对象上。

  静态代理

 1 /**
 2  * 静态代理举例
 3  *
 4  * 特点:代理类和被代理类在编译期间,就确定下来了。
 5  *
 6  */
 7 interface ClothFactory {
 8 
 9     void produceCloth();
10 
11 }
12 //被代理类
13 class NikeClothFactory implements ClothFactory{
14 
15     public void produceCloth() {
16         System.out.println("Nike工厂生产一批运动服");
17     }
18 }
19 //代理类
20 class ProxyClothFactory implements ClothFactory{
21 
22     private ClothFactory factory; //用被代理类对象进行实例化
23 
24     public ProxyClothFactory(ClothFactory factory){
25         this.factory = factory;
26     }
27 
28     public void produceCloth() {
29         System.out.println("代理工厂做一些准备工作");
30         factory.produceCloth();
31         System.out.println("代理工厂做一些收尾工作");
32     }
33 }
34 public class StaticProxyTest {
35     public static void main(String[] args) {
36         //创建被代理类的对象
37         ClothFactory nike = new NikeClothFactory();
38         //创建代理类的对象
39         ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
40 
41         proxyClothFactory.produceCloth();
42     }
43 }

  动态代理

  Java动态代理相关API

  Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一 个或多个接口动态地生成实现类。提供用于创建动态代理类和动态代理对象的静态方法

  static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

  创建一个动态代理类所对应的Class对象  

  static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 直接创建一个动态代理对象

  代码如下:

 1 /**
 2  *
 3  * 动态代理的举例
 4  *
 5  */
 6 
 7 interface Human {
 8     String getBelief();
 9 
10     void eat(String food);
11 }
12 
13 //被代理类
14 class SuperMan implements Human{
15 
16     public String getBelief() {
17         return "I believe I can fly!";
18     }
19 
20     public void eat(String food) {
21         System.out.println("我喜欢吃" + food);
22     }
23 }
24 
25 class HumanUtil {
26     public static void method1(){
27         System.out.println("=========通用方法一=========");
28     }
29 
30     public static void method2(){
31         System.out.println("=========通用方法二=========");
32     }
33 }
34 
35 
36 /*
37 要想实现动态代理,需要解决的问题?
38 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
39 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
40 
41 
42  */
43 
44 class ProxyFactory {
45     //调用此方法,返回一个代理类的对象。解决问题一
46     public static Object getProxyInstance(Object obj) {
47         MyInvocationHandler handler = new MyInvocationHandler();
48         handler.bind(obj);
49 
50         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler );
51     }
52 }
53 
54 class MyInvocationHandler implements InvocationHandler{
55 
56     private Object obj;//需要使用被代理类的对象进行赋值
57 
58     public void bind(Object obj){
59         this.obj = obj;
60     }
61 
62     //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
63     //将被代理类要执行的方法a的功能就声明在invoke()中
64     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
65         System.out.println("动态代理准备");
66         HumanUtil.method1();
67 
68         //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
69         //obj:被代理类的对象
70         Object val = method.invoke(obj, args);
71 
72         HumanUtil.method2();
73         System.out.println("动态代理收尾");
74 
75         //上述方法的返回值就作为当前类中的invoke()的返回值。
76         return val;
77     }
78 }
79 
80 
81 public class ProxyTest {
82     public static void main(String[] args) {
83         Human superMan = new SuperMan();
84         //proxyInstance:代理类的对象
85         Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
86         //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
87         System.out.println(proxyInstance.getBelief());
88         proxyInstance.eat("大米");
89 
90         System.out.println("*****************");
91 
92         NikeClothFactory nikeClothFactory = new NikeClothFactory();
93         ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
94 
95         proxyClothFactory.produceCloth();
96 
97     }
98 }

 

  动态代理与AOP

  使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有 太大的意义。通常都是为指定的目标对象生成动态代理

  这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理 包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异: 

  AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

  

posted on 2021-02-25 11:21  H__D  阅读(24)  评论(0编辑  收藏