Java reflection real application (finally explain the reflection)

Java reflection (reflect) real application (read parsing configuration file)


Requirements: We need to read the configuration file, and then dynamically create a connection driver (mysql or oracle) based on the configuration file information


  • Understand the requirements: the drive object cannot be created by new, because we don't know what parameters the user passes to us!

  • To solve the demand: the problem can only be solved by dynamically creating objects!

  • Summary of requirements: Use the dynamic principle of reflection to create corresponding objects based on specific parameters passed by users.


    The demand prototype is shown in the figure below (take the properties file as an example)

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

    The user gives the corresponding information, we return to the connection object! (Decoupling)


    How to solve the problem in 3 steps

    1. Create different driver classes

    2. Create a test class

    3. Configure different information to test the connection


    1. Create a driver class, take MySQL and Oracle as examples

    • Create a mysql driver class (the abbreviated code is only to highlight reflection ideas, the specific implementation is not written)

      /**
      * 模拟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";
          }
      }
      
    • Create an oracle driver (the abbreviated code is only to highlight reflection ideas, the specific implementation is not written)

      /**
      * 模拟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. Create a test class (focus on reflection), if you don’t understand the code here, please see the appendix (reflection knowledge) of this article.

/**
* 测试反射
**/
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. Simulate the configuration file, test the code!

  • When the user configuration properties information is mysql, as shown below.

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

    The print result is:

    获取mysql驱动:
    username:root
    password:123456
    url:jdbc:mysql://localhost:3306/test
    
    Process finished with exit code 0
    
  • When the user configures the information of properties as orcal, as shown below.

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

    The print result is:

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

Appendix (reflection knowledge)

This appendix explains through six aspects

  1. Reflection concept

  2. Get class

  3. Get constructor

  4. Get attributes

  5. How to obtain

  6. Get comments (not considered for the time being)


    reflection


1. The concept of reflection (reflect)

​ We cannot create objects dynamically in languages ​​like JavaScript. For example, such syntax is legal in js.

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

​ In our java, this kind of grammar is not possible, because our cognition cannot treat strings as java code!

​ The above js code can automatically create variables and other operations during running, we think it is dynamic. If our Java is dynamic, everything in Java will be an object. Let's take creating objects as an example.

​ Look at the shy and difficult definition of reflection: in java running state (not our manual new object), for any class, we can know all the methods and attributes of this class; for any object, we can call it Any method and attribute; the function of dynamically obtaining information and dynamically calling object methods is called Java's reflection mechanism.

​ Looking at the passage above, there are classmates and beautiful Chinese. Some classmates asked to speak human words, so let me talk about my cognition!

​ What is dynamic? What people say is that if you and the written object are called static, because it will not change, and there is no need to create new things in the running process, for example:

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

​ I have already created it, and I don’t need others to help me create it, but the disadvantage can be immediately reflected, such as this

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

​ Only logical ghosts can solve this problem. Since we are not sure what this class is, then we can define a variable! For example like this

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

​ As expected of you, but we must uniquely determine a class, otherwise, multiple class jvms will report errors! Then you think of the fully qualified class name

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

​ It's wrong, how does this string and the parameter-free structure () splicing together? This is a string as a whole, and java can't recognize it. Ok! We won't be sneaky. Let's take a look at the operating principle of jvm and understand it from the bottom!

​ I want an object, OK, new one. Wait... I haven't said what I want. I will give you a template, and you can create one according to this template!

I'm going to start writing templates. We give us what we probably need! If you want multiple objects, we can use this template to copy.

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 +
                '}';
    }
}

So where is this template? Because this template is shared, we put the compiled class object in the heap (jvm). And we create an object just use the class in the heap to create an object reference class file. And it will be modified on the basis of the class! Give an example to illustrate the difference and practice between class and object.


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());
    }
}
  • We can know that, let's assume that we can create objects based on class, and based on transformation, we can get the unique objects we need.

  • Our first step is to obtain the standard object, which is the class.

  • We transform the class to become what we want.


    2. Get class (in order to get standard objects)

    1. We know that the parent class of all classes is object, and object has a common method that returns a class, and its method is getClass. Then we can use it like this.

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

      [External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-BbqlLCEt-1599718235499)(C:\Users\13298\Desktop\object.PNG)]

      The reason why we can't create new objects is that when we create new objects, we don't need to create objects through reflection, which violates the principle of dynamically creating uncertain objects using classes.


    2. Any class, or interface, or enumeration, or basic type. All have an attribute called .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);
          }
      }
      

      In this way, we cannot decouple, nor can we create objects through strings, we need to import packages.


    3. We know that the underlying class loading mechanism of jvm knows that we can uniquely determine a class by getting the full path name of the class through classpath. Then there is this Class class that uses the mechanism of ClassLoader.getClassLoader to help us create an object ( commonly used ) based on the fully qualified class name .

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

      At this point, we have the class object, the class object. What is the use of class objects? We create objects based on the class, and we must know all the fields, methods, annotations and other information, because the class is compiled from the class. The class content inside will identify all class methods, class attributes, and modifiers of our class, etc. Class is a mirror that can map out all the information of the class and pass it to jvm.


    3. Create an object through the construction method (Constructor)

    • Get construction method

    • Create an object through the constructor

      1. A method has private methods and shared methods, that is, different modifiers.

      2. For the same method name, different parameter lists are also different methods.

        		// 获取指定类的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. Get the attribute value (Field)

      ​? ? Why not write the acquisition method first? ? , Because sometimes our method passes parameters, we may use the attribute value we got first as the input parameter, so we start from getting the attribute value!

      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. Obtaining method (Method)

      		// 获取指定类的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. Get comments (not discussed yet)

Guess you like

Origin blog.csdn.net/qq_44112474/article/details/108512986