[Java Basics] In-depth understanding of reflection and application of reflection (factory mode, proxy mode)

1. What is the Java reflection mechanism?

The Java reflection mechanism refers to the 运行时动态地获取和操作类的信息、调用对象的方法和访问对象的属性的能力. Through reflection, the structure, behavior, and state of classes can be analyzed and modified while the program is running.

The Java reflection mechanism provides the following features:

  1. Get class information: You can get the class name, modifiers, parent class, implemented interface, etc.
  2. Create objects: Objects can be instantiated through reflection, even if the specific class name is not known.
  3. Call method: You can get the methods of the class through reflection and call these methods.
  4. Access and modify fields: Field values ​​of classes can be obtained and set through reflection.
  5. Dynamic proxy: You can use reflection to dynamically generate proxy objects to implement AOP (aspect-oriented programming) and other functions.
  6. Manipulating arrays: Array objects can be created, accessed, and modified through reflection.
  7. Annotation processing: Annotations on classes, methods, and fields can be obtained through reflection and processed accordingly.

Through the reflection mechanism, it can be 运行时动态地操作类和对象,解耦了编译时的依赖关系,提供了更大的灵活性和扩展性。used, but it should be noted that since reflection involves dynamically generating and modifying the class structure, performance may be affected and additional permissions are required.

1.2 Java reflection example

Ortho:
Under normal circumstances, when we use a certain class, we will know the class and what to use it for. We can pass it directly new实例化创建对象, and then use this object to operate on the class. This belongs to the Orthographic ~
Student class

		public class Student {
    
    
		    private int id;
		
		    public void setId(int id) {
    
    
		        this.id = id;
		    }
		    public int getId() {
    
    
		        return this.id;
		    }
		}
		Student student = new Student();
        student.setId(1);
        System.out.println("正射获取ID :"+student.getId());
        
		//输出:正射获取ID :1

Orthography is easy to understand, so I won’t say much about it. Now let’s talk about reflection. You can also create objects, call object methods, and so on. What you can do with orthography can be done through reflection.
reflection:
Reflection is at the beginning and 不知道要初始化的是什么类, 无法使用new来实例化创建对象mainly through JDK提供的反射APIto achieve, only know what class to operate at runtime, and it can 获取到类的完整构造以及调用对应的方法, this is reflection~

		//1、通过 Class.forName 方法获取 Srudent类的 Class 对象
        Class<?> clz = Class.forName("Reflect.Student");  
		//2、通过 getConstructor 方法获取类的无参构造方法
      //  Constructor<?> constructor = clz.getConstructor();//可省略
        //3、通过 newInstance 方法实例化对象
       // Object stu = constructor.newInstance();//可省略
       Student stu = (Student) clz.newInstance();//(代替2-3)直接通过字节码对象Class.newInstance()实例化对象
		//4、通过 getMethod 方法获取类的方法信息("setId", int.class) setId为方法名  int.class为方法参数类型
        Method setId = clz.getMethod("setId", int.class);
        //5、通过 invoke 方法调用该方法
        setId.invoke(stu,3);
       //也可以直接通过对象stu   调用Student类的方法
        Method getId = clz.getMethod("getId");
       
        System.out.println("反射获取ID :"+getId.invoke(stu));
        
		//输出:反射获取ID :3

The summary is:
the calling process reflected in the above example, as you can see 获取一个类的反射对象, the main process is:

  1. Get the Class instance object of the class
  2. Get the Constructor object according to the Class instance object
  3. Then get the reflection object of the class according to the newInstance method of the Constructor object

获取到类的反射对象后,就可以对类进行操作了~ For example, the process of invoking the method of the class in the above example is:

  1. Obtain the Method object of the class according to the Class instance object

  2. Then call the method of the specific class according to the invoke method of the Method object

In the reverse call process of the above example, we Class.forName("类的全局定名")obtain the Class instance object of the class in this way. In addition to this, there are two other commonly used

2. What are the three ways to obtain Class in the Java reflection mechanism and their differences?

