Aplicación real de reflexión de Java (finalmente explique la reflexión)

Aplicación real de reflexión de Java (reflexión) (archivo de configuración de análisis de lectura)


Requisitos: Necesitamos leer el archivo de configuración y luego crear dinámicamente un controlador de conexión (mysql u oracle) basado en la información del archivo de configuración.


  • Comprenda los requisitos: el objeto de accionamiento no puede ser creado por nuevo, ¡porque no sabemos qué parámetros nos pasa el usuario!

  • Para resolver la demanda: ¡el problema solo se puede resolver creando objetos dinámicamente!

  • Resumen de requisitos: utilice el principio dinámico de reflexión para crear los objetos correspondientes basados ​​en parámetros específicos pasados ​​por los usuarios.


    El prototipo de demanda se muestra en la siguiente figura (tome el archivo de propiedades como ejemplo)

    driverName=edu.xja.demo.MysqlConnection
    url=jdbc:mysql://localhost:3306/test
    userName=root
    password=123456
    

    El usuario da la información correspondiente, ¡volvemos al objeto de conexión! (Desacoplamiento)


    Cómo resolver el problema en 3 pasos

    1. Cree diferentes clases de conductores

    2. Crea una clase de prueba

    3. Configure información diferente para probar la conexión


    1. Cree una clase de controlador, tome MySQL y Oracle como ejemplos

    • Cree una clase de controlador mysql (el código abreviado es solo para resaltar ideas de reflexión, la implementación específica no está escrita)

      /**
      * 模拟Mysql驱动类
      **/
      public class MysqlConnection {
              
              
      
          /**
           * 为了简化理解 ,我们模仿创建三个连接数据库必须的参数
           */
          private String url = "";
          private String userName = "";
          private String password = "";
      
          /**
           * 获取mysql连接(简写,只为突出反射思想)
           * @param username 用户名
           * @param password 密码
           * @param url 连接地址
           * @return String
           */
          public String getConnection(String username,String password,String url){
              
              
              this.url = url;
              this.userName = username;
              this.password = password;
              // 打印出信息,此时证明我们反射成功。
              System.out.println("获取mysql驱动:\n username:"+this.userName+"\n password:"+this.password+"\n url:"+this.url);
              return "mysqlConnection";
          }
      }
      
    • Cree un controlador de Oracle (el código abreviado es solo para resaltar ideas de reflexión, la implementación específica no está escrita)

      /**
      * 模拟oracle驱动类
      **/
      public class OracleConnection {
              
              
      
          /**
           * 为了简化理解 ,我们模仿创建三个连接数据库必须的参数
           */
          private String url = "";
          private String userName = "";
          private String password = "";
      
          /**
           * 获取oracle连接(简写,只为突出反射思想)
           * @param username 用户名
           * @param password 密码
           * @param url 连接地址
           * @return String
           */
          public String getConnection(String username,String password,String url){
              
              
              this.url = url;
              this.userName = username;
              this.password = password;
              // 打印出信息,此时证明我们反射成功。
              System.out.println("获取oracle驱动: \n username:"+this.userName+"\n password:"+this.password+"\n url:"+this.url);
              return "oracleConnection";
          }
      }
      

2. Cree una clase de prueba (centrarse en la reflexión). Si no comprende el código aquí, consulte el apéndice (conocimiento de la reflexión) de este artículo.

/**
* 测试反射
**/
public class TestReflect {
    
    
    
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
    
        // 获取反射类的class
        Class cla = Class.forName(getValue("driverName"));
        // 获取反射类的获取连接方法(此方法不同的连接必须方法名一致)
        Method getConnection = cla.getMethod("getConnection",String.class,String.class,String.class);
        Object connection = cla.getConstructor().newInstance();
        // 传入参数,操作connection
        getConnection.invoke(connection,getValue("userName"),getValue("password"),getValue("url"));
    }

    /**
     * 读取配置文件
     * @param key 配置文件中的key
     * @return String 配置文件中key对应的value
     * @throws IOException 异常
     */
    public static String getValue(String key) throws IOException {
    
    
        Properties properties = new Properties();
        // 文件名自定义
        FileReader fileReader = new FileReader("jdbc.properties");
        properties.load(fileReader);
        fileReader.close();
        // 在properties文件中的信息是key-value关系
        return properties.getProperty(key);
    }
}

