小言_互联网的博客

Java反射机制与动态代理

333人阅读  评论(0)

1. 概述

Java 反射机制与动态代理我们平时写代码可能用得比较少,但在各种常见的框架(Spring、MyBatis 等)中却屡见不鲜。有句话叫“无反射,不框架;无代理,不框架”。

由于以后打算阅读和学习框架的源码,这里先简单回顾反射机制和动态代理(暂不深入分析实现原理),为后面做些准备

2. 反射机制

Java 反射机制是在 Java 程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。

从面向对象的角度来看,我们平时用到的"类"、"构造器"、"属性"、"方法"其实也是一个"类",它们在 JDK 中分别对应 Class、Constructor、Field、Method 类。其中 Class 相当于"类的类",可称为"元类",从这个角度看,我们平时自定义的"类"可以理解为 Class 的一个对象。

简单来说(个人理解),反射机制就是通过 Class、Constructor 等"元类"来操作其他的普通类(创建对象、调用方法等)。下面以简单代码示例。

2.1 准备代码

定义一个普通的 Java 类 Human,如下:


   
  1. package com.jaxer.example.reflection;
  2. public class Human {
  3. public String gender;
  4. private int age;
  5. protected void hello() {
  6. }
  7. public void hi() {
  8. }
  9. }

定义一个普通的 Person 类,继承自 Human,如下:


   
  1. public class Person extends Human {
  2.    // 两个属性
  3. public String name;
  4. private int age;
  5.    // 各种修饰符的构造器
  6. public Person() {
  7. }
  8. private Person(String name) {
  9. this.name = name;
  10. }
  11. protected Person(int age) {
  12. this.age = age;
  13. }
  14. Person(String name, int age) {
  15. this.name = name;
  16. this.age = age;
  17. }
  18.    // getter, setter 方法
  19. public String getName() {
  20. return name;
  21. }
  22. public void setName(String name) {
  23. this.name = name;
  24. }
  25. public int getAge() {
  26. return age;
  27. }
  28. public void setAge(int age) {
  29. this.age = age;
  30. }
  31.    // 自定义方法
  32. private void test() {
  33. System.out.println( "test is invoked.");
  34. }
  35. @Override
  36. public String toString() {
  37. return "Person{" +
  38. "name='" + name + '\'' +
  39. ", age=" + age +
  40. '}';
  41. }
  42. }

该 Person 类定义了 name 和 age 两个成员变量及其 getter/setter 方法,还有四个构造器,分别使用不同的修饰符,自定义了一个 test 方法,重写了 toString 方法。

PS: 这两个类仅供参考,只是为了演示。

2.2 获取 Class

获取 Person 的 Class 对象通常有下面三种方式:


   
  1. // 方式一:从对象获取
  2. Person person = new Person();
  3. person.getClass();
  4. // 方式二:类名.class
  5. Person.class;
  6. // 方式三:Class.forName
  7. Class<?> aClass = Class.forName( "com.jaxer.example.reflection.Person");
  8. /*
  9. 这三种方式的获取到的都是:
  10. class com.jaxer.example.reflection.Person
  11. 而且可以证明,这三种方式得到的同一个 Class 对象,即同一个 Person 类
  12. */

获取到了 Class 对象,就可以使用 Class 对象来创建 Person 对象或者做一些其他操作,例如:


   
  1. // 获取 Class 对象
  2. Class<?> aClass = Class.forName( "com.jaxer.example.reflection.Person");
  3. // 使用 Class 创建 Person 对象
  4. System. out.println(aClass.newInstance()); // 创建 Person 对象
  5. // 获取 Person 的父类(Human)
  6. System. out.println( "superClass-->" + aClass.getSuperclass());
  7. /* 输出结果:
  8. * Person{name='null', age=0}
  9. * superClass-->class com.jaxer.example.reflection.Human
  10. */

2.3 获取 Constructor

Class 中获取构造器(Constructor)的方法如下:

  1. getConstructors: 获取所有 public 构造器;

  2. getDeclaredConstructors: 获取所有构造器(包括 private 类型);

  3. getConstructor(Class… parameterTypes): 获取指定参数的 public 构造器;

  4. getDeclaredConstructor(Class… parameterTypes): 获取指定参数的构造器(包括 private 类型)。

