Simply simulate the application scenario of reflection in Java

Posted by blinks on Mon, 03 Jan 2022 13:18:34 +0100

Some people say that Java is a static language. So what is static language and what is dynamic language?

1. Dynamic language
It is a kind of language that can change its structure at run time: for example, new functions, objects and even code can be introduced, existing functions can be deleted or other structural changes. Generally speaking, the code can change its structure according to some conditions at run time. Main dynamic languages: Object-C, c#, JavaScript, PHP, Python, Erlang.
2. Static language
Corresponding to dynamic language, the language with immutable runtime structure is static language. Such as C, C + +.

Java is not a dynamic language, but it can not be simply said as a static language. Java can be called "quasi dynamic language". That is, Java has certain dynamics. We can use reflection mechanism and bytecode operation to obtain characteristics similar to dynamic language.
The dynamic nature of Java makes programming more flexible!

Therefore, we can "turn Java into" a dynamic language through reflection, that is, we can change the structure of the program runtime through reflection. Take a simple example to simulate:

package day_12_30.reflection_test;

import day_12_30.reflection.java.Person;

import java.util.Date;
import java.util.Random;

/**
 * @author soberw
 * @Classname RefTest
 * @Description Experience the dynamics of reflection
 * @Date 2021-12-30 20:56
 */
public class RefTest {

    public static void main(String[] args) throws Exception {
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int j = random.nextInt(3);
            Class clazz = switch (j) {
                case 1 -> User.class;
                case 2 -> Date.class;
                default -> Person.class;
            };
            Object o = clazz.getDeclaredConstructor().newInstance();
            System.out.println(o);
        }
    }
}

class User {
    private String name;
    private int age;
    public User() {}
    {
        this.age = 18;
        this.name = "wang";
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


Before the program runs, no one knows which class object the program will create.

Next, I will briefly give two application scenarios of Java reflection mechanism in actual development to help you better master reflection:
For example, when we connect to the database, with the change of requirements, we may use MySQL today and Oracle tomorrow, so we can't create objects frequently. It's too troublesome. At this time, we can use the reflection mechanism to design and implement, for example:
I have a connection interface:

public interface Connect {
    /**
     * Save database file
     */
    void safe();
}

There are two implementation classes:

public class MysqlConnect implements Connect{
    @Override
    public void safe() {
        System.out.println("Data saved in mysql in...");
    }
}

public class OracleConnect implements Connect{
    @Override
    public void safe() {
        System.out.println("Data saved in oracle in...");
    }
}

Now I want to switch which database to call at any time. I just need to declare a file and save the full class name, which is named bean properties


Then I create a factory class to call the database connection through the factory class:

package day_12_31.mybean;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

/**
 * @author soberw
 * @Classname MyBeanFactory
 * @Description
 * @Date 2021-12-31 9:00
 */
public class MyBeanFactory {
    private static Properties props;
    private static String className;
    static{
        String path = MyBeanFactory.class.getClassLoader().getResource("data/bean.properties").getPath();
        props = new Properties();
        try {
            props.load(new FileInputStream(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        className = props.getProperty("user");
    }

    public static Object getBean(){
        Class clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}

The tests are as follows:

public class FactoryTest {
    public static void main(String[] args) {
        Object bean = MyBeanFactory.getBean();
        Connect c = (Connect) bean;
        c.safe();
    }
}


At this time, if you want to change to mysql connection, just change it in the configuration file:

Of course, there are more than one method. As we all know, annotations are also very important in the advanced part of Java. Many frameworks use annotations a lot. For example, JPA is based on annotations and spring 2 5 the above is based on annotation, even to a certain extent: Framework = annotation + reflection + design pattern.
Moreover, using annotations is more efficient than configuration files, which meets the high cohesion and low coupling nature of factory design patterns.
Let me simply simulate:
First declare an annotation class:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
}

Write a new factory class:

package day_12_31.mybean;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author soberw
 * @Classname AnnotationFactory
 * @Description
 * @Date 2021-12-31 10:00
 */
public class AnnotationFactory {
    private static String packageName = "day_12_31.mybean";
    private static List<String> list = new ArrayList<>();

    static {
        StringBuilder path = new StringBuilder(AnnotationFactory.class.getClassLoader().getResource("").getPath());
        String[] split = packageName.split("[.]");
        for (String s : split) {
            path.append(s).append("/");
        }
        File file = new File(path.toString());
        File[] files = file.listFiles();
        for (File f : files) {
            String s = f.getName().substring(0, f.getName().lastIndexOf("."));
            String className = packageName + "." + s;
            list.add(className);
        }
    }

    public static Object getBean() {
        for (String s : list) {
            try {
                if (Class.forName(s).isAnnotationPresent(MyAnnotation.class)) {
                    return Class.forName(s).getDeclaredConstructor().newInstance();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

Test:

package day_12_31.mytest;

import day_12_31.mybean.AnnotationFactory;
import day_12_31.mybean.Connect;

/**
 * @author soberw
 * @Classname AnnnotationFactoryTest
 * @Description
 * @Date 2021-12-31 9:59
 */
public class AnnotationFactoryTest {
    public static void main(String[] args) {
        Object bean = AnnotationFactory.getBean();
        if (bean != null) {
            Connect c = (Connect) bean;
            c.safe();
        }else {
            System.out.println("Data saving failed...");
        }
    }
}


If we want to connect to which database, we can add annotations on the connection class, and there is no need to modify the configuration file:

So this is the application scenario of reflection in Java. Of course, in practice, its application scenario will be more extensive. Therefore, mastering reflection will be more handy in practical development!

Topics: Java Back-end