3. ¡Simule el archivo de configuración, pruebe el código!

  • Cuando la información de las propiedades de configuración del usuario es mysql, como se muestra a continuación.

    driverName=edu.xja.demo.MysqlConnection
    url=jdbc:mysql://localhost:3306/test
    userName=root
    password=123456
    

    El resultado de la impresión es:

    获取mysql驱动:
    username:root
    password:123456
    url:jdbc:mysql://localhost:3306/test
    
    Process finished with exit code 0
    
  • Cuando el usuario configura la información de propiedades como orcal, como se muestra a continuación.

    driverName=edu.xja.demo.OracleConnection
    url=jdbc:oracle:thin:@localhost:1521:orcl
    userName=scott
    password=trigger
    

    El resultado de la impresión es:

    获取oracle驱动: 
    username:scott
    password:trigger
    url:jdbc:oracle:thin:@localhost:1521:orcl
    
    Process finished with exit code 0
    

Apéndice (conocimiento de la reflexión)

Este apéndice explica a través de seis aspectos

  1. Concepto de reflexión

  2. Obtener clase

  3. Obtener constructor

  4. Obtener atributos

  5. Como obtener

  6. Obtener comentarios (no considerados por el momento)


    reflexión


1. El concepto de reflexión (reflexionar)

No podemos crear objetos dinámicamente en lenguajes como JavaScript. Por ejemplo, dicha sintaxis es legal en js.

var x = "var a = 3; var b = 4; alert(a+b)";
eval(x);

En nuestro java, este tipo de gramática no es posible, ¡porque nuestra cognición no puede tratar las cadenas como código java!

El código js anterior puede crear automáticamente variables y otras operaciones durante la ejecución, creemos que es dinámico. Si nuestro Java es dinámico, todo en Java será un objeto. Tomemos como ejemplo la creación de objetos.

Mire la tímida e incomprensible definición de reflexión: en el estado de ejecución de Java (no nuestro nuevo objeto manual), para cualquier clase, podemos conocer todos los métodos y atributos de esta clase; para cualquier objeto, podemos llamarlo Cualquier método y atributo; la función de obtener información dinámicamente y llamar dinámicamente a métodos de objeto se denomina mecanismo de reflexión de Java.

Mirando el pasaje de arriba, hay compañeros de clase y hermosos chinos. Algunos compañeros de clase pidieron hablar palabras humanas, ¡así que déjame hablar sobre mi cognición!

¿Qué es dinámico? Lo que la gente dice es que si usted y el objeto escrito se llaman estáticos, porque no cambiará y no hay necesidad de crear cosas nuevas en el proceso en ejecución, por ejemplo:

		// User是一个类名
		User user = new User();

Ya lo he creado y no necesito que otros me ayuden a crearlo, pero la desventaja se puede reflejar de inmediato, como esta

		// 此处Undefined代表未定义的类,我们不确定
		Undefined undefined = new Undefined();

Solo los fantasmas lógicos pueden resolver este problema. Como no estamos seguros de qué clase es, ¡podemos definir una variable! Por ejemplo como este

		// 定义一个变量储存类名
		String className = "";
		// 例如:我们得到入参为User
		className = User;
		ClassName className = new ClassName(); 

Como se esperaba de usted, pero debemos determinar de forma única una clase, de lo contrario, ¡múltiples jvms de clase informarán errores! Entonces piensa en el nombre de la clase completamente calificado

		// 定义一个变量储存类名
		String className = "";
		// 例如:我们得到入参为com.fu.entity.User
		className = com.fu.entity.User;
		ClassName className = new ClassName(); 

Está mal, ¿cómo se unen esta cadena y la estructura libre de parámetros ()? Esta es una cadena como un todo, y Java no puede reconocerla. ¡Okay! No seremos astutos. ¡Echemos un vistazo al principio operativo de jvm y comprendamos desde abajo!

Quiero un objeto, está bien, uno nuevo. Espera ... no he dicho lo que quiero. ¡Te daré una plantilla y puedes crear una de acuerdo con esta plantilla!

Voy a empezar a escribir plantillas. ¡Nos damos lo que probablemente necesitemos! Si desea varios objetos, podemos usar esta plantilla para copiar.