There are three common ways to obtain the java.lang.Class instance object of a class:

  1. By MyClass.classobtaining, MyClass here refers to the specific class~~
  2. Through Class.forName("类的全局定名")acquisition, the global name is package name + class name
  3. By new MyClass().getClass()obtaining, MyClass here refers to the specific class~
		Class<?> clz = Class.forName("Reflect.Student");		  //全类名获取
        Class<Student> clz = Student.class;                      //类.class获取
        Class<? extends Student> clz = new Student().getClass();//newClass().getClass()获取

The difference is that:

  • Obtained through MyClass.class, the JVM will use the ClassLoader class loader to load the class into memory, butDoes not do any class initialization work, returns java.lang.Class object
  • Obtained by Class.forName ("global naming of the class"), similarly, the class will be loaded into memory by the JVM, andIt will perform static initialization of the class and return the java.lang.Class object
  • Obtained by newMyClass().getClass(), this method usesnew is instantiated, so both static initialization and non-static initialization work, the getClass method belongs to the method in the top-level Object class, any subclass object can be called, which subclass calls, and returns the java.lang.Class object of that subclass

For specific manifestations, please refer to the link: Justin in my three provinces----->Java-reflection mechanism principle, several Class acquisition methods and application scenarios

Summarize:

  • MyClass.class不会做任何类的初始化工作
  • Class.forName will perform class静态初始化工作
  • new MyClass().getClass 静态初始化and 非静态初始化work will be done
  • Using any of these three methods will eventually be loaded into memory by the JVM 内存地址相同(the parental delegation mechanism of jvm class loading, the class will only be loaded once)

3. What are the application scenarios of the Java reflection mechanism?

3.1. Optimizing static factory mode (decoupling)

3.1.1 Before optimization (coupling of factory class and product class)

Simple factory example:

Step 1: Create an abstract product class

public interface Product {
    
    
      void show();
}

Step 2: Create a concrete product class:

public class ProductA implements Product {
    
    
    @Override
    public void show() {
    
    
        System.out.println("生产了产品A");
    }
}
public class ProductB implements Product {
    
    
    @Override
    public void show() {
    
    
        System.out.println("生产了产品B");
    }
}

public class ProductC implements Product {
    
    
    @Override
    public void show() {
    
    
        System.out.println("生产了产品C");
    }
}

Step 3: Create a simple factory class

/**
 * 静态工厂
 */
public class Product_factory {
    
    
	 /**
     * todo 常规工厂 (工厂和产品耦合)
     */
    public static Product createProduct(String productName) throws Exception {
    
    
        Product product = null;
        if ("A".equals(productName)) {
    
    
            product = new ProductA();

        }else if("B".equals(productName)){
    
    
            product = new ProductB();

        }else if("C".equals(productName)){
    
    
            product = new ProductC();

        }else{
    
    
            throw new Exception("没有该产品");
        }
        return product;
    }
}

Step 4: Call the simple factory class

    public static void main(String[] args) throws Exception {
    
    

        //通过工厂生产对象A
        Product A = Product_factory.createProduct("A");
        //调用A对象的方法
        A.show();

        Product B = Product_factory.createProduct("B");
        B.show();

        Product C = Product_factory.createProduct("C");
        C.show();

    }

output:

A产品被生产了
B产品被生产了
B产品被生产了

Disadvantages before optimization
Every time a subclass of an interface is added, the logic of the factory class must be modified

For example, if I need to add a product C, I must modify the factory and add the production process of product C

public class ProductD implements Product{
    
    
    @Override
    public void show() {
    
    
        System.out.println("D产品被生产了");
    }
}

insert image description here
This violates the principle of opening and closing (when adding new products, you should not modify the original existing code, but expand on the original basis)

At this time, reflection can overcome this disadvantage (decoupling products and factories)

3.1.2 After reflection optimization (factory class and product class decoupling)

Optimized

