本文共 4094 字,大约阅读时间需要 13 分钟。
反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力。通过反射,我们可以直接操作类或对象,例如获取某个对象的类定义、获取类声明的属性和方法、调用方法或构造对象,甚至可以运行时修改类定义。
获取类对象有三种方法:
forName()
方法:Class.forName("PeopleImpl")
getClass()
方法:new PeopleImpl().getClass()
.class
属性:PeopleImpl.class
类中提供了许多有用的方法:
getName()
:获取类完整名称;getSuperclass()
:获取类的父类;newInstance()
:创建实例对象;getFields()
:获取当前类和父类的 public 修饰的所有属性;getDeclaredFields()
:获取当前类(不包含父类)的声明的所有属性;getMethod()
:获取当前类和父类的 public 修饰的所有方法;getDeclaredMethods()
:获取当前类(不包含父类)的声明的所有方法;更多方法还有:getEnclosingClass()
, getModifiers()
, getParameters()
, getReturnType()
, getSignum()
, 等等。
通过反射调用类中的方法,需要使用 invoke()
方法。方法调用分为三种类型:
示例代码:
public static void main(String[] args) { Class myClass = Class.forName("example.PeopleImpl"); // 调用静态(static)方法 Method getSex = myClass.getMethod("getSex"); getSex.invoke(myClass);}
示例代码:
public static void main(String[] args) { Class myClass = Class.forName("example.PeopleImpl"); Object object = myClass.newInstance(); Method method = myClass.getMethod("sayHi", String.class); method.invoke(object, "老王");}
示例代码:
public static void main(String[] args) { Class myClass = Class.forName("example.PeopleImpl"); Object object = myClass.newInstance(); Method privSayHi = myClass.getDeclaredMethod("privSayHi"); privSayHi.setAccessible(true); privSayHi.invoke(object);}
newInstance()
获取类实例,getMethod()
获取方法,使用 invoke()
进行方法调用,通过 setAccessible(true)
修改私有变量/方法的访问限制。动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,广泛应用于 RPC 调用、面向切面编程(AOP)等场景。
JDK Proxy 是通过实现 InvocationHandler
接口来实现的。代码示例:
interface Animal { void eat();}class Dog implements Animal { @Override public void eat() { System.out.println("The dog is eating"); }}class Cat implements Animal { @Override public void eat() { System.out.println("The cat is eating"); }}// JDK 代理类class AnimalProxy implements InvocationHandler { private Object target; public Object getInstance(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用前"); Object result = method.invoke(target, args); System.out.println("调用后"); return result; }}public static void main(String[] args) { AnimalProxy proxy = new AnimalProxy(); Animal dogProxy = (Animal) proxy.getInstance(new Dog()); dogProxy.eat();}
需要注意的是,JDK Proxy 只能代理实现接口的类(即使是继承类也是不可以的)。
Cglib 是一种基于 ASM 的动态代理框架,它可以对任意类(包括 final 类)生成代理类,覆盖特定方法实现。代码示例:
class Panda { public void eat() { System.out.println("The panda is eating"); }}class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("调用前"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("调用后"); return result; }}public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Panda panda = (Panda) proxy.getInstance(new Panda()); panda.eat();}
Cglib 的优势是可以直接代理任意类,而 JDK Proxy 只能代理实现接口的类。然而,Cglib 的缺点是对 final 类无法代理,且依赖 ASM 类库。
JDK Proxy:
Cglib:
JDK Proxy:
Cglib:
总结:在选型时需要综合考虑性能、可靠性、可维护性等因素,标准类库和反射编程的门槛较低,是初次学习动态代理的不错选择。
转载地址:http://zrgfz.baihongyu.com/