Spring IOC——Java反射

Spring IOC——Java反射

1 基礎

spring的技術核心之一是動態代理,而動態代理的核心是java反射。所以如果要學習spring框架,java反射是我們永遠也繞不開的一個坎,這是基礎。

2 反射定義

反射被視爲動態語言的關鍵,java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法

對於任意一個對象,都能夠調用它的任意方法和屬性

這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

3 反射概述

Class類java.lang.reflect類庫一起對反射的概念進行了支持,該類庫中包含了FieldMethodConstructor類(每個類都實現了Member接口)。

這些類型的對象是由JVM在運行時創建的,用以表示未知類裏對應的成員。

這樣就可以使用Constructor創建新的對象,用get()set()方法讀取修改Field對象關聯的字段

invoke()方法調用與Method對象關聯的方法

另外,還可以調用getFields()getMethod()getConstructor()等方法,返回表示字段方法構造器對象數組。這樣匿名對象的類信息就能在運行時被完全確定下來,而在編譯時不需要知道任何事情。

4 反射示例

4.1 如何獲取Class(加載一次)實例

  • 運行時類本身的.class屬性,如:Student.class
  • 通過運行時類的對象獲取,如:student.getClass()
  • 通過Class的靜態方法獲取,如:Class.forName("model.reflect.Student")
  • 通過類的加載器,如:classLoader.loadClass("model.reflect.Student");

Student類

public class Student {

    private String name;
    private String id;

    public Student() {

    }

    public Student(String name,String id) {
        this.id = id;
        this.name = name;
    }


    public void setName(String name) {
        this.name = name;
    }

    public void setId(String id) {
        this.id = id;
    }


    public String getName() {
        return name;
    }

    public String getId() {
        return id;
    }

    private String show(int index) {
        String show = null;
        switch (index){
            case 0:
                show = "我這樣show";
                break;
            case 1:
                show = "我那樣show";
                break;
            default:
                show = "我按平常show";
        }

        return show;

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                '}';
    }
}

反射測試類

public class StudentTest {

    //反射公有方法
    public static void reflectPublicMethod() throws Exception{
        Class<?> aClass = Class.forName("model.reflect.Student");

        Method setName = aClass.getMethod("setName", String.class);
        Method setId = aClass.getMethod("setId", String.class);

        //通過Class獲取對象的實例時,需要空的構造方法,權限也要足夠
        Object student = aClass.newInstance();

        setName.invoke(student, "fjx");
        setId.invoke(student, "20173106666");
        System.out.println("反射公有方法");
        System.out.println(student);
    }

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

public class StudentTest {

