反射反射机制是Java语言提供的一项基本功能,它赋予程序在运行时自省的能力。简单来说,通过反射,可以在运行时获取、检测和调用对象的属性和方法。反射的使用场景现实中反射的使用场景有很多,比如下面的。使用场景一:编程工具IDEA或者Eclipse等,写代码的时候会有代码(属性或者方法名)提示,因为用到了反射。使用场景二:很多知名的框架为了让程序更加优雅简洁,也使用了反射。例如,Spring可以配置为加载不同的类并调用不同的方法。代码如下:例如MyBatis使用外部类的Sql构造一个在Mapper中查询,代码如下:@SelectProvider(type=PersonSql.class,method="getListSql")ListgetList();classPersonSql{publicStringgetListSql(){Stringsql=newSQL(){{SELECT("*");FROM("person");}}.toString();returnsql;}}使用场景三:数据库连接池,同样使用反射调用不同类型的数据库驱动,代码如下:Stringurl="jdbc:mysql://127.0.0.1:3306/mydb";Stringusername="root";Stringpassword="root";Class.forName("com.mysql.jdbc.Driver");Connectionconnection=DriverManager.getConnection(url,username,password);当然,类似反射的使用场景还有很多,这里就不一一列举了。读者可以举一反三,想想平时开发中还用到了哪些其他的反射函数。场景。反射的基本使用下面通过反射调用类中的方法来学习反射的基本使用。使用反射调用类中的方法可以分为三种情况:调用静态方法调用公有方法调用私有方法假设有一个实体类MyReflect,包含以上三种方法,代码如下:packagecom.interview.chapter4;classMyReflect{//静态方法publicstaticvoidstaticMd(){System.out.println("StaticMethod");}//公共方法publicvoidpublicMd(){System.out.println("PublicMethod");}//私有方法privatevoidprivateMd(){System.out.println("PrivateMethod");}}下面看看如何使用反射来调用以上三类方法。①反射调用静态方法ClassmyClass=Class.forName("com.interview.chapter4.MyReflect");Methodmethod=myClass.getMethod("staticMd");method.invoke(myClass);②反射调用publicmethodClassmyClass=Class.forName("com.interview.chapter4.MyReflect");//创建实例对象(相当于new)Objectinstance=myClass.newInstance();Methodmethod2=myClass.getMethod("publicMd");method2.invoke(instance);③反射调用私有方法ClassmyClass=Class.forName("com.interview.chapter4.MyReflect");//创建实例对象(相当于new)Objectobject=myClass.newInstance();Methodmethod3=myClass.getDeclaredMethod("privateMd");method3.setAccessible(true);method3.invoke(object);反射使用小结反射可以通过Class.forName()获取调用类。反射通过newInstance()获取类实例,相当于new了一个新对象。反射通过getMethod()获取方法,获取类方法后使用invoke()调用类方法。如果类方法是私有方法,需要通过setAccessible(true)修改方法的访问限制。以上操作是反射的基本使用。动态代理动态代理可以理解为,本来应该自己做的事情,却交给别人处理,这个过程叫做动态代理。动态代理的一个著名用例是Spring中的面向方面编程(AOP)。例如依赖注入@Autowired和事务注解@Transactional都是使用动态代理实现的。动态代理还可以封装一些RPC调用,也可以通过代理实现一个全局的拦截器。动态代理和反射的关系JDK提供的动态代理是通过反射实现的,但是动态代理的实现也可以是ASM(一种短小精悍的字节码运行框架)、cglib(基于ASM)等,不限进行反思。下面分别来看一下:JDK原生动态代理和cglib的实现。1)JDK原生动态代理接口Animal{voideat();}classDogimplementsAnimal{@Overridepublicvoideat(){System.out.println("Thedogiseating");}}classCatimplementsAnimal{@Overridepublicvoideat(){System.out.println("Thecatising");}}Java反射与动态代理,看看就明白了//JDK代理类classAnimalProxyimplementsInvocationHandler{privateObjecttarget;//代理对象publicObjectgetInstance(Objecttarget){this.target=target;//获取代理对象returnProxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("调用前");Objectresult=method.invoke(target,args);//方法调用System.out.println("aftercall");returnresult;}}Java反射和动态代理,你可以理解一下dogProxy.eat();}上面的代码,我们实现了通过动态代理,在所有请求的简单信息前后打印一条消息。注意:JDKProxy只能代理实现了接口的类(即使是extends继承类也不能代理)。2)如果cglib动态代理是用cglib实现的,需要添加对cglib的引用。如果是maven项目,直接添加如下代码:cglibcglib3.2.12具体实现cglib的实现请参考如下代码:newEnhancer();//设置父类为实例类enhancer.setSuperclass(this.target.getClass());//回调方法enhancer.setCallback(this);//创建代理对象returnenhancer.create();}publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println("调用前");Objectresult=methodProxy.invokeSuper(o,objects);//执行方法CallSystem.out.println("通话后");返回结果;}}publicstaticvoidmain(String[]args){//cglib动态代理调用CglibProxyproxy=newCglibProxy();Pandapanda=(Panda)proxy.getInstance(newPanda());panda.eat();}上面程序的执行结果:在调用Thepandaiseating调用之前,从上面的代码我们可以知道,cglib的调用实现了MethodInterceptor接口的拦截方法,调用invokeSuper来执行动态代理。可以直接实现普通类动态代理,不需要像JDK代理那样通过接口来完成。值得一提的是,Spring的动态代理也是通过cglib实现的。注意:cglib底层是通过子类继承代理对象来实现动态代理的,所以代理类不能是最终类(final),否则会报错java.lang.IllegalArgumentException:Cannotsubclassfinalclassxxx。相关面试题1、动态代理解决什么问题?答:首先是代理机制。如果熟悉设计模式中的代理模式,我们就会知道,代理可以看成是对调用目标的一个封装,这样我们调用目标代码的调用就不会直接发生,而是完成通过代理,它可以将调用者与实现者分离。比如进行RPC调用,可以通过代理提供更友好的接口;也可以通过代理创建全局拦截器。2、动态代理和反射有什么关系?答:反射可以实现动态代理,但是还有其他方式可以实现动态代理,比如ASM(一种短小精悍的字节码运行框架)、cglib等。3.下面哪个描述是错误的?A:cglib性能更高B:Spring使用cglib实现动态代理C:Spring使用JDK原生动态代理D:JDK原生动态代理性能更高答案:问题D分析:Spring动态代理有两种实现方式:cglib和JDK原生动态代理。4、请完成以下代码?classMyReflect{//私有方法privatevoidprivateMd(){System.out.println("PrivateMethod");}}classReflectTest{publicstaticvoidmain(String[]args)throwsClassNotFoundException,NoSuchMethodException,InvocationTargetException,IllegalExpressionException,Instantiation{ClassmyClass=Class.forName("MyReflect");Objectobject=myClass.newInstance();//补充这行代码method.setAccessible(true);method.invoke(object);}}答:Methodmethod=myClass.getDeclaredMethod("privateMd");题目分析:本题主要考察私有方法的获取,不是通过getMethod()获取的,而是通过getDeclaredMethod()获取的。5、cglib是不是真的可以代理任何类?为什么?答:这句话并不完全正确,因为cglib只能代理可以有子类的普通类。对于final类(final),cglib是不能实现动态代理的,因为cglib底层是通过继承代理类的子类来实现动态代理的,所以不能继承的类不能使用cglib。6、JDK原生动态代理和cglib有什么区别?答:JDK原生动态代理和cglib的区别如下:JDK原生动态代理是基于接口实现的,不需要添加任何依赖,可以平滑支持JDK版本的升级;cglib不需要实现接口,可以直接表示普通类,需要添加依赖包才能达到更高的性能。7、为什么JDK的原生动态代理一定要通过接口来完成?答:这是因为JDK的原始设计。我们看一下动态代理实现方法newProxyInstance()的源码:[]interfaces,InvocationHandlerh)throwsIllegalArgumentException{//省略其他代码看前两个参数的声明:loader:类加载实现者,即target.getClass().getClassLoader()接口代理类。看了上面的参数说明,我们明白了JDK原生动态的使用只能通过实现接口来完成。小结通过这篇文章我们可以知道JDK的原生动态代理是使用反射实现的,但是动态代理的实现不仅仅是体现,还有ASM(一个短小精悍的字节码运行框架),cglib(基于ASM)等其中,JDK的原生动态代理是通过接口实现的,而cglib是通过子类实现的,所以cglib无法代理最终类(final)。反射不仅可以反射调用静态方法,还可以反射调用普通方法和私有方法。调用私有方法时,将setAccessible设置为true。