package edu.xja.demo;

public class GrilFirend {
    
    

    private String name = "波多野结衣";
    private int age = 18;
    public String sex = "女";
    public static long phone = 13298107421L;
    public  GrilFirend(){
    
    }

    private GrilFirend(String name){
    
    
        this.name = name;
    }
    public void method4(){
    
    
        System.out.println("执行了public修饰的method1");
    }
     void method3(String name){
    
    
        this.name = name;
        System.out.println("执行了默认修饰符的method3,name="+this.name);
    }
    protected void method2(){
    
    
        System.out.println("执行了protected修饰的method2");
    }
    private void method1(){
    
    
        System.out.println("执行了private修饰method1");
    }
    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "GrilFirend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Entonces, ¿dónde está esta plantilla? Debido a que esta plantilla es compartida, colocamos el objeto de clase compilado en el montón (jvm). Y creamos un objeto, solo usamos la clase en el montón para crear un archivo de clase de referencia de objeto. ¡Y se modificará en función de la clase! Dé un ejemplo para ilustrar la diferencia y la práctica entre clase y objeto.


public class TestClass {
    
    

    public static void main(String[] args) {
    
    
        // 基本的类模板,标准的对象
        GrilFirend grilFirend = new GrilFirend();
        System.out.println(grilFirend);
        // 改造特有的对象
        grilFirend.setAge(12);
        grilFirend.setName("女优");
        System.out.println(grilFirend.toString());
    }
}
  • Podemos saber eso, supongamos que podemos crear objetos basados ​​en la clase, y basados ​​en la transformación, podemos obtener los objetos únicos que necesitamos.

  • Nuestro primer paso es obtener el objeto estándar, que es la clase.

  • Transformamos la clase para que se convierta en lo que queremos.


    2. Obtener clase (para obtener objetos estándar)

    1. Sabemos que la clase padre de todas las clases es objeto, y el objeto tiene un método común que devuelve una clase, y su método es getClass. Entonces podemos usarlo así.

      							List<String> list = new ArrayList<>();
              					System.out.println(list.getClass());
      

      [Error en la transferencia de la imagen del enlace externo. Es posible que el sitio de origen tenga un mecanismo de enlace anti-sanguijuela. Se recomienda guardar la imagen y subirla directamente (img-BbqlLCEt-1599718235499) (C: \ Users \ 13298 \ Desktop \ object.PNG)]

      La razón por la que no podemos crear nuevos objetos es que cuando creamos nuevos objetos, no necesitamos crear objetos a través de la reflexión, lo que viola el principio de crear dinámicamente objetos inciertos usando clases.


    2. Cualquier clase, interfaz, enumeración o tipo básico. Todos tienen un atributo llamado .class

      import java.util.ArrayList;
      /**
       * @author: fudy
       * @date: 2020/9/10 上午 08:17
       * @Decription:
       **/
      public class TestClass {
              
              
      
          public static void main(String[] args) {
              
              
              Class intClass = int.class;
              Class listClass = ArrayList.class;
              System.out.println(intClass);
              System.out.println(listClass);
          }
      }
      

      De esta manera, no podemos desacoplar, ni podemos crear objetos a través de cadenas, necesitamos importar paquetes.


    3. Sabemos que el mecanismo de carga de clases subyacente de jvm sabe que podemos determinar de forma única una clase obteniendo el nombre de ruta completo de la clase a través de classpath. Luego está esta clase Class que usa el mecanismo de ClassLoader.getClassLoader para ayudarnos a crear un objeto ( comúnmente usado ) basado en el nombre de la clase completamente calificado .

      		Class stringClass = Class.forName("java.lang.String");
              System.out.println("stringClass = " + stringClass);
      

      En este punto, tenemos el objeto de clase, el objeto de clase. ¿Para qué sirven los objetos de clase? Creamos objetos basados ​​en la clase, y debemos conocer todos los campos, métodos, anotaciones y otra información, porque la clase se compila a partir de la clase. El contenido de la clase dentro identificará todos los métodos de clase, atributos de clase y modificadores de nuestra clase, etc. Class es un espejo que puede mapear toda la información de la clase y pasarla a jvm.


    3. Crea un objeto mediante el método de construcción (Constructor)

    • Obtener método de construcción

    • Crea un objeto a través del constructor

