新四季網

ioc的具體使用(老生再談IoC)

2023-04-15 19:44:49 2

IoC,Spring的核心理念之一,確實這是一個老生常談的東西。但是今天呢!又重新溫習之後,想再說說自己對IOC的一些想法。

IoC——Inversion of Control,控制反轉。要想理解IoC還是要從其本身出發,首先就控制而言,控制是對誰的控制——是對象的控制。其次,反轉是什麼的反轉或者說為什麼要稱做反轉——是對象控制權反轉。

對象控制,傳統的方式就是程式設計師通過new關鍵字的方式來生成一個對象,然後由程式設計師根據程序邏輯人為地控制對象的使用。從這裡出發,就可以很好地理解什麼是控制反轉了。

所謂控制反轉,就是將原本在程式設計師手中的對象創建和管理的權限交給了Spring IoC容器。也就是說,控制反轉就是要轉移程式設計師對對象的控制權,而在Spring當中的實現就是Spring IoC容器通過Xml或註解的描述生成或者獲取對象,再由IoC容器對這些Bean進行管理。

所以,理解IoC(控制反轉),就只需要記住,控制權由誰反轉給了誰。

IoC容器頂級IoC容器接口—BeanFactory

對於BeanFactory,它的重要性源自於所有IoC容器都是直接或者間接派生自它。雖然,它的功能不是很強大,但是從其源碼當中卻可以看出很多端倪。

public interface BeanFactory { /** 工廠Bean的前綴, 用於判斷獲取的是FactoryBean還是FactoryBean所產生的實例 下面會有詳細解釋 **/ String FACTORY_BEAN_PREFIX = "&"; /**通過name 獲取Bean**/ Object getBean(String name) throws BeansException; /**通過name和Class類型 獲取Bean**/ T getBean(String name, @Nullable Class requiredType) throws BeansException; /**通過name和構造參數,也就是可以指定調用某個構造方法 獲取Bean**/ Object getBean(String name, Object... args) throws BeansException; /**通過Class類型 獲取Bean**/ T getBean(Class requiredType) throws BeansException; /**通過Class類型和構造參數,同樣可以指定調用某個構造方法 獲取Bean**/ T getBean(Class requiredType, Object... args) throws BeansException; /**返回一個被ObjectProvider包裝的Bean**/ ObjectProvider getBeanProvider(Class requiredType); ObjectProvider getBeanProvider(ResolvableType requiredType); /**通過name判斷是否在容器中有這個Bean**/ boolean containsBean(String name); /**是否為單例**/ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /**是否為原型**/ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /**類型匹配否**/ boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class typeToMatch) throws NoSuchBeanDefinitionException; /**根據name找到Bean的Class類型**/ @Nullable Class getType(String name) throws NoSuchBeanDefinitionException; /**獲取此Bean之外的別名**/ String[] getAliases(String name);}

FACTORY_BEAN_PREFIX 在Spring當中,有一個叫做FactoryBean的接口,這個類有一個T getObject throws Exception;這樣的方法,這個方法會返回一個對象實例。對於這個接口的實現類而言,通過BeanFactory的getBean返回的Bean是實現類本身的實例,還是getObject的返回實例就在於有沒有前綴。有,返回FactoryBean;沒有,返回getObject的返回實例。

/**舉個簡單的例子 實現這樣一個FactoryBean 用這樣一個FactoryBean來創建一個我們需要的User**/@component("user")public class UserFactoryBean implements FactoryBean { @Autowired private User user; @Override public User getObject throws Exception { return user; } @Override public Class getObjectType { return user.getClass; }}

//測試方法public static void test1{ ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class); //沒有前綴 //得到的是User getObject throws Exception的返回值 User user = (User) ctx.getBean("user"); System.out.println(user); //有前綴 //得到的是UserFactoryBean的實例 UserFactoryBean userFactoryBean = (UserFactoryBean) ctx.getBean("&user"); System.out.println(userFactoryBean);}

這裡只是簡單的例子,用來說明FACTORY_Bean_PREFIX的作用,FactoryBean更具體的用法,可以參考工廠模式當中工廠的作用。

ObjectProvider 這是在spring4.3之後才出現的一個接口,它主要作用是解決注入時Bean不存在或者Bean存在多個時出現的異常情況。

//getIfAvailable可以解決容器中沒有userdao時的異常public class UserService{ private UserDao userDao; public UserService(ObjectProvider dao){ userDao = dao.getIfAvailable; }}//5.1之後可以通過流式處理來解決容器中存在多個userDao情況public class UserService{ private UserDao userDao; public UserService(ObjectProvider dao){ userDao = dao.orderedStream .findFirst .orElse(null) }}

核心容器—ApplicationContext

學習過Spring的人,對ApplicationContext都不會陌生。它是BeanFactory的子(準確的說應該是孫子)接口之一,而我們所使用到的大部分Spring IoC容器都是ApplicationContext的實現類。

Spring的源碼很龐大,也很複雜,所以建議學習的時候,從某幾個重點類開始,分析其繼承、擴展關係,以此橫向展開對Spring的認識。

這裡也就不再對ApplicationContext的各個繼承接口一一解釋了,API文檔裡面都有: ApplicationContext。對於ApplicationContext這個容器更多的是側重於對它的應用介紹,就是如何通過這個容器來獲取Bean。

通過一個簡單的例子來了解一下:

//普通的JavaBeanpublic class User { private Long id; private String name; private int age; /**getter,setter,toString**/}

//配置類,採用註解的形式來配置Bean@Configurationpublic class UserConfig { ) public User getBeanUser{ User user = new User; user.setId(1L); user.setName("klasdq1"); user.setAge(18); return user; }}