测试代码:


   
  1. private static void testConstructors(Class<?> aClass) throws Exception {
  2. // 1. 获取所有public构造器
  3. Constructor<?>[] constructors = aClass.getConstructors();
  4. for (Constructor<?> constructor : constructors) {
  5. System.out.println("constructor-->" + constructor);
  6. }
  7.     // 2. 获取所有构造器
  8. Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
  9. for (Constructor<?> declaredConstructor : declaredConstructors) {
  10. System.out.println("declaredConstructor-->" + declaredConstructor);
  11. }
  12. // 4. 获取指定的构造器(根据参数类型)
  13. Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
  14.     // 修改访问权限(用这种方式可以使用 private 类型构造器创建对象)
  15. constructor.setAccessible(true);
  16.     // 使用该构造器创建 Person 对象
  17. Object instance = constructor.newInstance("Ace");
  18. System.out.println(instance);
  19. }

输出结果如下:


   
  1. constructor-->public com.jaxer.example.reflection.Person()
  2. declaredConstructor-->com.jaxer.example.reflection.Person(java.lang.String,int)
  3. declaredConstructor-->protected com.jaxer.example.reflection.Person(int)
  4. declaredConstructor-->private com.jaxer.example.reflection.Person(java.lang.String)
  5. declaredConstructor-->public com.jaxer.example.reflection.Person()
  6. Person{name='Ace', age=0}

2.4 获取 Field

获取 Class 属性(Field)的方法如下:

  1. getFields(): 获取 public 属性(包含父类);

  2. getDeclaredFields(): 获取所有属性(包括 private 类型);

  3. getField(String name): 获取指定名称的 public 属性;

  4. getDeclaredField(String name): 获取指定名称的属性(包括 private 类型)。

测试代码:


   
  1. private static void testFields(Class<?> aClass) throws Exception {
  2. // 1. 获取 public 属性(包含父类的 public 属性)
  3. Field[] fields = aClass.getFields();
  4. for (Field field : fields) {
  5. System. out.println( "field-->" + field);
  6. }
  7. // 2. 获取所有属性
  8. Field[] declaredFields = aClass.getDeclaredFields();
  9. for (Field declaredField : declaredFields) {
  10. System. out.println( "declaredField-->" + declaredField);
  11. }
  12. // 4. 获取指定的属性(这里获取 age 属性)
  13. Field age = aClass.getDeclaredField( "age");
  14. System. out.println( "age-->" + age);
  15. // 给指定对象的属性赋值(private 类型不能赋值,需要修改访问权限)
  16. Object obj = aClass.newInstance();
  17. age.setAccessible( true); // 修改访问权限
  18. age. set(obj, 18); // 设置新值
  19. System. out.println( "obj-->" + obj);
  20. }

输出结果:


   
  1. field--> public java.lang.String com.jaxer.example.reflection.Person.name
  2. field--> public java.lang.String com.jaxer.example.reflection.Human.gender
  3. declaredField--> public java.lang.String com.jaxer.example.reflection.Person.name
  4. declaredField--> private int com.jaxer.example.reflection.Person.age
  5. age--> private int com.jaxer.example.reflection.Person.age
  6. obj-->Person{name= 'null', age= 18}

2.5 获取 Method

获取 Class 方法(Method)的方法如下:

  1. getMethods(): 获取所有 pubic 方法(包括父类及 Object 类);

  2. getDeclaredMethods(): 获取所有方法(包括 private 方法);

  3. getMethod(String name, Class… parameterTypes): 获取指定名称和参数的 public 方法;

  4. getDeclaredMethod(String name, Class… parameterTypes): 获取指定名称和参数的方法(包括 private 方法)。

测试代码:


   
  1. private static void testMethods(Class<?> aClass) throws Exception {
  2. // 1. 获取所有 public 方法(包括父类及 Object 类)
  3. Method[] methods = aClass.getMethods();
  4. for (Method method : methods) {
  5. System. out.println( "method-->" + method);
  6. }
  7. // 2. 获取该类声明的所有方法
  8. Method[] declaredMethods = aClass.getDeclaredMethods();
  9. for (Method declaredMethod : declaredMethods) {
  10. System. out.println( "declaredMethod-->" + declaredMethod);
  11. }
  12. // 4. 获取指定的方法并调用
  13. Method method = aClass.getDeclaredMethod( "test");
  14. method.setAccessible( true); // 修改访问权限
  15. System. out.println( "方法名:" + method.getName());
  16. System. out.println( "方法修饰符:" + Modifier.toString(method.getModifiers()));
  17. Object instance = aClass.newInstance();
  18. System. out.println(method.invoke(instance));
  19. }