      1. Un método tiene métodos privados y métodos compartidos, es decir, diferentes modificadores.

      2. Para el mismo nombre de método, diferentes listas de parámetros también son métodos diferentes.

        		// 获取指定类的class
        		Class cla = Class.forName("edu.xja.demo.GrilFirend");
                // 获取所有的public修饰的构造方法
                Constructor[] constructors = cla.getConstructors();
                // 获取public修饰的无参构造
                Constructor constructor = cla.getConstructor(null);
                // 获取public修饰的无参构造
                Constructor constructor1 = cla.getConstructor();
                // 获取所有构造方法
                Constructor[] declaredConstructors = cla.getDeclaredConstructors();
                // 获取特有的构造方法,如果有参数,传入参数的数据类型对应的class
                Constructor declaredConstructor = cla.getDeclaredConstructor(String.class);
                // 如果是私有方法,我们需要突破访问修饰符(暴力破解)
                declaredConstructor.setAccessible(true);
                // 通过构造方法的newInstance方法创建对象,如果该方法有参数,则需要 传递参数
                Object girlFirend = declaredConstructor.newInstance("迪丽热巴");
                System.out.println(girlFirend.toString());
        

      4. Obtenga el valor del atributo (campo)

      ? ? ¿Por qué no escribir primero el método de adquisición? ? Debido a que a veces nuestro método pasa parámetros, podemos usar el valor del atributo que obtuvimos primero como parámetro de entrada, ¡así que comenzamos obteniendo el valor del atributo!

      public class TestClass {
              
              
          // 这里数据类型必须是包装类型,可以参考java值传递!
          private static  final Long PHONE = 13298107421L;
          public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
              
              
              // 获取指定类的class
              Class cla = Class.forName("edu.xja.demo.GrilFirend");
              // 通过构造函数创建对象
              Object girlFirend = cla.getConstructor().newInstance();
              // 获取所有public修饰的属性
              Field[] fields = cla.getFields();
              // 获取指定属性名且被public修饰的属性
              Field sex = cla.getField("sex");
              // 获取所有属性
              Field[] declaredFields = cla.getDeclaredFields();
              // 获取任意修饰符指定名字的属性
              Field name = cla.getDeclaredField("name");
              // 如果是private修饰,我们需要越过检查(暴力破解)
              name.setAccessible(true);
              // 我们修改我们创建的girlFirend对象的属性值,得到我们特有的对象
              name.set(girlFirend,"仓老师");
              // TestClass是这个测试类
              Field phone1 = TestClass.class.getDeclaredField("PHONE");
              // Field这个类的modifiers属性是private修饰的int值
              Field modifiers = Field.class.getDeclaredField("modifiers");
              // 放开权限
              modifiers.setAccessible(true);
              // 设置本类的静态字段为可修改
              modifiers.setInt(phone1,phone1.getModifiers()&~Modifier.FINAL);
              // 设置静态变量
              phone1.set(null,123L);
              System.out.println(PHONE);
      
          }
      }
      

      5. Método de obtención (Método)

      		// 获取指定类的class
              Class cla = Class.forName("edu.xja.demo.GrilFirend");
              // 通过构造函数创建对象
              Object girlFirend = cla.getConstructor().newInstance();
              // 获得所有的public修饰的方法
              Method[] methods = cla.getMethods();
              // 获取指定名称的public修饰的方法
              Method getName = cla.getMethod("getName");
              // 获取所有的方法
              Method[] declaredMethods = cla.getDeclaredMethods();
              // 获取指定名字的方法
              Method method4 = cla.getDeclaredMethod("method4");
              // 方法执行
              method4.invoke(girlFirend);
              // 如果这个方法有入参,需要传入入参的class类型
              Method method3 = cla.getDeclaredMethod("method3", String.class);
              // 执行方法,记得传参
              method3.invoke(girlFirend,"日本老师");
              // 这个method1是private修饰的方法
              Method method1 = cla.getDeclaredMethod("method1");
              // 放开权限检查(暴力破解)
              method1.setAccessible(true);
              // 执行方法
              method1.invoke(girlFirend);
      

      6. Obtenga comentarios (aún no discutidos)

Supongo que te gusta

Origin blog.csdn.net/qq_44112474/article/details/108512986
Recomendado
Clasificación