Optimize the factory class

    /**
     * todo 反射工厂(通过产品全类名来创建产品) (工厂和产品解耦合)
     */
    public static Product createProductReflect(String Full_product_name ) {
    
    
        Product product = null;
        try {
    
    
      	  //根据产品类的全类名反射生成产品类的class字节对象
            Class<?> aClass = Class.forName(Full_product_name);
            //通过产品类的字节码对象  创建真实对象
            product = (Product) aClass.newInstance();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        return product;
    }

Test class:

        //全类名反射通过工厂生产产品
        Product A = Product_factory.createProductReflect("factory.Simple_factory.ProductA");
        A.show();

        Product B = Product_factory.createProductReflect("factory.Simple_factory.ProductB");
        B.show();

        Product C = Product_factory.createProductReflect("factory.Simple_factory.ProductC");
        B.show();

output:

A产品被生产了
B产品被生产了
B产品被生产了

In this way, if you want to add product D, you only need to add product D without modifying the factory. You only need to introduce the full class name of the product to the factory to produce the product class object and add product D when product D is needed
.

public class ProductD implements Product{
    
    
    @Override
    public void show() {
    
    
        System.out.println("D产品被生产了");
    }
}

Produce product class objects by introducing the full class name of the product to the factory

  Product D = Product_factory.createProductReflect("factory.Simple_factory.ProductD");
  D.show();

output:

D产品被生产了

After using the Java reflection mechanism to optimize the simple factory model, you can see that, 不论具体产品类更新多频繁,都不需要再修改工厂类thus solving the problem of high operating cost and high system complexity of the ordinary simple factory model~

3.1.3 Re-optimize using reflection (configuration file configuration full class name mapping)

After the factory class of the simple factory mode is optimized by the Java reflection mechanism, there is still such a problem at this time, 子类的全局定名(包名+类名)是写死的but in fact, it is difficult for developers to predict the global naming of all subclasses in advance (package name + class name), so a second optimization is required~

Optimization idea:

Through the configuration file method, 统一定义类名对应全局定名(包名+类名),将配置文件存放到资源目录下, the program runs through ClassLoader类加载器动态获取到配置文件中定义的子类的全局定名~

Re-optimize step 2: Configure the class name corresponding to the global name (package name + class name)
to create the attribute configuration file Product.properties

//产品抽象类Product相关子类的全局定名(包名+类名)定义
//key            value
ProductA = com.justin.java.lang.ProductA
ProductB = com.justin.java.lang.ProductB
ProductC = com.justin.java.lang.ProductC

Re-optimize step 3: modify the calling factory class

public class FactoryTest {
    
    
    @Test
    public void test() throws IOException {
    
    
        ClassLoader classLoader = this.getClass().getClassLoader();
        Properties prop = new Properties();
        prop.load(classLoader.getResourceAsStream("Product.properties"));

        String className = "";
        try {
    
    
            className = prop.getProperty("ProductA");
            Product productA = Factory.getInstance(className);
            productA.show();
        } catch (NullPointerException e) {
    
    
            System.out.println("没有A这款产品,无法生产~");
        }

        try {
    
    
            className = prop.getProperty("ProductB");
            Product productA = Factory.getInstance(className);
            productA.show();
        } catch (NullPointerException e) {
    
    
            System.out.println("没有B这款产品,无法生产~");
        }

        try {
    
    
            className = prop.getProperty("ProductC");
            Product productA = Factory.getInstance(className);
            productA.show();
        } catch (NullPointerException e) {
    
    
            System.out.println("没有C这款产品,无法生产~");
        }
    }
}

output:

生产了产品A
生产了产品B
生产了产品C

Compared with before optimization, the full category name corresponding to the product category is placed in the configuration file. When producing products, the full category name corresponding to the product category is obtained from the configuration file according to the following configuration, and then according to the full category name Reflectively constructs the product object:

		ClassLoader classLoader = this.getClass().getClassLoader();
        Properties prop = new Properties();
        prop.load(classLoader.getResourceAsStream("Product.properties"));

classLoader.getResourceAsStream("Product.properties") It is to obtain the input stream of the resource file "Product.properties" through the class loader. The getResourceAsStream() method is a method of the java.lang.ClassLoader class that can根据给定的路径从类路径中查找并返回对应的资源文件的输入流。

prop.load()is a method of the java.util.Properties class, used 将输入流中的数据加载到属性对象中。in this line of code, where prop is a property object, by calling the prop.load() method, and putting类加载器获取到的资源文件输入流作为参数,实现将资源文件的内容加载到属性对象中。

Putting it all together, what this line of code does is使用类加载器加载名为 "Product.properties" 的资源文件,并将其读取为属性对象。这样可以方便地获取和操作资源文件中定义的属性值。

3.2 Dynamic Proxy Implementation in Proxy Mode

What is the proxy mode?
Proxy mode is a design mode,通过代理对象来访问目标对象,还可以在不修改目标对象的情况下,对代理对象进行拓展,增强目标对象的功能~

3.2.1 Static proxy

static proxyIt is in 编译时就已经确定代理类和被代理类的关系, the proxy class and the proxy class 实现同一个接口或继承同一个父类. The proxy class holds a reference to the proxied object (the proxy object), before or after invoking the target method 执行一些额外的逻辑. The code of the static proxy has been determined at compile time, so the proxy class needs to write a corresponding proxy class for each proxy class. The advantage of this method is that it is simple and intuitive, easy to understand and master, but when there are many proxied classes,会产生大量的重复代码。

That is, a proxy class corresponds to a proxy class, but when there are many proxy classes,会产生大量的重复代码。
insert image description here

In fact, in layman's terms, static proxy is 被代理类和代理类共同实现一个接口, and the method of implementing the interface, 代理类通过声明被代理类的实例化对象(代理对象)(that is, the relationship between the proxy class and the proxy class has been determined at compile time), 通过调用和被代理类一样的方法(this is why an interface method must be implemented together), and 在代理类方法中通过代理对象调用被代理类的方法(can In the method of the proxy class, the enhancement of the proxy class setting is made), so as to achieve the effect of proxy or proxy enhancement

3.2.2 Dynamic proxy

dynamic proxyis in 运行时生成代理类,不需要对每个被代理类都编写一个对应的代理类. It does this at runtime by using Java's reflection mechanism 动态地创建代理类和代理对象. 代理类实现一个统一的接口或继承一个父类, and holds an InvocationHandler object as its invocation processor. When calling the target method, 代理类会将方法调用转发给 InvocationHandler 处理器,并可以在调用之前或之后添加额外的逻辑the advantage of dynamic proxy is that it can create proxy objects more flexibly and dynamically, reducing repeated proxy class writing, and is suitable for scenarios where there are many proxy classes or dynamic management of proxy objects is required.

3.2.2.1 JDK dynamic proxy (reflection construction proxy object)

JDK native dynamic proxy, mainly using the JDK API
java.lang.reflect.Proxyand java.lang.relfect.InnvocationHandlerthese two classes to achieve~

通过java.lang.reflect.Proxy代理类的newProxyInstance方法, passing 3 parameters, namely:

  1. The loader of the target object is obtained by Object.getClass().getClassLoader
  2. The implementation interface type of the target object is obtained by Object.getClass().getInterfaces()
  3. The InnvocationHandler event handler is obtained by instantiating the object with new and rewriting the invoke method

step:

  1. Create the interface of the proxy class and let the proxy class implement the interface method
  2. Create a proxy class and inject the proxy class object into the proxy class through the constructor
  3. By setting 3 parameters (1. The loader of the target object, 2. The implementation interface type of the target object, 3. InnvocationHandler event handler (the method of enhancing the target object))
  4. Write a method to return a proxy object: pass in three parameters through the newProxyInstance method of the Proxy proxy class, and return the generated proxy object.
  5. In the test class, the proxy class object is introduced through the proxy class parameter construction, and then the proxy object is generated to execute the enhanced method

Example:
Product class interfaceProduct_interface

/**
 * 被代理类接口
 */
public interface Product_interface {
    
    
    void sell();
}

product categoryProduct

/**
 * @Description TODO 被代理类
 **/
public class Product implements Product_interface{
    
    
    @Override
    public void sell() {
    
    
        System.out.println("生产了一台iphone 15 pro max");
    }
}

Proxy classProductProxy

/**
 * @Description TODO 动态代理类
 **/
public class ProductProxy {
    
    
    private  Object  target;//被代理的对象

    public ProductProxy (Object target){
    
    //通过构造方法引入被代理对象
        this.target = target;
    }

    /**
     * 利用JDK API获取到代理对象
     * @return
     */
    public Object getProxyInstance(){
    
    
        //目标对象的加载器
        ClassLoader classLoader = target.getClass().getClassLoader();//反射

        //目标对象的实现接口类型
        Class<?>[] interfaces = target.getClass().getInterfaces();//反射

        //InvocationHandler事件处理器实例对象
        InvocationHandler invocationHandler = new InvocationHandler() {
    
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                //前置增强
               System.out.println("提前接到消息的黄牛正在蹲抢中.............");
                // 执行目标对象方法
                Object value = method.invoke(target, args);
                //后置增强
                System.out.println("无货......................");
                return null;//若无返回值  就直接返回   若需要返回一个返回值  就如实返回
            }
        };
        //传入3个参数,创建代理类的实例对象,并返回
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

test classProductBuyTest

    public static void main(String[] args) {
    
    

        Product_interface product = new Product();//创建被代理类对象

        ProductProxy productProxy = new ProductProxy(product);//将被代理类的对象交给代理类

        Product_interface proxy = (Product_interface) productProxy.getProxyInstance();//由代理类生成代理对象

        proxy.sell();//通过代理对象执行被代理类的增强方法

    }

output:

提前接到消息的黄牛正在蹲抢中.............
富士康生产了一台iphone 15 pro max
无货......................

In the JDK native dynamic proxy, in the process of obtaining the proxy example object, the class loader of the target object is obtained, and it is target.getClass().getClassLoader获取到目标对象的类加载器realized target.getClass()方式获取目标对象的Class实例对象by using the Java reflection mechanism~

3.2.2.2 cglib dynamic proxy (without reflection)

CGLIB (Code Generation Library) is one 基于ASM(一个Java字节码操作框架)的代码生成库, it can be in 运行时动态地生成目标类的子类, thus 实现对目标类的代理.

Need to introduce cglib dependency when using

      <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

product categoryProduct

/**
 *  被代理类
 */
public class Product {
    
    

    public void sell() {
    
    
        System.out.println("富士康生产了一台iphone 15 pro max");
    }
}

Proxy classCglibProxy

/**
 * @Description: 代理类  用来获取代理对象
 */
public class CglibProxy implements MethodInterceptor {
    
    

    private  Object  target;//被代理的对象

    public CglibProxy (Object target){
    
    //通过构造方法引入被代理对象
        this.target = target;
    }

    /**
     * 用于构造代理对象
     * @return
     */
    public Object getProxyObject() {
    
    
        //创建Enhancer对象,类似于JDK代理中的Proxy类
        Enhancer enhancer = new Enhancer();
        //设置父类的字节码对象。指定父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        Object proxyObject =  enhancer.create();
        return proxyObject;
    }
    /*
     * 拦截器
     *       	1.目标对象的方法调用
     *       	2.行为增强
     *      参数 o: cglib 动态生成的代理类的实例
     *          method:实体类所调用的都被代理的方法的引用
     *          objects 参数列表
     *          methodProxy:生成的代理类对方法的代理引用
     * */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        //前置增强
        System.out.println("提前接到消息的黄牛正在蹲抢中.............");
        //要调用目标对象的方法
        Object obj = method.invoke(target, objects);
        //后置增强
        System.out.println("无货......................");
        return null;
    }
}

output:

提前接到消息的黄牛正在蹲抢中.............
富士康生产了一台iphone 15 pro max
无货......................

Implementation steps:

  1. Introduce cglib dependency
  2. Create proxy class
  3. Create a cglib proxy class and implement the MethodInterceptor interface, rewriting the intercept method
  4. Inject the proxy class object through the construction method to assign the proxy object
  5. Write a method to return a proxy object:
    1. Create an Enhancer object,
    2. Set the bytecode object of the parent class (the proxy class) for the Enhancer object,
    3. Set a callback function for the Enhancer object,
    4. Create a proxy object and return the proxy object
  6. Call the method of the target object in the intercept method (enhanced)
  7. In the test class, the proxy class object is introduced through the proxy class parameter construction, and then the proxy object is generated to execute the enhanced method

The reference comes from:
Java – Reflection Mechanism Principle, Several Class Acquisition Methods and Application Scenarios—Author: Justin Wuri Sansheng

Guess you like

Origin blog.csdn.net/weixin_45618869/article/details/132623197