閉關修煉(八)反射機制
阿新 • • 發佈:2021-01-11
技術標籤:java閉關修煉
文章目錄
反射
什麼是反射機制?
反射機制就是正在執行(的程式中)動態獲取類的所有資訊。
反射機制的作用
-
反編譯:.class -> .java。
-
通過反射機制訪問物件屬性,方法,構造方法等。
-
使用java反射機制可以不用new來初始化類。
-
類私有屬性可以通過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建立物件
使用無參建構函式
實現思路
- 用dom4j解析xml
- 通過beanid查詢對應的xml節點,獲取class節點屬性
- 使用java反射機制newInstance類
- 使用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實現成功了
使用方便,但是初始化物件效率很低。