//測試類public class IocTest { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class); User user = ctx.getBean("user");// System.out.println(user); }}

@Configuration @Configuration這個註解的作用就在於它標示的類擁有一個或多個@Bean修飾的方法,這些方法會被Spring容器處理,然後用於生成Bean或者服務請求。

//@Configuration的源碼@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Configuration { @AliasFor( annotation = Component.class ) String value default ""; boolean proxyBeanMethods default true;}

從註解的源碼當中可以看出,它有兩個;一是value,用於為配置類聲明一個具體的Bean name。二是proxyBeanMethods,用於指定@Bean修飾的方法能否被代理。

@Bean 這個註解只用在方法上面,用於Spring容器管理生成Bean。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Bean { @AliasFor("name") String[] value default {}; @AliasFor("value") String[] name default {}; /** @deprecated */ @Deprecated Autowire autowire default Autowire.NO; boolean autowireCandidate default true; String initMethod default ""; String destroyMethod default "(inferred)";}

@Bean的參數中重要的就是name(value),其含義在於為Bean聲明具體的名稱,一個Bean可以有多個名稱,這也是為什麼BeanFactory中有一個getAliases方法。其他參數,看名字就知道什麼意圖,就不再多解釋了。

AnnotationConfigApplicationContext 這是ApplicationContext類的具體實現類之一,用於註解形式的Bean的生成。與之相對應的還有ClassPathXmlApplicationContext從XML文件中獲取Bean。Bean的裝配

在Spring當中對於Bean的裝配允許我們通過XML或者配置文件裝配Bean,但在Spring Boot中常用註解的形式,為了方便Spring Boot開發的需要,就不再使用XML的形式了。

直接看例子:

//配置JavaBean@Component("klasdq2")public class User { @Value("2") private Long id; @Value("klasdq2") private String name; @Value("19") private int age; /**getter,setter,toString**/}

//配置類掃描裝配Bean@Configuration@ComponentScanpublic class UserConfig {}

//測試類public class IocTest { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class); User user = (User) ctx.getBean("klasdq2"); System.out.println(user);}

@Component@Component的源碼很簡單:@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface Component { String value default ""; } 參數當中只有一個value,用於聲明Bean的名字(標識)。這裡又出現一個新的註解@Indexed,顧名思義這個註解就是增加一個索引,這是因為Spring Boot當中大量採用掃描的形式來裝配Bean之後,掃描的Bean越多,解析時間就越長,為了提高性能,在5.0版本的時候就引入了這樣一個註解。@Value

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Value { String value;}

這個註解可以用在欄位、方法、方法參數、註解上,通過一個表達式或者具體字符串為其傳入相應的值。@Value是一個功能非常強大的註解,建議對其多做了解。

其功能主要包括以下幾種:

注入普通字符串書寫SpEL表達式,如:@Value("#{person.name}"),可以從配置文件、Bean屬性、調用方法等等得到數據。注入Resource,如:@Value("classpath:com/demo/config.txt") 使用Resource類型接收注入URL資源,如:@Value("http://www.baidu.com") 使用Resource類型接收@ComponentScan

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Repeatable(ComponentScans.class)public @interface ComponentScan { /** * 這個參數是ComponetScan註解最常用的,其作用就是聲明掃描哪些包, * 通過掃描,將含有@Componet註解的Bean裝入Spring容器中。 * value和basePackages效果一樣,其默認值為配置類所在包及其子包。 **/ @AliasFor("basePackages") String[] value default {}; @AliasFor("value") String[] basePackages default {}; /**掃描哪些類**/ Class[] basePackageClasses default {}; /**Bean Name生成器:自定義bean的命名生成規則**/ Class nameGenerator default BeanNameGenerator.class; /**作用域解析器**/ Class scopeResolver default AnnotationScopeMetadataResolver.class; /**作用域代理**/ ScopedProxyMode scopedProxy default ScopedProxyMode.DEFAULT; /**資源的匹配模式,默認就是.Class**/ String resourcePattern default "**/*.class"; /**是否啟用默認過濾器(源碼下面自定義的過濾器)**/ boolean useDefaultFilters default true; /**符合過濾器條件的組件 才會掃描**/ ComponentScan.Filter[] includeFilters default {}; /**符合過濾器條件的組件 不會掃描**/ ComponentScan.Filter[] excludeFilters default {}; /**是否啟用懶加載**/ boolean lazyInit default false; /**過濾器**/ @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Filter { /**可以按照註解類型或者正則式過濾**/ FilterType type default FilterType.ANNOTATION; /**過濾哪些類**/ @AliasFor("classes") Class[] value default {}; @AliasFor("value") Class[] classes default {}; /**匹配方式**/ String[] pattern default {}; }}

例如:

@ComponetScan(basePackages="com.klasdq.sb.service.*",excludeFilters=(@Filter(classes="UtilService.Class")))

這樣的一個例子中,basePcakages指定了掃描service包下所有具體@Component註解的Service Bean(@Service包含了@Component)。而excludeFilters定義使用@Filter過濾掉UtilService.Class。其他的參數使用,可以參數API文檔中的介紹,大同小異。

@ComponetScans 這個註解也可以用於掃描組件,可以定義@ComponetScan,如:

@ComponentScans(value = { @ComponentScan(value = "com.klasdq.sb.service.*"), @ComponentScan(value = "com.klasdq.sb.dao.*",)) })

通過這樣一種方式來定義多個掃描組件,使得掃描更加精確。因為@ComponentScan(value="com.klasdq.sb.*")全包掃描的方式雖然寫起來簡單,但是耗費的時間代價卻是極大的。

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了