javascript
Spring注入方法
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
依賴注入的3種方式
? ? 1.在xml中顯式配置
? ? 2.在java中基于注解配置
? ? 3.隱式Bean的發(fā)現(xiàn)機(jī)制和自動(dòng)裝配原則
在現(xiàn)實(shí)工作中,這三種方式都會(huì)用到,并且經(jīng)常混合使用。建議優(yōu)先級(jí)如下?
(1)基于約定優(yōu)于配置的原則,最優(yōu)先的應(yīng)該是隱式Bean的發(fā)現(xiàn)機(jī)制和自動(dòng)裝配原則。這樣的好處是減少程序開(kāi)發(fā)者的決定權(quán),簡(jiǎn)單又不失靈活。?
(2)在沒(méi)有辦法使用自動(dòng)裝配原則的情況下應(yīng)該優(yōu)先考慮Java接口和類中實(shí)現(xiàn)配置。這樣的好處是避免xml配置的泛濫,也更為容易?
(3)上述方法都無(wú)法使用的情況下,那么只能選擇xml去配置spring IoC容器,比如第三方的類庫(kù)。
1.通過(guò)xml裝配
? ? ?1.1 通過(guò)setter方法配置
- id:spring找到這個(gè)Bean的編號(hào),不是一個(gè)必須的屬性,如果沒(méi)有指定,spring采用"權(quán)限定名“(類名首字母小寫)的格式生成編號(hào),id不支持特殊字符,且必須唯一。
- name:spring配置Bean的名稱,支持重復(fù)命名和特殊字符
- class:一個(gè)類的全限定名
- property:定義類中的屬性
? ? ?1.2 通過(guò)構(gòu)造器注入
<bean id="departmentService" class="com.wise.tiger.service.impl.DepartmentServiceImpl" init-method="init" destroy-method="destory"> <!--使用類構(gòu)造器實(shí)例化Bean --> <constructor-arg name="departmentName" value="生產(chǎn)部"/> </bean>constructor-arg用于定義類構(gòu)造方法的參數(shù),其中index用于定義參數(shù)的位置,而value則是設(shè)置值,也可以通過(guò)參數(shù)名name進(jìn)行注入。
? ? ?1.3 使用命名空間注入
<beans?xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean name="user" class="com.wise.tiger.User" p:name="jack" p:age="20" p:car-ref="car"></bean> <beans/>p:name代表構(gòu)造方法參數(shù)名為name的參數(shù),也可以采用p:_0表示構(gòu)造方法的第一個(gè)參數(shù)?
p:car-ref代表引用屬性
? ? ? 1.4 其他命名空間注入(如xmlns:c? ?xmlns:util)
<?xml?version="1.0"?encoding="UTF-8"?> <beans?xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans?https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util?https://www.springframework.org/schema/util/spring-util.xsd"> <util:list?name="emails"><value>pechorin@hero.org</value><value>raskolnikov@slums.org</value><value>stavrogin@gov.org</value><value>porfiry@gov.org</value> </util:list> <util:map?id="emails"><entry?key="pechorin"?value="pechorin@hero.org"/><entry?key="raskolnikov"?value="raskolnikov@slums.org"/><entry?key="stavrogin"?value="stavrogin@gov.org"/><entry?key="porfiry"?value="porfiry@gov.org"/> </util:map> <util:set?name="emails"><value>pechorin@hero.org</value><value>raskolnikov@slums.org</value><value>stavrogin@gov.org</value><value>porfiry@gov.org</value> </util:set> <util:properties?id="jdbcConfiguration"?location="classpath:jdbc-production.properties"/> </beans>2.通過(guò)注解裝配Bean
?? 在Spring中,提供了兩種方式來(lái)讓 Spring IoC容器發(fā)現(xiàn)Bean。
- 組件掃描:通過(guò)定義資源的方式,讓Spring IoC容器掃描對(duì)應(yīng)的包,從而把Bean裝配進(jìn)來(lái)。
- 自動(dòng)裝配:通過(guò)注解定義,使得一些依賴關(guān)系可以通過(guò)注解完成
? ? 2.1 使用@Component裝配Bean
? 首先打開(kāi)注解掃描開(kāi)關(guān)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd "><context:component-scan base-package="cn.itcast.bean"></context:component-scan></beans>再在需要設(shè)置為Bean的類上面添加@Component注解
@Component public?class?EmilSender?{ }注解@Component代表Spring IoC容器會(huì)把這個(gè)類掃描成Bean實(shí)例。而其中的value屬性代表這個(gè)實(shí)例在Spring中的id,相當(dāng)于xml方式定義的Bean的id,也可以簡(jiǎn)寫成@Component(""),也可以直接寫成@Component,id即為類的簡(jiǎn)單名稱首字母小寫。?
??? spring主要有四種注解可以注冊(cè)bean,每種注解可以任意使用,只是語(yǔ)義上有所差異:
- @Component:可以用于注冊(cè)所有bean
- @Repository:主要用于注冊(cè)dao層的bean
- @Controller:主要用于注冊(cè)控制層的bean
- @Service:主要用于注冊(cè)服務(wù)層的bean
? 2.2 自動(dòng)裝配@Autowired
? ?為了應(yīng)對(duì)這種明確的裝配場(chǎng)景,Spring提供了自動(dòng)裝配。
?? 當(dāng)涉及到自動(dòng)裝配Bean的依賴關(guān)系時(shí),Spring有多種處理方式。因此,Spring提供了4種自動(dòng)裝配策略。?
- no:不進(jìn)行自動(dòng)裝配,手動(dòng)設(shè)置Bean的依賴關(guān)系
- byName:根據(jù)Bean的名字進(jìn)行自動(dòng)裝配
- byType:根據(jù)Bean的類型進(jìn)行自動(dòng)裝配
- constructor:類似于byType,不過(guò)是應(yīng)用于構(gòu)造器的參數(shù),如果正好有一個(gè)Bean與構(gòu)造器的參數(shù)類型相同則可以自動(dòng)裝配,否則會(huì)導(dǎo)致錯(cuò)誤
- autodetect:如果有默認(rèn)的構(gòu)造器,則通過(guò)constructor的方式進(jìn)行自動(dòng)裝配,否則使用byType的方式進(jìn)行自動(dòng)裝配
@Autowired默認(rèn)按類裝配。當(dāng) Spring 容器啟動(dòng)時(shí),AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當(dāng)發(fā)現(xiàn) Bean 中擁有 @Autowired 注釋時(shí)就找到和其匹配的 Bean,并注入到對(duì)應(yīng)的地方中去。IoC容器有時(shí)候會(huì)尋找失敗,在默認(rèn)情況下失敗就會(huì)拋出異常,可以通過(guò)配置項(xiàng)required來(lái)改變它,比如:@Autowired(required=false)?
@Service public?class?PersonServiceImpl?implements?PersonSerivce?{ @Autowired(required=false) private?PersonDao?dao; }? 2.3 自動(dòng)裝配@Autowired的歧義性
? ? @Autowired僅有一個(gè)bean匹配所需的結(jié)果時(shí),自動(dòng)裝配才是有效的。如果符合條件的bean不只一個(gè),這時(shí)就會(huì)阻礙Spring自動(dòng)裝配屬性、構(gòu)造器參數(shù)或方法參數(shù)。?為了消除歧義性,Spring提供了兩個(gè)注解@Primary和@Qualifier。?
??? @Primary標(biāo)識(shí)首選的bean,某個(gè)接口有多個(gè)實(shí)現(xiàn)類,可在某個(gè)實(shí)現(xiàn)類上標(biāo)注@Primary,出現(xiàn)歧義時(shí),Spring會(huì)首選該bean,忽略其他的,但@Primary只能標(biāo)注在一個(gè)接口的一個(gè)實(shí)現(xiàn)類上
? ? ?以下三種方式:類、xml、方法使用primary
? ? 其次,可以使用@Qualifier("beanName")明確指定要注入的是哪個(gè)bean
public class BookServiceImplTest {@Autowired@Qualifier("bookService")private BookServiceImpl service; }? ?2.4?@Resource注解解決歧義(推薦使用)
@Resource(name="car")private Car car;? ? **2.5?裝配帶有參數(shù)的構(gòu)造方法類**
@Service public?class?PersonServiceImpl?{private?PersonDao?dao;public?PersonServiceImpl(@Autowired?PersonDao?dao)?{this.dao?=?dao;} }? ?@Autowired和@Qualifier這兩個(gè)注解可以支持到參數(shù)。?
3. 使用@Bean裝配Bean
? ? ?以上大部分都是通過(guò)@Component裝配Bean,但是@Component只能注解在類上,不能注解到方法上,@Bean可以注解到方法上,將方法返回的對(duì)象作為Spring的Bean存放在IoC容器中,如沒(méi)指定name,bean的id默認(rèn)為方法名。?
3.1 注解自定義Bean的初始化和銷毀方法
@Bean(name="",?initMethod="init",destroyMethod="destroy")@Bean的配置項(xiàng)中包含4個(gè)配置項(xiàng)。
- name:字符串?dāng)?shù)組,允許配置多個(gè)BeanName,沒(méi)有配置默認(rèn)為方法名。
- autowire:標(biāo)志是否是一個(gè)引用的Bean對(duì)象,默認(rèn)值是Autowire.NO
- initMethod:自定義初始化方法
- destroyMethod:自定義銷毀方法
**3.2 裝配的混合使用**
??????? spring-data.xml->使用xml配置數(shù)據(jù)源?
這種方式我們不需要去了解第三方的更多細(xì)節(jié),也不需要過(guò)多的java代碼,尤其是不用try...catch...finally...語(yǔ)句去處理它們,相對(duì)與@Bean的注入會(huì)更好一些,也更為簡(jiǎn)單,所以對(duì)于第三方的包或者其它外部的接口,建議使用xml的方式。?
??? spring同時(shí)支持這兩種形式的裝配,可以自由選擇,無(wú)論是xml還是注解都是將bean裝配到Spring IoC容器中,這樣就可以通過(guò)spring IoC容器去管理各類資源了,首先使用@ImportResource,引入spring-data.xml所定義的類容。
當(dāng)需要使用到數(shù)據(jù)源DataSource時(shí),就可以使用@Autowired或者@Resource進(jìn)行注入
?4.使用Profile
? ? ? 在軟件開(kāi)發(fā)過(guò)程中,敏捷開(kāi)發(fā)模式很常見(jiàn),一種時(shí)間控制的迭代式實(shí)現(xiàn)和發(fā)布軟件的方法。那么可能是開(kāi)發(fā)人員使用一套環(huán)境,而測(cè)試人員使用另外一套環(huán)境,而這兩天系統(tǒng)的數(shù)據(jù)庫(kù)是不一樣的,畢竟測(cè)試人員也需要花費(fèi)很多時(shí)間去構(gòu)建測(cè)試數(shù)據(jù),可不想老是被開(kāi)發(fā)人員修改那些測(cè)試數(shù)據(jù),這樣就有了在不同環(huán)境中進(jìn)行切換的需求了。spring也會(huì)對(duì)這樣的場(chǎng)景進(jìn)行支持。?
? ? 4.1使用@Profile配置
? ? 4.2.使用xml定義Profile?
<beans?xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <!--?other?bean?definitions?--> <beans?profile="development"> jdbc:embedded-database?id="dataSource" jdbc:script?location="classpath:com/bank/config/sql/schema.sql"/ jdbc:script?location="classpath:com/bank/config/sql/test-data.sql"/ </jdbc:embedded-database> </beans> <beans?profile="production"> jee:jndi-lookup?id="dataSource"?jndi-name="java:comp/env/jdbc/datasource"/ </beans> </beans>? ? 4.3 啟動(dòng)Profile?
? 當(dāng)啟動(dòng)java配置或者xml配置profile時(shí),Bean并不會(huì)被加載到IoC容器中。需要自行激活Profile。激活方法有5種
- 在使用SpringMVC的情況下可以配置Web上下文參數(shù),或者DispatchServlet參數(shù)
- 作為JNDI條目
- 配置環(huán)境變量
- 配置JVM啟動(dòng)參數(shù)
- 在集成測(cè)試環(huán)境中使用@ActiveProfiles
常用激活:?
??? 在測(cè)試代碼中激活Profile,如果是開(kāi)發(fā)人員進(jìn)行測(cè)試,那么可以使用注解@ActiveProfiles進(jìn)行定義
在測(cè)試代碼中可以加入@ActiveProfiles來(lái)指定加載哪個(gè)Profile,這樣程序就會(huì)自己去加載對(duì)應(yīng)的profile了。但是畢竟不是什么時(shí)候都是在測(cè)試代碼中運(yùn)行,有些時(shí)候要在服務(wù)器上運(yùn)行,那么這個(gè)時(shí)候可以配置java虛擬機(jī)的啟動(dòng)項(xiàng),關(guān)于指定profile的參數(shù)存在兩個(gè)。?
spring.profiles.active?啟動(dòng)的 spring.profiles.default?默認(rèn)的可以配置JVM的參數(shù)來(lái)啟動(dòng)對(duì)應(yīng)的Profile,比如需要啟動(dòng)test:
JAVA_OPTS="-Dspring.profiles.active=test"在大部分情況下需要啟動(dòng)web服務(wù)器,通常可以在 web.xml 中定義全局 servlet 上下文參數(shù) spring.profiles.default 實(shí)現(xiàn),代碼如下
<!--?配置spring的默認(rèn)profile?--> <context-param> <param-name>spring.profiles.default</param-name> <param-value>test</param-value> </context-param>**5 加載屬性(properties)文件**
? ? 在開(kāi)發(fā)過(guò)程中,配置文件往往就是那些屬性(properties)文件,比如:?
使用properties是十分常見(jiàn)的情景,可以有效減少硬編碼,有效提高運(yùn)維人員的操作便利性。
? ?**?5.1.使用注解方式加載屬性文件**
??? Spring提供了@PropertySource來(lái)加載屬性文件
- name:字符串,屬性配置的名稱
- value:字符串?dāng)?shù)組,可以配置多個(gè)屬性文件
- ignoreResourceNotFound:boolean值,默認(rèn)值為false:如果屬性文件沒(méi)有找到是否忽略處理。
- encoding:編碼
使用注解@Value和占位符去解析屬性占位符
@Configuration @ComponentScan(basePackages?=?"com.wise.tiger") @PropertySource(value?=?"classpath:dbcp-config.properties",ignoreResourceNotFound?=?true,encoding?=?"UTF-8") public?class?ApplicationConfig?{@Value("${url}")private?String?url;@Value("${username}")private?String?username;@Value("${password}")private?String?password;@Value("${driverClassName}")private?String?driverClassName;@Value("${maxTotal}")private?int?maxTotal;@Value("${maxWaitMillis}")private?long?maxWaitMillis;@Value("${maxIdle}")private?int?maxIdle;@Value("${defaultAutoCommit}")private?boolean?defaultAutoCommit;@Value("${connectionProperties}")private?String?connectionProperties;@Bean(name?=?"dataSource")public?DataSource?getDataSource()?{var?dataSource?=?new?BasicDataSource();dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setDriverClassName(driverClassName);dataSource.setPassword(password);dataSource.setMaxTotal(maxTotal);dataSource.setMaxIdle(maxIdle);dataSource.setMaxWaitMillis(maxWaitMillis);dataSource.setConnectionProperties(connectionProperties);return?dataSource;} }? ?**?5.2 使用xml方式加載屬性文件**
<context:property-placeholder?location="classpath:dbcp-config.properties"?ignore-resource-not-fount="true"/>? ? ignore-resource-not-fount屬性代表是否允許文件不存在,當(dāng)默認(rèn)值為false時(shí),不允許文件不存在,如果不存在,spring會(huì)拋出異常?
??? location是一個(gè)配置文件路徑的選項(xiàng),可以配置多個(gè)或者單個(gè)文件,多個(gè)文件之間用,分割。如果系統(tǒng)中存在很多文件,那么屬性location就要配置長(zhǎng)長(zhǎng)的字符串了,不過(guò)還有其它的xml方式可以進(jìn)行配置:?
6 條件化裝配Bean
? ? ?在某些條件下不需要去裝配Bean,比如當(dāng)屬性文件中沒(méi)有數(shù)據(jù)源的基礎(chǔ)配置的時(shí)候就不要去創(chuàng)建數(shù)據(jù)源。這時(shí)就需要通過(guò)條件化去判斷。Spring提供了@Conditional去配置,通過(guò)配置一個(gè)或多個(gè)類,只是這些類需要實(shí)現(xiàn)接口Condition。?
通過(guò)@Value往參數(shù)里注入了對(duì)應(yīng)屬性文件的配置,但是我們沒(méi)有辦法確定這些數(shù)據(jù)源連接池的屬性是否在屬性文件中已經(jīng)配置完整,如果是不充足的屬性配置,則會(huì)引起創(chuàng)建失敗,為此要判斷屬性文件的配置是否滿足才能繼續(xù)創(chuàng)建Bean。通過(guò)@Conditional去引入了一個(gè)條件判斷類----DataSourceCondition,由它來(lái)進(jìn)行判斷。
package?com.wise.tiger; import?org.springframework.context.annotation.Condition; import?org.springframework.context.annotation.ConditionContext; import?org.springframework.core.type.AnnotatedTypeMetadata; /** *?條件判斷,需要實(shí)現(xiàn)Condition接口 */ public?class?DataSourceCondition?implements?Condition?{ /** *?判斷屬性文件中是否配置了數(shù)據(jù)源的相關(guān)參數(shù) *?@param?context:通過(guò)它可以獲取spring的運(yùn)行環(huán)境 *?@param?metadata:通過(guò)它可以獲得關(guān)于該Bean的注解信息 *?@return?true:創(chuàng)建對(duì)應(yīng)的Bean,false:不會(huì)創(chuàng)建 */ @Overridepublic?boolean?matches(ConditionContext?context,?AnnotatedTypeMetadata?metadata)?{ //獲取Spring的運(yùn)行環(huán)境var?env?=?context.getEnvironment(); //判斷是否存在關(guān)于數(shù)據(jù)源的基礎(chǔ)配置return?env.containsProperty("driverClassName")&&?env.containsProperty("url")&&?env.containsProperty("username")&&?env.containsProperty("password");}}? ?6.1 Bean的作用域
?? 在默認(rèn)情況下,Spring IoC容器只會(huì)對(duì)一個(gè)Bean創(chuàng)建一個(gè)實(shí)例。bean可以定義為部署在多個(gè)作用域中的一個(gè)作用域中??梢圆捎?#64;Scope注解聲明Bean的作用域?
7 使用SPring表達(dá)式(Spring EL)
Spring還提供了更靈活的注入方式,那就是Spring表達(dá)式,Spring表達(dá)式語(yǔ)言(簡(jiǎn)稱spel)是一種功能強(qiáng)大的表達(dá)式語(yǔ)言,支持在運(yùn)行時(shí)查詢和操作對(duì)象圖。語(yǔ)言語(yǔ)法類似于統(tǒng)一的EL,但提供了其他特性,最顯著的是方法調(diào)用和基本的字符串模板功能:
- 使用Bean的id來(lái)引用Bean
- 調(diào)用指定對(duì)象的方法和訪問(wèn)對(duì)象的屬性
- 進(jìn)行運(yùn)算
- 提供正則表達(dá)式進(jìn)行匹配
- 集合配置
? ? 7.1 Spring EL相關(guān)類
ExpressionParser?parser?=?new?SpelExpressionParser(); Expression?exp?=?parser.parseExpression("'Hello?World'"); String?message?=?(String)?exp.getValue();//The?value?of?the?message?variable?is?'Hello?World'. exp?=?parser.parseExpression("'Hello?World'.concat('!')"); message?=?(String)?exp.getValue();?//The?value?of?message?is?now?'Hello?World!'. //?Create?and?set?a?calendar GregorianCalendar?c?=?new?GregorianCalendar(); c.set(1856,?7,?9); //?The?constructor?arguments?are?name,?birthday,?and?nationality. Inventor?tesla?=?new?Inventor("Nikola?Tesla",?c.getTime(),?"Serbian"); ExpressionParser?parser?=?new?SpelExpressionParser(); Expression?exp?=?parser.parseExpression("name"); String?name?=?(String)?exp.getValue(tesla); //?name?==?"Nikola?Tesla" exp?=?parser.parseExpression("name?==?'Nikola?Tesla'"); boolean?result?=?exp.getValue(tesla,?Boolean.class); //?result?==?true? ? 7.2 Bean的屬性和方法
???? 使用注解的方式需要用到@Value,在屬性文件的讀取中使用的是$,而在spring el中則使用#。
? ? ?7.3 使用類的靜態(tài)常量和方法,運(yùn)算
@Value("#{T(Math).PI}") private?double?pi; @Value("#{T(Math).random()?*?100}") private?int?random; @Value("#{person.getName()?.toString()}") private?String?note; @Value("#{person.getName()?:'peppa'}") private?String?defaultnote;?
轉(zhuǎn)載于:https://my.oschina.net/u/4134962/blog/3052922
總結(jié)
以上是生活随笔為你收集整理的Spring注入方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MongoDB进阶-内嵌文档查询
- 下一篇: Spring Cloud:使用Ribbo