【回归系列】反射

反射就是动态加载对象,并对对象进行剖析。在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象方法的功能成为Java反射机制。

 

一、基本操作

 1 package com.slp.springbootelasticsearch.test;
 2 
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Modifier;
 6 
 7 /**
 8  * @author sanglp
 9  * @create 2018-07-05 17:30
10  * @desc 测试类
11  **/
12 public class Student {
13     private long id;
14     private String name;
15 
16     public long getId() {
17         return id;
18     }
19 
20     public void setId(long id) {
21         this.id = id;
22     }
23 
24     public String getName() {
25         return name;
26     }
27 
28     public void setName(String name) {
29         this.name = name;
30     }
31 
32 
33     public static  void  main(String[] args){
34         try {
35             //初始化指定的类
36             Class<?> claz= Class.forName("com.slp.springbootelasticsearch.test.Student");
37             //获取类中所有的方法(包括其继承类的方法)
38             Method[] methods = claz.getMethods();
39             for(Method method :methods){
40                 System.out.println("方法名:"+method.getName());
41             }
42             System.out.println("------------------");
43             //获取加载类中的方法,不要父类的方法
44             methods = claz.getDeclaredMethods();
45             for(Method method :methods){
46                 System.out.println("方法名:"+method.getName());
47                 System.out.println("方法返回类型:"+method.getReturnType().getName());
48                 System.out.println("方法修饰符:"+ Modifier.toString(method.getModifiers()));
49                 System.out.println("方法参数信息:"+ method.getParameters());
50                 System.out.println("方法上的注解:"+method.getAnnotations());
51             }
52             System.out.println("--------------------");
53             //使用反射调用方法 通过class的newInstance()方法构造一个Student对象,然后调用getName()方法,这个时候输出的是null,然后通过方法名获取到setName方法,通过invoke调用方法,传入参数,然后调用getName()方法就可以看到输出的就是我们设置的值“大小”
54             Student stu = (Student)claz.newInstance();
55             System.out.println(stu.getName());
56             Method method = claz.getMethod("setName",String.class);
57             method.invoke(stu,"大小");
58             System.out.println(stu.getName());
59             System.out.println("--------------------");
60             //获取类中所有属性 getFields只能获取public的属性,包括父类的
61             Field[]  fields = claz.getFields();
62             for(Field field:fields){
63                 System.out.println("属性名"+field.getName());
64             }
65             //获取自己声明的各种字段要使用getDeclaredFields()
66             fields = claz.getDeclaredFields();
67             for(Field field:fields){
68                 System.out.println("属性名"+field.getName());
69                 System.out.println("属性类型"+field.getType().getName());
70                 System.out.println("属性修饰符"+Modifier.toString(field.getModifiers()));
71                 System.out.println("属性上的注解"+field.getAnnotations());
72             }
73             //操作属性
74             Field field = claz.getDeclaredField("name");
75             //取消java控制权限,让访问时可以访问私有变量
76             field.setAccessible(true);
77             System.out.println(field.get(stu));
78             field.set(stu,"大小");
79             System.out.println(field.get(stu));
80         } catch (Exception e) {
81             e.printStackTrace();
82         }
83 
84     }
85 }

输出:

 1 方法名:main
 2 方法名:getName
 3 方法名:getId
 4 方法名:setName
 5 方法名:setId
 6 方法名:wait
 7 方法名:wait
 8 方法名:wait
 9 方法名:equals
10 方法名:toString
11 方法名:hashCode
12 方法名:getClass
13 方法名:notify
14 方法名:notifyAll
15 ------------------
16 方法名:main
17 方法返回类型:void
18 方法修饰符:public static
19 方法参数信息:[Ljava.lang.reflect.Parameter;@49097b5d
20 方法上的注解:[Ljava.lang.annotation.Annotation;@37a71e93
21 方法名:getName
22 方法返回类型:java.lang.String
23 方法修饰符:public
24 方法参数信息:[Ljava.lang.reflect.Parameter;@7e6cbb7a
25 方法上的注解:[Ljava.lang.annotation.Annotation;@37a71e93
26 方法名:getId
27 方法返回类型:long
28 方法修饰符:public
29 方法参数信息:[Ljava.lang.reflect.Parameter;@7c3df479
30 方法上的注解:[Ljava.lang.annotation.Annotation;@37a71e93
31 方法名:setName
32 方法返回类型:void
33 方法修饰符:public
34 方法参数信息:[Ljava.lang.reflect.Parameter;@7106e68e
35 方法上的注解:[Ljava.lang.annotation.Annotation;@37a71e93
36 方法名:setId
37 方法返回类型:void
38 方法修饰符:public
39 方法参数信息:[Ljava.lang.reflect.Parameter;@7eda2dbb
40 方法上的注解:[Ljava.lang.annotation.Annotation;@37a71e93
41 --------------------
42 null
43 大小
44 --------------------
45 属性名id
46 属性类型long
47 属性修饰符private
48 属性上的注解[Ljava.lang.annotation.Annotation;@37a71e93
49 属性名name
50 属性类型java.lang.String
51 属性修饰符private
52 属性上的注解[Ljava.lang.annotation.Annotation;@37a71e93
53 大小
54 大小

二、优点和缺点

1、优点

反射提高了程序的灵活性和扩展性,在底层框架中用的比较多,业务层面的开发过程中尽量少用。

2、缺点

反射是一种解释操作,用于字段和方法接入时要远慢于直接代码。

程序逻辑有影响。

   使用反射操作会模糊下程序的内部逻辑,从维护的角度来讲,我们更希望在源码的程序中看到程序的逻辑,反射相当于绕过了源码的方式,因此会带来维护难度比较大的问题。

三、反射使用场景

1、实现RPC框架

RPC是远程过程调用的简称,广泛应用在大规模分布式应用中。提到RPC框架首先想到的是Dubbo,远程过程调用的实现原理是放客户端调用的时候通过动态代理向服务提供方发送调用的信息,服务提供方收到后根据客户端需要调用的方法,调用本地方法,拿到结果后组装返回。

 

2、实现ORM框架

3、拷贝属性值(BeanUtils.copyProperties)

 

 1 private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
 2         Assert.notNull(source, "Source must not be null");
 3         Assert.notNull(target, "Target must not be null");
 4         Class actualEditable = target.getClass();
 5         if(editable != null) {
 6             if(!editable.isInstance(target)) {
 7                 throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
 8             }
 9 
10             actualEditable = editable;
11         }
12 
13         PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
14         List ignoreList = ignoreProperties != null?Arrays.asList(ignoreProperties):null;
15         PropertyDescriptor[] var7 = targetPds;
16         int var8 = targetPds.length;
17 
18         for(int var9 = 0; var9 < var8; ++var9) {
19             PropertyDescriptor targetPd = var7[var9];
20             Method writeMethod = targetPd.getWriteMethod();
21             if(writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
22                 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
23                 if(sourcePd != null) {
24                     Method readMethod = sourcePd.getReadMethod();
25                     if(readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
26                         try {
27                             if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
28                                 readMethod.setAccessible(true);
29                             }
30 
31                             Object ex = readMethod.invoke(source, new Object[0]);
32                             if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
33                                 writeMethod.setAccessible(true);
34                             }
35 
36                             writeMethod.invoke(target, new Object[]{ex});
37                         } catch (Throwable var15) {
38                             throw new FatalBeanException("Could not copy property \'" + targetPd.getName() + "\' from source to target", var15);
39                         }
40                     }
41                 }
42             }
43         }
44 
45     }

 

posted @ 2018-07-06 11:52  霓裳梦竹  阅读(282)  评论(0)    收藏  举报