    //反射定製的構造方法
    public static void reflectConstructor() throws Exception{
        Class<?> aClass = Class.forName("model.reflect.Student");
        Constructor<?> constructor = aClass.getConstructor(String.class, String.class);
        Object student = constructor.newInstance("fjx","20173106666");
        System.out.println("反射定製的構造方法");
        System.out.println(student);
    }

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

反射私有方法(私有成員變量也大同小異)

public class StudentTest {
    //反射私有方法
    public static void reflectPrivateMethod() throws Exception {
        Class<?> aClass = Class.forName("model.reflect.Student");
        
        //注意使用getDeclaredMethod()方法,可以得到私有方法,得到私有屬性使用getDeclaredField()
        //getMethod()只能得到公有方法
        Method show = aClass.getDeclaredMethod("show", int.class);
        
        //如果沒有設置爲true,就不能訪問私有的方法或變量,拋出java.lang.IllegalAccessException
        show.setAccessible(true);

        Object student = aClass.newInstance();

        String showDes = (String)show.invoke(student,1);
        System.out.println("反射私有方法");
        System.out.println(showDes);
    }

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

錯誤演示

正確演示

5 反射相關類及相應方法

5.1 相關類

類名 用途
Class類 代表類的實體,在運行的Java應用程序中表示類和接口
Field類 代表類的成員變量(成員變量也稱爲類的屬性)
Method類 代表類的方法
Constructor類 代表類的構造方法
5.1.1 Class類
  • 獲得類相關方法
方法 用途
asSubclass(Class clazz) 把傳遞的類的對象轉換成代表其子類的對象
cast 把對象轉換成代表類或是接口的對象
getClassLoader() 獲得類的加載器
getClasses() 返回一個數組,數組中包含該類中所有公共類和接口類的對象
getDeclaredClasses() 返回一個數組,數組中包含該類中所有類和接口類的對象
forName(String className) 根據類名返回類的對象
getName() 獲得類的完整路徑名字
newInstance() 創建類的實例
getPackage() 獲得類的包
getSimpleName() 獲得類的名字
getSuperclass() 獲得當前類繼承的父類的名字
getInterfaces() 獲得當前類實現的類或是接口
  • 獲得類中屬性相關方法
方法 用途
getField(String name) 獲得某個公有的屬性對象
getFields() 獲得所有公有的屬性對象
getDeclaredField(String name) 獲得某個屬性對象
getDeclaredFields() 獲得所有屬性對象
  • 獲得類類中註解相關的方法
方法 用途
getAnnotation(Class annotationClass) 返回該類中與參數類型匹配的公有註解對象
getAnnotations() 返回該類所有的公有註解對象
getDeclaredAnnotation(Class annotationClass) 返回該類中與參數類型匹配的所有註解對象
getDeclaredAnnotations() 返回該類所有的註解對象
  • 獲得類中構造器相關的方法
方法 用途
getConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的公有構造方法
getConstructors() 獲得該類的所有公有構造方法
getDeclaredConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的構造方法
getDeclaredConstructors() 獲得該類所有構造方法
  • 獲得類中方法相關的方法
方法 用途
getMethod(String name, Class…<?> parameterTypes) 獲得該類某個公有的方法
getMethods() 獲得該類所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes) 獲得該類某個方法
getDeclaredMethods() 獲得該類所有方法
  • 類中其他重要的方法
方法 用途
isAnnotation() 如果是註解類型則返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定類型註解類型則返回true
isAnonymousClass() 如果是匿名類則返回true
isArray() 如果是一個數組類則返回true
isEnum() 如果是枚舉類則返回true
isInstance(Object obj) 如果obj是該類的實例則返回true
isInterface() 如果是接口類則返回true
isLocalClass() 如果是局部類則返回true
isMemberClass() 如果是內部類則返回true
5.1.2 Field類
方法 用途
equals(Object obj) 屬性與obj相等則返回true
get(Object obj) 獲得obj中對應的屬性值
set(Object obj, Object value) 設置obj中對應屬性值
5.1.3 Method類
方法 用途
invoke(Object obj, Object… args) 傳遞object對象及參數調用該對象對應的方法
5.1.4 Constructor類
方法 用途
newInstance(Object… initargs) 根據傳遞的參數創建類的對象

6 SpringIOC中的反射

從存儲着類信息的BeanDefinition中獲得相關的構造方法,通過反射創建實例

SimpleInstantiationStrategy

@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		// 如果不存在方法覆寫,那就使用 java 反射進行實例化,否則使用 CGLIB,
		// 方法覆寫 請參見附錄"方法注入"中對 lookup-method 和 replaced-method 的介紹
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}

			// 利用構造方法進行實例化
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			// 存在方法覆寫,利用 CGLIB 來完成實例化,需要依賴於 CGLIB 生成子類
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

BeanUtils

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		Assert.notNull(ctor, "Constructor must not be null");
		try {
			ReflectionUtils.makeAccessible(ctor);
			return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
					KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));//使用構造方法的newInstance反射創建實例
		}
		catch (InstantiationException ex) {
			throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
		}
		catch (IllegalAccessException ex) {
			throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
		}
		catch (IllegalArgumentException ex) {
			throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
		}
		catch (InvocationTargetException ex) {
			throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
		}
	}

7 參考

Java高級特性——反射

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章