输出结果:


   
  1. // getMethods 方法返回结果:
  2. // Person 类的 public 方法:
  3. method--> public java.lang.String com.jaxer.example.reflection.Person.toString()
  4. method--> public java.lang.String com.jaxer.example.reflection.Person.getName()
  5. method--> public void com.jaxer.example.reflection.Person.setName(java.lang.String)
  6. method--> public void com.jaxer.example.reflection.Person.setAge( int)
  7. method--> public int com.jaxer.example.reflection.Person.getAge()
  8. // Human 类的 public 方法:
  9. method--> public void com.jaxer.example.reflection.Human.hi()
  10. // Object 类的 public 方法:
  11. method--> public final void java.lang.Object.wait( long, int) throws java.lang.InterruptedException
  12. method--> public final native void java.lang.Object.wait( long) throws java.lang.InterruptedException
  13. method--> public final void java.lang.Object.wait() throws java.lang.InterruptedException
  14. method--> public boolean java.lang.Object.equals(java.lang.Object)
  15. method--> public native int java.lang.Object.hashCode()
  16. method--> public final native java.lang.Class java.lang.Object.getClass()
  17. method--> public final native void java.lang.Object.notify()
  18. method--> public final native void java.lang.Object.notifyAll()
  19. // getDeclaredMethods 方法返回结果:
  20. declaredMethod--> public java.lang.String com.jaxer.example.reflection.Person.toString()
  21. declaredMethod--> public java.lang.String com.jaxer.example.reflection.Person.getName()
  22. declaredMethod--> public void com.jaxer.example.reflection.Person.setName(java.lang.String)
  23. declaredMethod--> private void com.jaxer.example.reflection.Person.test()
  24. declaredMethod--> public void com.jaxer.example.reflection.Person.setAge( int)
  25. declaredMethod--> public int com.jaxer.example.reflection.Person.getAge()
  26. // 调用 test 方法:
  27. 方法名: private void com.jaxer.example.reflection.Person.test()
  28. 方法修饰符: private
  29. test is invoked.
  30. null

框架中常用的反射机制主要是以上那些,这里暂不深究其实现原理,以后有需要再行补充。下面简要分析代理相关的内容。

3. 代理

使用代理的主要目的:对目标对象(的方法)进行功能增强,例如 Spring 的 AOP。

既然是对目标对象的方法进行增强,代理对象的方法中一定会调用目标对象的方法。而且一般会在目标对象的方法调用前后(或者其他时机)做一些其他的处理以达到增强的效果。

代理模式通常分为「静态代理」和「动态代理」,静态代理使用较少;而动态代理常用的有 JDK 动态代理和 CGLib 动态代理。下面简要分析。

3.1 准备代码

一个普通的 Java 接口及其实现类如下:


   
  1. // 接口
  2. public interface UserService {
  3. void save(String name);
  4. }
  5. // 实现类
  6. public class UserServiceImpl implements UserService {
  7. @Override
  8. public void save(String name) {
  9. System.out.println( "保存用户名:" + name);
  10. }
  11. }

无论静态代理还是动态代理,都是对该类的 save 方法进行增强。

此处以在该方法执行前后各打印一句话来演示。

3.2 静态代理

主要思想:创建一个 UserService 接口的实现类 UserServiceProxy(代理对象),并且该类持有 UserServiceImpl 对象(目标对象),在 UserServiceProxy 类的 save 方法中调用 UserServiceImpl 的 save 方法,示例代码如下:


   
  1. public class UserServiceProxy implements UserService {
  2. private UserService userService;
  3. public UserServiceProxy(UserService userService) {
  4. this.userService = userService;
  5. }
  6. @Override
  7. public void save(String name) {
  8. System.out.println( "---静态代理:方法执行前---");
  9. userService.save(name); // 调用目标类的方法
  10. System.out.println( "---静态代理:方法执行后---");
  11. }
  12. }

测试代码:


   
  1. private static void testStaticProxy() {
  2. UserService userService = new UserServiceImpl();
  3. UserService proxy = new UserServiceProxy(userService);
  4. proxy.save( "jack");
  5. }
  6. /* 运行结果:
  7. ---静态代理:方法执行前---
  8. 保存用户:jack
  9. ---静态代理:方法执行后---
  10. */

显而易见,使用 UserServiceProxy 可以增强 UserServiceImpl 的 save 方法。但由于代码是固定的(编码期间写好的),不够灵活,因此静态代理使用较少,通常使用动态代理。

PS: 此处代码实现形式可能不尽相同,但思路相近。

3.3 动态代理

与静态代理相比,动态代理则是在运行时动态生成一个代理类,该类可以对目标对象的方法进行功能增强。动态代理常用的有 JDK 动态代理和 CGLib 动态代理,下面简要分析。

3.3.1 JDK 动态代理

JDK 的动态代理实现方式:

