1. 程式人生 > 其它 >閉關修煉(八)反射機制

閉關修煉(八)反射機制

技術標籤:java閉關修煉

文章目錄


反射

什麼是反射機制?

反射機制就是正在執行(的程式中)動態獲取類的所有資訊。

反射機制的作用

  1. 反編譯:.class -> .java。

  2. 通過反射機制訪問物件屬性,方法,構造方法等。

  3. 使用java反射機制可以不用new來初始化類。

  4. 類私有屬性可以通過java反射機制賦值。

提高程式的擴充套件性,封裝一些工具類,寫框架都會用到反射機制。

通過java反射機制建立物件例子

初始化類使用new建立物件,現在用反射機制來建立物件。

使用預設無參建構函式建立物件

package ch8;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;

@Data
@AllArgsConstructor
@NoArgsConstructor
class User{ private String username; private String password; } public class Test1 { @SneakyThrows public static void main(String[] args) { // forName中寫類的完整路徑,即包名+類名 Class<?> aClass = Class.forName("ch8.User"); // 使用預設無參建構函式建立物件 User user =
(User) aClass.newInstance(); System.out.println(user); } }

在這裡插入圖片描述

使用有參建構函式建立物件

package ch8;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;

import java.lang.reflect.Constructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
    private String username;
    private String password;
}
public class Test1 {
    @SneakyThrows
    public static void main(String[] args) {
        /*
        // forName中寫類的完整路徑,即包名+類名
        Class<?> aClass = Class.forName("ch8.User");
        // 使用預設無參建構函式建立物件
        User user = (User) aClass.newInstance();
        System.out.println(user);
        */
        // forName中寫類的完整路徑,即包名+類名
        Class<?> aClass = Class.forName("ch8.User");
        // 使用getConstructor獲取有參建構函式
        // 引數傳入有參建構函式的引數型別
        Constructor<?> constructor = aClass.getConstructor(String.class, String.class);
        // 建立物件
        User user2 = (User) constructor.newInstance("jack", "123");
        System.out.println(user2);
    }
}

怎麼禁止反射機制?

把建構函式改成private修飾,但是反射還是能通過設定訪問許可權禁止禁止反射

spring中的運用場景

寫標籤的時候經常遇到類似下面的格式
<bean id=“user” class=“com.java.bean.User”>
class值寫類的reference path,目的之一就是通過反射機制例項化物件

springIoc

jdbc中的運用場景

在載入jdbc驅動類的時候我們會寫:
class.forNmae(“com.mysql.jdbc.Driver”)

使用反射機制訪問方法和屬性

訪問屬性

package ch8;


import java.lang.reflect.Field;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 獲取類的位元組碼檔案
        Class<?> aClass = Class.forName("ch8.User");
        // 獲取當前類的所有屬性
        Field[] declaredFields = aClass.getDeclaredFields();
        // 遍歷
        for (Field field : declaredFields) {
            // 列印名稱
            System.out.println(field.getName());
        }

    }
}

在這裡插入圖片描述

訪問方法

package ch8;


import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 獲取類的位元組碼檔案
        Class<?> aClass = Class.forName("ch8.User");
        // 獲取類的所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        // 遍歷輸出
        for (Method m : declaredMethods){
            System.out.println(m.getName());
        }
    }
}

給私有屬性賦值

package ch8;


import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        // 獲取類的位元組碼檔案
        Class<?> aClass = Class.forName("ch8.User");
        // 例項化物件
        User user = (User) aClass.newInstance();
        // 獲取屬性
        Field username = aClass.getDeclaredField("username");
        // 設定許可權,允許操作私有屬性
        username.setAccessible(true);
        // 屬性賦值,第一個引數是類的物件, 第二個引數是賦的值
        username.set(user, "likeghee");
        // 獲取屬性
        Field password = aClass.getDeclaredField("password");
        // 設定許可權
        password.setAccessible(true);
        // 屬性賦值
        password.set(user, "1234");
        // 列印
        System.out.println(user);
    }
}

乞丐版springIOC實現

什麼是springIOC?

控制反轉,將bean與bean之間的關係交給第三方容器進行管理,不用自己手動建立物件。

springioc過程

載入springxml配置檔案
傳入beanid獲取bean物件

springioc建立物件

使用無參建構函式

實現思路

  1. 用dom4j解析xml
  2. 通過beanid查詢對應的xml節點,獲取class節點屬性
  3. 使用java反射機制newInstance類
  4. 使用java的反射機制給類屬性賦值

實現

新建一個Context.xml放到resources資料夾下,模擬spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="user1" class="myspringioc.User">
        <property name="username" value="ghee"/>
        <property name="password" value="123"/>
    </bean>
</beans>

新建ClassPathXmlApplicationContext類,實現它的getBean方法

package myspringioc;

import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Field;
import java.util.List;

public class ClassPathXmlApplicationContext {
    private static String PATH;
    private static String ID;
    private static String CLASS;
    private static String NAME;
    private static String VALUE;
    
    public void init() {
        ID = "id";
        CLASS = "class";
        NAME = "name";
        VALUE = "value";
    }


    // 建構函式
    public ClassPathXmlApplicationContext(String path) {
        PATH = path;
    }

    @SneakyThrows
    public Object getBean(String id) {
        init();

        /*
        1. 用dom4j解析xml
        2. 通過beanid查詢對應的xml節點,獲取class節點屬性
        3. 使用java反射機制newInstance類
        4. 使用java的反射機制給類屬性賦值
        */
        if (id.equals("")) {
            return null;
        }
        SAXReader saxReader = new SAXReader();
        // 讀取resources下的配置檔案
        Document read = saxReader.read(this.getClass().getClassLoader().getResource(PATH));
        // 遍歷二級節點,查詢beanid
        // 獲取根節點
        Element rootElement = read.getRootElement();
        // 獲取二級節點
        List<Element> elements = rootElement.elements();
        // 遍歷查詢
        for (Element e : elements) {
            // 獲取id屬性的值
            String beanId = e.attributeValue(ID);
            // 如果和傳入的id相等,找到bean
            if (!beanId.equals(id)) {
                // 如果不相等就跳出本次迴圈
                continue;
            }
            // 找到了beanid
            // 獲取class屬性值,也就是包PTAH
            String  attClassPath = e.attributeValue(CLASS);
            // 初始化bean
            Class<?> aClass = Class.forName(attClassPath);

            Object classObj = aClass.newInstance();

            // 獲取三級節點
            List<Element> propertyList = e.elements();
            for(Element property: propertyList){
                // 獲取name屬性的值
                String attNameValue = property.attributeValue(NAME);
                // 通過反射找到類中的屬性
                Field declaredField = aClass.getDeclaredField(attNameValue);
                // 獲取value屬性的值
                String attValueValue = property.attributeValue(VALUE);
                // 設定訪問許可權
                declaredField.setAccessible(true);
                // 類屬性賦值
                declaredField.set(classObj, attValueValue);
            }
            return classObj;
        }
        return null;
    }

}

測試程式碼

package myspringioc;


public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Context.xml");
        User user1 = (User) classPathXmlApplicationContext.getBean("user1");
        System.out.println(user1);
    }
}

乞丐版ioc實現成功了
在這裡插入圖片描述

使用方便,但是初始化物件效率很低。