后台反射在Java中非常重要,是Java区别于其他编程语言的一大特点。Java中的AOP方面、动态代理等看似黑魔法的技术,都离不开反射、字节码等。这些技术可以在不侵入原始代码的情况下进行一些增强的非功能性需求。还有一点,不要把业务逻辑放在AOP方面和动态代理里面,不然后人肯定会骂的。AOP方面:在方法执行前后添加逻辑,可以决定方法如何执行,甚至不执行。动态代理:在运行时生成目标类的代理类,可以增强目标类的功能。本文总结了反射的原理和实际使用。后面有时间再介绍AOP方面和动态代理。什么是反射?看看官方原文的定义:反射是Java编程语言中的一个特性。它允许正在执行的Java程序检查或“内省”自身,并操纵程序的内部属性。例如,Java类有可能获取其所有成员的名称并显示它们。翻译过来就是:反射是指一个运行中的Java程序可以检查和修改自己内部的属性,也可以叫做内省。反射是Java区别于其他编程语言的一大特点。reflection从字面意思来说,就是倒影和反光的意思,就像照镜子的时候,可以通过自己的倒影知道自己长什么样,可以通过整理头发来改变发型。一句话反射的原理是:JVM会动态加载Class,一个Class实例包含了该类的所有完整信息,如:包名、类名、各个字段、各个方法、父类、实现的接口等因此,如果你得到某个类或对象的Class实例,就可以通过它得到对应类的所有信息。动态加载是指JVM在第一次读取一个类类型时就加载到内存中,而不是一启动就加载所有的类信息。每次加载一个类时,JVM都会为它创建一个Class类型的实例并进行关联。即一个类的不同对象实例对应的是它们后面的同一个Class实例。详细介绍可以参考:https://www.liaoxuefeng.com/w...如何使用反射?需要熟练使用反射中的几个常用类:Class、Field、Method、Constructor。还有其他参数,例如Parameter类。可以这样理解,凡是出现在Java对象中的,都可以在java.reflect包中找到对应的类。Class的获取方式有三种:class.class:类的类静态变量对象。getClass()Class.forName("类的完整路径名")FieldClass实例。getField(name):根据字段名(包括父类)获取一个publicFieldClassinstance.getDeclaredField(name):根据字段名获取类(不包括父类)声明的一个Field。常用Field[]getFields():获取所有公共字段,包括父类的字段。不常用,因为根据Java规范,一般定义私有字段,然后通过公共的getter和setter方法获取字段值。Field[]getDeclaredFields():获取本类声明的所有字段,不包括父类的字段。常用。MethodClassinstance.getMethod(name,Class...):获取一个公共的Method(包括父类)Class实例。getDeclaredMethod(name,Class...):获取本类的一个方法(不包括父类)Method[]getMethods():获取所有公共方法(包括父类)。常用Method[]getDeclaredMethods():获取当前类(不包括父类)的所有Methods。AnnotatedElementClass、Field和Method都是AnnotatedElement的子类。常用的有这几个方法:getAnnotation(Class):根据Class获取对应的注解isAnnotationPresent(Class):判断是否被注解修改,相当于getAnnotation(annotationClass)!=null,比较方便getAnnotations()的写法:获取所有修改的注解反射使用示例1.反射获取一个类的所有字段,方便后续运行时读写如果一个类没有实现toString()方法,或者某些私有字段没有实现提供一个getter方法,那么如果要遍历所有的字段,就只能通过反射来实现了。得到所有的反射域后,很容易遍历或修改它们。通过Field和object获取对应字段的值:field.get(object)通过Field和object修改对应字段的值:先设置为可访问,这样即使是私有字段也可以修改:field.setAccessible(true);反射修改字段值:field.set(Object,Object)反射获取类的所有字段,分为两部分:一是类的父类的字段,二是该类的getDeclaredFields方法引用的其他类的字段只能获取到该类声明的字段,如果该类也继承父类(可能存在多层继承关系),如何获取所有这些继承的字段?默认情况下所有类都是Object的子类,或者Object是根上的父类。利用这个特性,可以不断的通过getSuperclass找到父类,得到父类的字段,然后直到父类是Object。了解Object类本身没有任何字段也很重要。因此,可以这样实现://获取类的所有字段,包括父类privateListgetAllFields(Class>clazz){Listresult=newArrayList<>();类>cls=clazz;while(cls!=null){//也可以写成while(cls!=null&&cls!=Object.class)result.addAll(Arrays.asList(cls.getDeclaredFields()));cls=cls.getSuperclass();}返回结果;}在本地缓存一个类(包括引用的其他类)涉及的所有字段的反射信息。之所以要把反射信息缓存在本地,是因为反射是一个耗时的操作。如果在初始化阶段就缓存起来,后面使用的时候性能会更快。使用广度优先遍历算法获取类引用的所有反射字段。下面是带注释的代码。/***获取一个类(包括引用类)的所有反射字段的实例*/publicclassGetAllFields{//构建反射字段的缓存:key是全路径类名,value是这个类的所有字段publicstaticMap>buildReflectCache(Class>clazz){Map>result=newHashMap<>();ListtopLevelFields=getAllFields(clazz);result.put(clazz.getName(),topLevelFields);//广度优先遍历类的字段,通过队列实现Queuequeue=newLinkedList<>(topLevelFields);while(!queue.isEmpty()){Field字段=queue.poll();//如果是集合或者Map类型的字段,需要提取泛型if(Collection.class.isAssignableFrom(field.getType())||Map.class.isAssignableFrom(field.getType())){=field.getGenericType();if(genericTypeinstanceofParameterizedType){ParameterizedTypeparameterizedType=(ParameterizedType)genericType;for(Typetype:parameterizedType.getActualTypeArguments()){Class>actualClass=(Class>)类型;如果(!isBasicClass(actualClass)){ListsubFields=getAllFields(actualClass);result.putIfAbsent(actualClass.getName(),subFields);queue.addAll(子字段);}}}}elseif(!isBasicClass(field.getType())){//只处理自定义类型,不处理Java基本类型ListsubFields=getAllFields(field.getType());result.putIfAbsent(field.getType().getName(),subFields);queue.addAll(子字段);}}返回结果;}//获取类的所有字段,包括父类privatestaticListgetAllFields(Class>clazz){Listresult=newArrayList<>();班级&lt;?>cls=clazz;while(cls!=null){result.addAll(Arrays.asList(cls.getDeclaredFields()));cls=cls.getSuperclass();}返回结果;}//Java是否是基本类型:知道加载类的类加载器是否为null,null为Java基本类型,否则为自定义类型privatestaticbooleanisBasicClass(Class>clazz){returnclazz!=null&&clazz.getClassLoader()==null;}publicstaticvoidmain(String[]args){ListallFields=getAllFields(SomeClass.class);System.out.println(allFields);Map>reflectCache=buildReflectCache(SomeClass.class);System.out.println(reflectCache);}}2。结合反射和Spring,初始化时找到对应bean对应的方法需要结合Spring获取bean:MapapplicationContext.getBeansWithAnnotation(Class),key是bean名,value是是bean类实例。假设有2个注解,@PrintInfoClass修饰在类上,@PrintInfoMethod修饰在方法上,代表要打印的方法的签名。@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.Type})@Documentedpublic@interfacePrintInfoClass{}@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})@Documentedpublic@interfacePrintInfoMethod{}@Componentpublic类PrintInfoimplementsApplicationContextAware{@PostConstructpublicvoidinit(){//通过Spring框架,方便获取带有PrintInfoClass注解的beanMapbeansWithAnnotationMap=applicationContext.getBeansWithAnnotation((PrintInfoClass.class);for(Map.Entryentry:beansWithAnnotationMap.entrySet()){Objectbean=entry.getValue();for(Methodmethod:bean.getMethods()){//仅获取使用PrintInfoMethod注释的方法if(method.isAnnotationPresent(PrintInfoMethod).class){//打印方法签名StringBuilderparamString=newStringBuilder();Class>[]paramClassList=method.getParameterTypes();for(inti=0;iparamClass=paramClassList[i];paramString.append(paramClass.getSimpleName());if(i!=paramClassList.length-1){paramString.append(",");}}System.out.print(Modifier.toString(method.getModifiers())+""+method.getReturnType().getSimpleName()+""+method.getName()+"("+paramString.toString()+")\n");}}}}@AutowiredprivateApplicationContextapplicationContext;publicvoidsetApplicationContext(ApplicationContextapplicationContext){this.applicationContext=applicationContext;}}