使用 Proxy 类的 newProxyInstance 方法创建代理对象,使用 InvocationHandler 来实现增强的逻辑(通常创建一个 InvocationHandler 接口的实现类,在其 invoke 方法中实现增强的逻辑)。示例代码如下(仅供参考):


   
  1. // JDK 代理工厂,作用是生成代理对象
  2. public class JDKProxyFactory {
  3. // 获取 target 的代理对象(其中 target 为目标对象)
  4. public Object getProxy(Object target) {
  5. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  6. target.getClass().getInterfaces(), new MyInvocationHandler(target));
  7. }
  8. // 自定义 InvocationHandler
  9. private static class MyInvocationHandler implements InvocationHandler {
  10. // 目标对象
  11. private Object target;
  12. public MyInvocationHandler(Object target) {
  13. this.target = target;
  14. }
  15. // 实现增强逻辑
  16. @Override
  17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18. System.out.println("目标类方法执行前-----");
  19. Object result = method.invoke(target, args); // 调用目标对象的方法
  20. System.out.println("目标类方法执行后-----");
  21. return result;
  22. }
  23. }
  24. }

测试代码:


   
  1. private static void testJdkProxy() {
  2. UserService userService = new UserServiceImpl(); // 目标对象
  3. JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(); // 代理工厂
  4. // 使用代理工厂生成代理对象
  5. UserService proxy = (UserService) jdkProxyFactory.getProxy(userService);
  6. proxy.save( "jack"); // 调用代理对象的方法(已对目标对象进行增强)
  7. }
  8. /* 运行结果:
  9. 目标类方法执行前-----
  10. 保存用户:jack
  11. 目标类方法执行后-----
  12. */

实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80586785

3.3.2 CGLib 动态代理

主要思路:

创建一个目标类的子类 Enhancer,在子类中设置回调对象(MethodInterceptor),并在回调方法(intercept)中实现对目标对象的增强功能逻辑。示例代码如下:


   
  1. // CGLIB 工厂,用于生成代理对象
  2. public class CglibProxyFactory {
  3. // 获取代理对象(对目标对象进行增强)
  4. public Object getProxy(Object target) {
  5. Enhancer enhancer = new Enhancer();
  6. enhancer.setSuperclass(target.getClass());
  7. enhancer.setCallback(new MyMethodInterceptor()); // 设置回调
  8. return enhancer.create();
  9. }
  10. // 自定义方法拦截 MethodInterceptor
  11. private static class MyMethodInterceptor implements MethodInterceptor {
  12. @Override
  13. public Object intercept(Object obj, Method method, Object[] args,
  14. MethodProxy proxy) throws Throwable {
  15. System.out.println("cglib方法调用前");
  16. Object o = proxy.invokeSuper(obj, args); // 调用目标对象的方法
  17. System.out.println("cglib方法调用后");
  18. return o;
  19. }
  20. }
  21. }

测试代码:


   
  1. private static void testCglibProxy() {
  2. UserService userService = new UserServiceImpl();
  3. CglibProxyFactory cglibProxyFactory = new CglibProxyFactory();
  4. UserService proxy = (UserService) cglibProxyFactory.getProxy(userService);
  5. proxy.save( "jack");
  6. }
  7. /* 运行结果:
  8. cglib方法调用前
  9. 保存用户:jack
  10. cglib方法调用后
  11. */

PS: 使用 CGLib 需要导入第三方 jar 包(cglib 和 asm)。

实现原理可参考:https://blog.csdn.net/yhl_jxy/article/details/80633194

二者主要区别:

  • JDK 动态代理不依赖第三方库,CGLib 需要依赖第三方库;

  • 若目标对象实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。

4. 小结

反射机制:简单来说,反射机制主要是通过 Class、Constructor 等"元类"来操作其他的普通类,以达到在运行期间动态创建对象、动态调用方法等目的。

静态代理&动态代理:二者主要区别在于时机,静态代理在编码期已写好代理类,而动态代理则是在运行期间动态生成代理类。

JDK 动态代理&CGLib 动态代理的主要区别:

  • JDK 动态代理不依赖第三方库,CGLib 则要依赖第三方

  • JDK 使用 InvocationHandler 接口实现增强逻辑,使用 Proxy.newProxyInstance 生成代理对象;而 CGLib 使用 MethodInterceptor 接口实现增强逻辑,使用 Enhancer 生成代理对象;

  • 若目标对象实现了接口,两种方式都可以使用;若未实现接口,则只能使用 CGLib。



转载:https://blog.csdn.net/u010551118/article/details/103573294
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场