1楼誓、IOC
概念:所謂控制反轉(zhuǎn),就是把原先我們代碼里面需要實(shí)現(xiàn)的對象創(chuàng)建名挥、依賴的代碼疟羹,反轉(zhuǎn)給容器來幫忙實(shí)現(xiàn)。當(dāng)應(yīng)用了IoC禀倔,一個對象依賴的其它對象會通過被動的方式傳遞進(jìn)來榄融,而不是這個對象自己創(chuàng)建或者查找依賴對象。
Spring 啟動時讀取應(yīng)用程序提供的Bean配置信息救湖,并在Spring容器中生成一份相應(yīng)的Bean配置注冊表愧杯,然后根據(jù)這張注冊表實(shí)例化Bean,裝配好Bean之間的依賴關(guān)系鞋既,為上層應(yīng)用提供準(zhǔn)備就緒的運(yùn)行環(huán)境力九。
1.1 底層實(shí)現(xiàn):
如果是xml文件,那么需要解析xml文件邑闺;如果是注解跌前,需要通過反射獲取注解,然后根據(jù)獲取到的bean信息通過反射實(shí)例化bean陡舅,實(shí)例化之后將bean放到spring容器的bean緩存池中(hashMap)抵乓,當(dāng)要使用bean時,可以通過applicationContext獲取bean(getBean)。
1.2 spring ioc autowired如何實(shí)現(xiàn)
@Autowired表示被修飾的類需要注入對象灾炭,spring會掃描所有被@Autowired標(biāo)注的類,然后根據(jù) 類型type 在ioc容器中找到匹配的類注入
@Autowired VS @Resource
- 提供方:@Autowired是由Spring提供茎芋;
@Resource是由javax.annotation.Resource提供,即J2EE提供咆贬,需要JDK1.6及以上败徊; - 注入方式:@Autowired只按照byType 注入;
@Resource默認(rèn)按byName自動注入掏缎,也提供按照byType 注入皱蹦;
1.3 @component
普通pojo實(shí)例化到spring容器中,相當(dāng)于配置文件中的<bean id="" class=""/>
雖然有了@Autowired,但是我們還是要寫一堆bean的配置文件眷蜈,相當(dāng)麻煩沪哺,而@Component就是告訴spring了袁,我是pojo類畦韭,把我注冊到容器中吧,spring會自動提取相關(guān)信息碌宴。那么我們就不用寫麻煩的xml配置文件了忌怎。
https://zhuanlan.zhihu.com/p/29344811
1.4 bean生命周期
- 1.4.1.當(dāng)調(diào)用者通過 getBean(beanName)向容器請求某一個 Bean 時籍滴,如果容器注冊了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口,在實(shí)例化 Bean 之前榴啸,將調(diào)用接口的 postProcessBeforeInstantiation()方法孽惰;
- 1.4.2.根據(jù)配置情況調(diào)用 Bean 構(gòu)造函數(shù)或工廠方法實(shí)例化 Bean;
- 1.4.3.如果容器注冊了 InstantiationAwareBeanPostProcessor 接口鸥印,在實(shí)例化 Bean 之后勋功,調(diào)用該接口的 postProcessAfterInstantiation()方法,可在這里對已經(jīng)實(shí)例化的對象進(jìn)行一些“梳妝打扮”库说;
- 1.4.4.如果 Bean 配置了屬性信息狂鞋,容器在這一步著手將配置值設(shè)置到 Bean 對應(yīng)的屬性中,不過在設(shè)置每個屬性之前將先調(diào)用InstantiationAwareBeanPostProcessor 接口的postProcessPropertyValues()方法潜的;
- 1.4.5.調(diào)用 Bean 的屬性設(shè)置方法設(shè)置屬性值骚揍;
- 1.4.6.如果 Bean 實(shí)現(xiàn)了 org.springframework.beans.factory.BeanNameAware 接口,將調(diào)用setBeanName()接口方法夏块,將配置文件中該 Bean 對應(yīng)的名稱設(shè)置到 Bean 中疏咐;
- 1.4.7.如果 Bean 實(shí)現(xiàn)了 org.springframework.beans.factory.BeanFactoryAware 接口,將調(diào)用 setBeanFactory()接口方法脐供,將 BeanFactory 容器實(shí)例設(shè)置到 Bean 中浑塞;
- 1.4.8.如果 BeanFactory 裝配了 org.springframework.beans.factory.config.BeanPostProcessor后處理器,將調(diào)用 BeanPostProcessor 的 Object postProcessBeforeInitialization(Object bean, String beanName)接口方法對 Bean 進(jìn)行加工操作政己。其中入?yún)?bean 是當(dāng)前正在處理的 Bean酌壕,而 beanName 是當(dāng)前 Bean 的配置名掏愁,返回的對象為加工處理后的 Bean。用戶可以使用該方法對某些 Bean 進(jìn)行特殊的處理卵牍,甚至改變 Bean 的行為果港, BeanPostProcessor 在 Spring 框架中占有重要的地位,為容器提供對 Bean 進(jìn)行后續(xù)加工處理的切入點(diǎn)糊昙, Spring 容器所提供的各種“神奇功能”(如 AOP辛掠,動態(tài)代理等)都通過 BeanPostProcessor 實(shí)施;
- 1.4.9.如果 Bean 實(shí)現(xiàn)了 InitializingBean 的接口释牺,將調(diào)用接口的 afterPropertiesSet()方法萝衩;
- 1.4.10.如果在<bean>通過 init-method 屬性定義了初始化方法,將執(zhí)行這個方法没咙;
- 1.4.11.BeanPostProcessor 后處理器定義了兩個方法:其一是 postProcessBeforeInitialization() 在第 8 步調(diào)用猩谊;其二是 Object postProcessAfterInitialization(Object bean, String beanName)方法,這個方法在此時調(diào)用祭刚,容器再次獲得對 Bean 進(jìn)行加工處理的機(jī)會牌捷;
- 1.4.12.如果在<bean>中指定 Bean 的作用范圍為 scope=“prototype”,將 Bean 返回給調(diào)用者涡驮,調(diào)用者負(fù)責(zé) Bean 后續(xù)生命的管理暗甥, Spring 不再管理這個 Bean 的生命周期。如果作用范圍設(shè)置為 scope=“singleton”捉捅,則將 Bean 放入到 Spring IoC 容器的緩存池中淋袖,并將 Bean引用返回給調(diào)用者, Spring 繼續(xù)對這些 Bean 進(jìn)行后續(xù)的生命管理锯梁;
- 1.4.13.對于 scope=“singleton”的 Bean,當(dāng)容器關(guān)閉時焰情,將觸發(fā) Spring 對 Bean 的后續(xù)生命周期的管理工作陌凳,首先如果 Bean 實(shí)現(xiàn)了 DisposableBean 接口,則將調(diào)用接口的afterPropertiesSet()方法内舟,可以在此編寫釋放資源合敦、記錄日志等操作;
- 1.4.14.對于 scope=“singleton”的 Bean验游,如果通過<bean>的 destroy-method 屬性指定了 Bean 的銷毀方法充岛, Spring 將執(zhí)行 Bean 的這個方法,完成 Bean 資源的釋放等操作耕蝉。
可以將這些方法大致劃分為三類:
Bean 自身的方法:如調(diào)用 Bean 構(gòu)造函數(shù)實(shí)例化 Bean垒在,調(diào)用 Setter 設(shè)置 Bean 的屬性值以及通過<bean>的 init-method 和 destroy-method 所指定的方法;
Bean 級生命周期接口方法:如 BeanNameAware、 BeanFactoryAware谈为、 InitializingBean 和 DisposableBean旅挤,這些接口方法由 Bean 類直接實(shí)現(xiàn)伞鲫;
容器級生命周期接口方法:在上圖中帶“★” 的步驟是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 這兩個接口實(shí)現(xiàn)粘茄,一般稱它們的實(shí)現(xiàn)類為“ 后處理器” 。 后處理器接口一般不由 Bean 本身實(shí)現(xiàn)秕脓,它們獨(dú)立于 Bean嘹朗,實(shí)現(xiàn)類以容器附加裝置的形式注冊到 Spring 容器中并通過接口反射為 Spring 容器預(yù)先識別。當(dāng)Spring 容器創(chuàng)建任何 Bean 的時候,這些后處理器都會發(fā)生作用,所以這些后處理器的影響是全局性的。當(dāng)然甫何,用戶可以通過合理地編寫后處理器遇伞,讓其僅對感興趣Bean 進(jìn)行加工處理渐排。
1.5 ApplicationContext 與 BeanFactory 區(qū)別
BeanFactory:是Spring里面最低層的接口次乓,提供了最簡單的容器的功能杏慰,只提供了實(shí)例化對象和拿對象的功能缘滥;
-
ApplicationContext:應(yīng)用上下文,繼承BeanFactory接口,它是Spring的一各更高級的容器,提供了更多的有用的功能搪柑;
- 國際化(MessageSource)
- 訪問資源,如URL和文件(ResourceLoader)
- 載入多個(有繼承關(guān)系)上下文 ,使得每一個上下文都專注于一個特定的層次舱权,比如應(yīng)用的web層
- 消息發(fā)送仑嗅、響應(yīng)機(jī)制(ApplicationEventPublisher)
- AOP(攔截器)
同時ApplicationContext會利用 Java 反射機(jī)制自動識別出配置文件中定義的 BeanPostProcessor、 InstantiationAwareBeanPostProcessor 和 BeanFactoryPostProcessor羡亩,并自動將它們注冊到應(yīng)用上下文中速侈;
而BeanFactory 需要在代碼中通過手工調(diào)用 addBeanPostProcessor()方法進(jìn)行注冊。
這也是為什么在應(yīng)用開發(fā)時乾蛤,我們普遍使用 ApplicationContext 而很少使用 BeanFactory 的原因之一每界。
1.6 Spring的bean的存儲和管理機(jī)制
1、讀取config.xml文件的bean標(biāo)簽放入數(shù)組家卖,讀取內(nèi)容包含的id和class眨层。
2、循環(huán)數(shù)組并根據(jù)class路徑利用反射機(jī)制實(shí)例化Bean(實(shí)例化bean的過程就是上面)上荡,并放入Map(spring容器中的Bean緩存池)趴樱。
3、根據(jù)傳入的BeanId獲取Map中對應(yīng)的bean實(shí)例酪捡。
1.7 Spring bean獲取方式
從上面我們知道bean是存儲在hashMap中的叁征,其中key是beanId,vlaue是bean實(shí)例逛薇;
bean獲取方式其實(shí)就是從應(yīng)用上下文ApplicationContext的HashMap中根據(jù)key獲取value的過程捺疼。
方式一:Object getBean(String name)
方式二:<T> T getBean(String name, Class<T> requiredType)
方式一和方式二的區(qū)別只是方式二自動做了類型轉(zhuǎn)換。
1.8 ApplicationContext 獲取方式
方式一:讀取xml文件獲取ApplicationContext對象
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
方式二:通過Spring提供的工具類獲取ApplicationContext對象
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
方式三:新建一個springUtils工具類永罚,且實(shí)現(xiàn)ApplicationContextAware接口
上面bean實(shí)例化時的第五步啤呼,如果bean實(shí)現(xiàn)了ApplicationContextAware接口卧秘,它的setApplicationContext()方法將被調(diào)用,將應(yīng)用上下文的引用傳入到bean中官扣;
通過這種方式可以把ApplicationContext傳入到springUtils中翅敌,然后再springUtils中就可以使用applicationContext.getBean()來獲取bean了
方式三是最推薦使用的
1.9 spring懶加載:
Spring默認(rèn)會在容器初始化的過程中,解析xml醇锚,并將單例的bean創(chuàng)建并保存到map中哼御,這樣的機(jī)制在bean比較少時問題不大,但一旦bean非常多時焊唬,spring需要在啟動的過程中花費(fèi)大量的時間來創(chuàng)建bean 花費(fèi)大量的空間存儲bean恋昼,但這些bean可能很久都用不上,這種在啟動時在時間和空間上的浪費(fèi)顯得非常的不值得赶促。
所以Spring提供了懶加載機(jī)制液肌。所謂的懶加載機(jī)制就是可以規(guī)定指定的bean不在啟動時立即創(chuàng)建,而是在后續(xù)第一次用到時才創(chuàng)建鸥滨,從而減輕在啟動過程中對時間和內(nèi)存的消耗嗦哆。
1.10 spring bean 注冊到 IOC容器的過程
- 1、讀取bean配置信息(通過xml中<bean> 婿滓,或者通過注解@Autowrite @Configuration)老速,根據(jù)配置信息,在容器內(nèi)部建立Bean定義注冊表凸主;
- 2橘券、根據(jù)bean注冊表實(shí)例化bean;
- 3卿吐、將bean實(shí)例化并將其放入hashMap旁舰;
- 4、獲取并使用bean
1.11 spring 循環(huán)依賴
spring 常用的注入方式有三種:構(gòu)造方法注入嗡官,setter注入箭窜,基于注解的注入。
所謂循環(huán)依賴衍腥,當(dāng)我們注入一個對象A時磺樱,需要注入對象A中標(biāo)記了某些注解的屬性,這些屬性也就是對象A的依賴婆咸,把對象A中的依賴都初始化完成竹捉,對象A才算是創(chuàng)建成功。那么擅耽,如果對象A中有個屬性是對象B,而且對象B中有個屬性是對象A物遇,那么對象A和對象B就算是循環(huán)依賴乖仇,如果不加處理憾儒,就會出現(xiàn):創(chuàng)建對象A-->處理A的依賴B-->創(chuàng)建對象B-->處理B的對象A-->創(chuàng)建對象A,這樣無限的循環(huán)下去乃沙,我們開發(fā)過程中有時也會遇到這種問題起趾,那么spring框架本身是怎么解決這個問題的呢?
從上面bean的生命周期中警儒,我們發(fā)現(xiàn)bean實(shí)例化(構(gòu)造器)和setter設(shè)置屬性并不是同時發(fā)生的训裆,這個其實(shí)就是解決循環(huán)依賴的依據(jù)。
當(dāng)我們初始化一個Bean時蜀铲,先調(diào)用Bean的構(gòu)造方法边琉,這個對象就在內(nèi)存中存在了(對象里面的依賴還沒有被注入),然后把這個對象保存下來记劝,當(dāng)循環(huán)依賴產(chǎn)生時变姨,直接拿到之前保存的對象,于是循環(huán)依賴就被終止了厌丑,依賴注入也就順利完成了定欧。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先存singletonObjects中獲取bean,
Object singletonObject = this.singletonObjects.get(beanName);
//如果bean不存在怒竿,并且bean正在創(chuàng)建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//從earlySingletonObjects中獲取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果earlySingletonObjects不存在(allowEarlyReference默認(rèn)為true)
if (singletonObject == null && allowEarlyReference) {
//獲取singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//從singletonFactories中獲取bean
singletonObject = singletonFactory.getObject();
//添加到earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
singletonObjects:緩存key = beanName, value = bean砍鸠;這里的bean是已經(jīng)創(chuàng)建完成的,該bean經(jīng)歷過實(shí)例化->屬性填充->初始化以及各類的后置處理耕驰。因此爷辱,一旦需要獲取bean時,我們第一時間就會尋找一級緩存
earlySingletonObjects:緩存key = beanName, value = bean耍属;這里跟一級緩存的區(qū)別在于托嚣,該緩存所獲取到的bean是提前曝光出來的,是還沒創(chuàng)建完成的厚骗。也就是說獲取到的bean只能確保已經(jīng)進(jìn)行了實(shí)例化示启,但是屬性填充跟初始化還沒有做完(AOP情況后續(xù)分析),因此該bean還沒創(chuàng)建完成领舰,僅僅能作為指針提前曝光夫嗓,被其他bean所引用
singletonFactories:該緩存key = beanName, value = beanFactory;在bean實(shí)例化完之后冲秽,屬性填充以及初始化之前舍咖,如果允許提前曝光,spring會將實(shí)例化后的bean提前曝光锉桑,也就是把該bean轉(zhuǎn)換成beanFactory并加入到三級緩存排霉。在需要引用提前曝光對象時再通過singletonFactory.getObject()獲取。
在整個getbean過程中民轴,singletonObjects攻柠、earlySingletonObjects球订、singletonFactories中對象變化如下
- 開始初始化對象A
singletonFactories:
earlySingletonObjects:
singletonObjects:
- 開始初始化對象A
- 調(diào)用A的構(gòu)造器實(shí)例化A,并把A放入singletonFactories
singletonFactories:A
earlySingletonObjects:
singletonObjects:
- 調(diào)用A的構(gòu)造器實(shí)例化A,并把A放入singletonFactories
- 開始注入A的依賴瑰钮,發(fā)現(xiàn)A依賴對象B之后冒滩,初始化對象B(同樣是調(diào)用B的構(gòu)造器實(shí)例化B,并把B放入singletonFactories)
singletonFactories:A,B
earlySingletonObjects:
singletonObjects:
- 開始注入A的依賴瑰钮,發(fā)現(xiàn)A依賴對象B之后冒滩,初始化對象B(同樣是調(diào)用B的構(gòu)造器實(shí)例化B,并把B放入singletonFactories)
- 開始注入B的依賴浪谴,發(fā)現(xiàn)B依賴對象A开睡,開始初始化對象A,發(fā)現(xiàn)A在singletonFactories里有苟耻,則直接獲取A篇恒,把A放入earlySingletonObjects(提前曝光),把A從singletonFactories刪除
singletonFactories:B
earlySingletonObjects:A
singletonObjects:
- 開始注入B的依賴浪谴,發(fā)現(xiàn)B依賴對象A开睡,開始初始化對象A,發(fā)現(xiàn)A在singletonFactories里有苟耻,則直接獲取A篇恒,把A放入earlySingletonObjects(提前曝光),把A從singletonFactories刪除
- 對象B的依賴注入完成之后梁呈,對象B創(chuàng)建完成婚度,把B放入singletonObjects,并且把B從earlySingletonObjects和singletonFactories中刪除
singletonFactories:
earlySingletonObjects:A
singletonObjects:B
- 對象B的依賴注入完成之后梁呈,對象B創(chuàng)建完成婚度,把B放入singletonObjects,并且把B從earlySingletonObjects和singletonFactories中刪除
- 繼續(xù)A的初始化官卡,把對象B注入給A蝗茁,繼續(xù)注入A的其他依賴,直到A注入完成
singletonFactories:
earlySingletonObjects:A
singletonObjects:B
- 繼續(xù)A的初始化官卡,把對象B注入給A蝗茁,繼續(xù)注入A的其他依賴,直到A注入完成
- 對象A創(chuàng)建完成寻咒,把A放入singletonObjects哮翘,并把A從earlySingletonObjects和singletonFactories中刪除
singletonFactories:
earlySingletonObjects:
singletonObjects:A,B
- 對象A創(chuàng)建完成寻咒,把A放入singletonObjects哮翘,并把A從earlySingletonObjects和singletonFactories中刪除
- 循環(huán)依賴處理結(jié)束,A和B都初始化和注入完成
singletonFactories:
earlySingletonObjects:
singletonObjects:A,B
- 循環(huán)依賴處理結(jié)束,A和B都初始化和注入完成
解決辦法:如果項目中出現(xiàn)了循環(huán)依賴毛秘,則使用setter注入替代構(gòu)造器注入饭寺。
2、AOP
2.1 概念
AOP的全稱是Aspect Orient Programming叫挟,即面向切面編程艰匙,擴(kuò)展功能不通過修改源代碼實(shí)現(xiàn)。
2.2實(shí)現(xiàn)方式
- 2.2.1 JDK 動態(tài)代理(必須有接口)
通過java.lang.reflect.Proxy類實(shí)現(xiàn)抹恳。
動態(tài)代理就是為了解決靜態(tài)代理不靈活的缺陷而產(chǎn)生的员凝。靜態(tài)代理是固定的,一旦確定了代碼奋献,如果委托類新增一個方法健霹,而這個方法又需要增強(qiáng),那么就必須在代理類里重寫一個帶增強(qiáng)的方法瓶蚂。而動態(tài)代理可以靈活替換代理方法糖埋,動態(tài)就是體現(xiàn)在這里。同時窃这,靜態(tài)代理每個方法都需要單獨(dú)寫一個代理類瞳别,而動態(tài)代理一個接口只實(shí)現(xiàn)一個動態(tài)代理類即可。
設(shè)計模式中,有一種
- 2.2.2 實(shí)現(xiàn)
Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new LogHandler(new Car()));
使用JDK的動態(tài)代理去生成代理只需要一行代碼祟敛,傳入的參數(shù)中其實(shí)就倆倍奢,一是被代理類的類對象,二是自定義的增強(qiáng)處理代碼垒棋。
從上面的例子中可以看出,動態(tài)代理除了接受Car類型的目標(biāo)對象痪宰,還可以接受任何其他類型的對象叼架;也不管目標(biāo)對象實(shí)現(xiàn)的接口有多少方法,都可以被代理衣撬。
public class LogHandler implements InvocationHandler{
private Object target;
public LogHandler(Object object){
super();
this.target = object;
}
//增強(qiáng)處理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = method.invoke(target,args);
return o;
}
}
- 2.2.2 cglib 動態(tài)代理(不需要類繼承任何接口乖订,字節(jié)碼技術(shù))
public class Plane {
public void fly(long ms) {
System.out.println("plane is flying!");
try {
Thread.sleep(ms);
} catch (Exception e) {
}
}
}
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//1. 實(shí)例化工具類
Enhancer en = new Enhancer();
//2. 設(shè)置父類對象
en.setSuperclass(this.target.getClass());
//3. 設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//4. 創(chuàng)建子類,也就是代理對象
return en.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before invoke ");
long begin = System.currentTimeMillis();
//執(zhí)行目標(biāo)對象的方法
Object returnValue = method.invoke(target, objects);
long end = System.currentTimeMillis();
System.out.println("after invoke elpased " + (end - begin));
return returnValue;
}
}
public static void main() {
CglibProxy cglibProxy = new CglibProxy(new Plane());
Plane plane = (Plane) cglibProxy.getProxyInstance();
plane.fly(150);
}
2.4使用場景:
- Logging 日志
- Authentication 權(quán)限
- Transactions 事務(wù)
- Context passing 內(nèi)容傳遞
- Error handling 錯誤處理
- Lazy loading 懶加載
- Debugging 調(diào)試
- logging具练,tracing乍构,profiling and monitoring 記錄跟蹤 優(yōu)化 校準(zhǔn)
- Performance optimization 性能優(yōu)化
- Persistence 持久化
- Resource pooling 資源池
- Synchronization 同步
- Caching 緩存
2.5 在Spring的AOP編程中
如果加入容器的目標(biāo)對象有實(shí)現(xiàn)接口,就使用JDK代理
如果目標(biāo)對象沒有實(shí)現(xiàn)接口扛点,就使用Cglib代理哥遮。
3、反射:
反射是Java的特征之一陵究,是一種間接操作目標(biāo)對象的機(jī)制眠饮,核心是JVM在運(yùn)行的時候才動態(tài)加載類,并且對于任意一個類铜邮,都能夠知道這個類的所有屬性和方法仪召,調(diào)用方法/訪問屬性,不需要提前在編譯期知道運(yùn)行的對象是誰松蒜,他允許運(yùn)行中的Java程序獲取類的信息扔茅,并且可以操作類或?qū)ο髢?nèi)部屬性。
程序中對象的類型一般都是在編譯期就確定下來的秸苗,而當(dāng)我們的程序在運(yùn)行時召娜,可能需要動態(tài)的加載一些類,這些類因為之前用不到难述,所以沒有加載到j(luò)vm萤晴,這時,使用Java反射機(jī)制可以在運(yùn)行期動態(tài)的創(chuàng)建對象并調(diào)用其屬性胁后,它是在運(yùn)行時根據(jù)需要才加載店读。
3.1 new和反射創(chuàng)建有什么區(qū)別
new:靜態(tài)編譯,在編譯期就將模塊編譯進(jìn)來攀芯,執(zhí)行該字節(jié)碼文件屯断,所有的模塊都被加載;
反射:動態(tài)編譯,編譯期沒有加載殖演,等到模塊被調(diào)用時才加載氧秘;
3.2 反射的作用
- 在運(yùn)行時判斷任意一個對象所屬的類;
- 在運(yùn)行時構(gòu)造任意一個類的對象趴久;
- 在運(yùn)行時判斷任意一個類所具有的成員變量和方法丸相;
- 在運(yùn)行時調(diào)用任意一個對象的方法;
3.3 反射的實(shí)現(xiàn)
要使用一個類彼棍,就要先把它加載到虛擬機(jī)中灭忠,生成一個Class對象。這個class對象就保存了這個類的一切信息座硕。
反射機(jī)制的實(shí)現(xiàn)弛作,就是獲取這個Class對象,通過Class對象去訪問類华匾、對象的元數(shù)據(jù)以及運(yùn)行時的數(shù)據(jù)映琳。
//通過反射機(jī)制創(chuàng)建class對象
class1 = Class.forName(className);
//在運(yùn)行時,通過創(chuàng)建的class對象蜘拉,獲取自己的父類信息
Class<?> parentClass = class1.getSuperclass();
//通過反射機(jī)制創(chuàng)建一個類的對象
Classname 對象=class1.newInstance(參數(shù));
//取得本類已聲明的所有字段萨西,包括私有的、保護(hù)的
Field[] field = class1.getDeclaredFields();
//返回一個 Method 對象旭旭,它反映此 Class 對象所表示的類或接口的指定公共成員方法原杂。
Method method = clazz.getMethod(方法名,參數(shù)類型);
//調(diào)用具體某個實(shí)例對象的這個公有方法
method.invoke(實(shí)例對象您机,參數(shù)值);