Spring中IOC和AOP的核心組成架構(gòu)詳解
IoC配置
注冊(cè)bean
XML
<bean> 標(biāo)簽
屬性
- id
- class
- init-method
- destroy-method
- scope
子標(biāo)簽
- <constructor-arg>:構(gòu)造器注入 --> c命名空間注入
- <property>:set方法注入 --> p命名空間注入
注解
- @Component:注解在類(lèi)上,配合自動(dòng)掃描(@ComponentScan),完成注冊(cè)
- @Controller
- @Service
- @Repository
- @Bean:注解在方法上,方法的返回值對(duì)象會(huì)注冊(cè)到Spring容器(常用于創(chuàng)建第三方j(luò)ar包中的類(lèi)對(duì)象)。這些被@Bean標(biāo)注的方法需要放到Spring配置類(lèi)當(dāng)中(被@Configuration標(biāo)注的類(lèi))
其他注解
- @ComponentScan:?jiǎn)⒂米詣?dòng)掃描,掃描指定包下帶有@Component注解的類(lèi)(對(duì)應(yīng)XML中的<context:component-scan>)
- @Configuration:將某個(gè)類(lèi)標(biāo)記為配置類(lèi)
- @Import:組合多個(gè)配置類(lèi)
- @Scope:指定bean的作用范圍。與@Component或@Bean組合使用。值是字符串,可以用ConfigurableBeanFactory中的常量,或WebApplicationContext中的常量
- @PostContruct:自定義初始化函數(shù),約等XML的init-method。(也可通過(guò)InitializingBean接口實(shí)現(xiàn))
- @PreDestroy:自定義銷(xiāo)毀函數(shù),約等于XML的destroy-method。(也可通過(guò)DisposableBean接口實(shí)現(xiàn))
裝配bean
@Autowired
屬于Spring,按類(lèi)型注入, required 屬性默認(rèn)為 true 。由Spring中的 AutowiredAnnotationBeanPostProcessor 進(jìn)行處理。
注:帶 @Autowired 注解的方法,會(huì)被Spring自動(dòng)調(diào)用。
@Resource
屬于JavaEE(JSR250),當(dāng)指定了 name 屬性時(shí),按照名稱( bean 的 id )注入;否則,按照類(lèi)型注入
@Inject
屬于JavaEE(JSR330),需導(dǎo)入額外的 jar 包,按照類(lèi)型注入
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
@Value
用來(lái)注入基本類(lèi)型和 String 類(lèi)型的值。
可以用 ${} ,來(lái)獲取 properties 文件中的屬性。( properties 文件要加載到Spring中)
關(guān)于把 properties 文件加載到Spring
- 創(chuàng)建一個(gè) PropertyPlaceholderConfigurer 對(duì)象到Spring容器中,設(shè)置 location 的值為 properties 文件的路徑
可通過(guò) <context:property-placeholder> 標(biāo)簽進(jìn)行簡(jiǎn)化
- @PropertySource
用 @PropertySource 結(jié)合 @Component 或者 @Configuration
注意: properties 文件中的屬性,會(huì)被加載到Spring的Environment對(duì)象中,多個(gè) properties 中的同名屬性,會(huì)發(fā)生覆蓋
高級(jí)裝配
環(huán)境與Profile
@Profile 與 @Configuration 或 @Component , @Bean 等結(jié)合使用。
可以控制僅在某一 profile 下激活對(duì)應(yīng)的類(lèi)。如下面的配置類(lèi)僅在 dev 環(huán)境下生效
@Profile("dev") @Configuration public class DevConfig { @Bean public PetService petService() { return new PetService(); } }
如何指定當(dāng)前激活的 profile ?
依賴于2個(gè)獨(dú)立屬性: spring.profiles.active 和 spring.profiles.default 。
若設(shè)置前者,則根據(jù)前者來(lái)確定;若沒(méi)設(shè)置前者,則根據(jù)后者來(lái)確定。若2者都未指定,則只會(huì)激活那些沒(méi)有定義在某一 profile 下的類(lèi)
有多種方式來(lái)設(shè)置這2個(gè)屬性
- 作為環(huán)境變量(如在maven的profile標(biāo)簽下配置上述2個(gè)屬性,并在maven打包時(shí)用 -P 參數(shù)指定maven的profile)
<profiles> <!-- 開(kāi)發(fā)環(huán)境 --> <profile> <id>dev</id> <properties> <spring.profiles.active>dev</spring.profiles.active> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <!-- 生產(chǎn)環(huán)境 --> <profile> <id>prod</id> <properties> <spring.profiles.active>prod</spring.profiles.active> </properties> </profile> </profiles>
- 作為Web應(yīng)用的上下文參數(shù)(SpringMvc)
<!-- web,xml 中 --> <!-- 省略其他部分 --> <context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param>
作為DispatcherServlet的啟動(dòng)參數(shù)(SpringMvc)
- 在 web.xml 中配置 servlet 時(shí),通過(guò) <init-param> 指定
- 集成測(cè)試時(shí),在測(cè)試類(lèi)上用 @ActiveProfiles 注解進(jìn)行指定
- 通過(guò)JVM啟動(dòng)參數(shù) -Dspring.profiles.active=dev
條件化bean
在滿足某些條件時(shí),才創(chuàng)建某些 bean 。通過(guò) @Conditional 注解和 Condition 接口實(shí)現(xiàn),示例如下
package org.demo.pet.conditional; public class MagicBean { }
package org.demo.pet.conditional; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class MagicCondition implements Condition { /** * 若 matches 方法返回 true, 則表示條件滿足, 會(huì)創(chuàng)建對(duì)應(yīng)的bean; 若返回 false, 則不會(huì)創(chuàng)建對(duì)應(yīng)bean * **/ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 當(dāng)Spring的Environment中存在名為 magic 的屬性, 才認(rèn)為滿足條件 return context.getEnvironment().containsProperty("magic"); } }
package org.demo.pet.conditional; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class ConditionalConfig { @Conditional(MagicCondition.class) @Bean public MagicBean magicBean() { return new MagicBean(); } }
測(cè)試
package org.demo.test.condition; import org.demo.pet.conditional.ConditionalConfig; import org.demo.pet.conditional.MagicBean; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = ConditionalConfig.class) public class ConditionTest { @Autowired(required = false) private MagicBean magicBean; @Test public void test() { System.out.println(magicBean == null); } }
由于 magic 屬性在Spring中不存在,所以上面的 magicBean == null 表達(dá)式值為 true ,如下
若添加 magic 屬性,則 MagicBean 會(huì)被創(chuàng)建
# magic.properties magic=HarryPotter
package org.demo.pet.conditional; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource("classpath:magic.properties") public class ConditionalConfig { @Conditional(MagicCondition.class) @Bean public MagicBean magicBean() { return new MagicBean(); } }
再次運(yùn)行測(cè)試, magicBean == null 表達(dá)式值為 false
Condition 接口中的 matches 方法提供了2個(gè)參數(shù): ConditionContext 和 AnnotatedTypedMetadata
通過(guò) ConditionContext
- 借助getRegistry方法,可以檢查bean的定義
- 借助getBeanFactory方法,可以檢查bean是否存在,檢查bean的屬性等
- 借助getEnvironment方法,檢查環(huán)境變量等
- …
通過(guò) AnnotatedTypedMetadata ,可以檢查注解的情況
Spring自帶的的 @Profile 注解,也是通過(guò) @Conditional 這種方式實(shí)現(xiàn)的
處理歧義性
依賴注入時(shí),當(dāng)同一個(gè)類(lèi)型,有多個(gè)候選 bean 時(shí),如何解決?
- 將其中一個(gè) bean 標(biāo)記為首選
使用 @Primary 注解( @Primary 與 @Component 搭配,或 @Primary 與 @Bean 搭配)(只能有一個(gè)首選 bean ,當(dāng)出現(xiàn)多個(gè)首選 bean 時(shí),Spring仍然不知道該注入哪一個(gè))
- 使用限定符
@Qualifier 是主要的限定符使用方式,可以搭配 @Autowired ,或者 @Inject 。
一個(gè) bean 的限定符,默認(rèn)情況下是這個(gè) bean 的 id ;當(dāng)然,也可以為 bean 設(shè)置自定義的限定符,如
@Component @Qualifier("cold") // 也可以與@Bean注解結(jié)合使用 public class IceCream implements Dessert { }
一個(gè) bean 可以有多個(gè)限定符,不同的 bean 可以有相同的限定符。當(dāng)依賴注入時(shí),如果通過(guò)一個(gè)限定符仍然能匹配多個(gè)候選者,那么可以指定更多的限定符,如
@Component @Qualifier("cold") @Qualifier("creamy") public class IceCream implements Dessert { }
@Component @Qualifier("cold") public class Popsicle implements Dessert { }
@Autowired @Qualifier("cold") @Qualifier("creamy") public void setDessert(Dessert dessert) {}
注:Java 8 之前不允許出現(xiàn)重復(fù)注解,Java 8以后,當(dāng)這個(gè)注解被標(biāo)記為 @Repeatable 時(shí),可以重復(fù)。
可以創(chuàng)建自定義的限定符注解來(lái)解決這個(gè)問(wèn)題,如
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Cold { }
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Creamy { }
AOP配置
術(shù)語(yǔ)
- JoinPoint:連接點(diǎn)。共3種:方法級(jí)別,字段級(jí)別,構(gòu)造器級(jí)別。SpringAop僅支持方法級(jí)別,AspectJ支持全部3種。
- PointCut:切入點(diǎn)。連接點(diǎn)的匹配條件。
- Advice:通知。根據(jù)執(zhí)行時(shí)機(jī),分為前置通知,后置通知,環(huán)繞通知,等。
- Aspect:切面。等于切入點(diǎn)+通知
- Weaving:織入。分為靜態(tài)織入,動(dòng)態(tài)織入。SpringAop采用動(dòng)態(tài)織入(動(dòng)態(tài)代理),AspectJ采用靜態(tài)織入。
- Target:目標(biāo)對(duì)象。即被AOP攔截的原始對(duì)象。
- Proxy:代理對(duì)象。
- Introduction:引介??蓪?duì)目標(biāo)對(duì)象添加新的方法。
使用 XML
<aop:config> <aop:pointcut id="pointcut" expression="within(org..*.PetService)"/> <!-- 切入點(diǎn) --> <aop:aspect ref="xmlAspect"> <!-- 切面, 使用id為xmlAspect的對(duì)象 --> <aop:before method="log" pointcut-ref="pointcut"/> <!-- 使用切面對(duì)象中的log方法, 對(duì)切入點(diǎn)應(yīng)用前置通知 --> </aop:aspect> </aop:config>
注解
- 定義切面
- @Aspect @Component
- 切面中定義切入點(diǎn)和通知
切入點(diǎn)
- @Pointcut :定義一個(gè)切入點(diǎn),在其他地方可引用。
切入點(diǎn)表達(dá)式
- execution:匹配方法。格式為[修飾符] 返回值 包名.類(lèi)名.方法名(形參列表) [異常]
- within:匹配類(lèi)
- args:匹配參數(shù)
- this:匹配代理對(duì)象(可指定為接口,會(huì)攔截到接口實(shí)現(xiàn)類(lèi))
- target:匹配目標(biāo)對(duì)象
- bean:匹配Spring容器中的bean
- @annotation:匹配帶某一注解的方法
- @within:匹配帶某一注解的類(lèi)(要求注解的RetentionPolicy至少為CLASS級(jí)別)
- @target:匹配帶某一注解的類(lèi)(要求注解的RetentionPolicy至少為RUNTIME)
- @args:匹配帶某一注解的參數(shù)
切入點(diǎn)表達(dá)式中可以用通配符和邏輯運(yùn)算符
通配符
- *:匹配任意數(shù)量字符
- ..:匹配多級(jí)包名, 或任意參數(shù)
- +:匹配指定類(lèi)及其子類(lèi)
邏輯運(yùn)算符: && , || , ! (在XML中需要換成 and , or , not )
通知
- @Before
- @After:無(wú)論目標(biāo)對(duì)象的方法是否執(zhí)行成功,都會(huì)執(zhí)行通知。
- @Around
環(huán)繞通知,最強(qiáng)大的通知。其方法形參中必須包含一個(gè) ProceedingJoinPoint 對(duì)象,可以通過(guò)這個(gè)對(duì)象上的 getArgs 方法獲取被攔截的目標(biāo)對(duì)象的方法入?yún)?,通過(guò) proceed 方法執(zhí)行原目標(biāo)對(duì)象的方法。
- @AfterReturning :僅在目標(biāo)對(duì)象的方法執(zhí)行成功后,執(zhí)行通知。
- @AfterThrowing :僅在目標(biāo)對(duì)象的方法執(zhí)行拋出異常時(shí),執(zhí)行通知。
- 開(kāi)啟AOP自動(dòng)代理
@EnableAspectJAutoProxy 或者 <aop:aspectj-autoproxy/>
事務(wù)支持
3大核心接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
需要
- 一個(gè)PlatformTransactionManager對(duì)象(這個(gè)對(duì)象需要一個(gè)DataSource對(duì)象)
- 事務(wù)的AOP配置
通常使用Mybatis時(shí),采用spring-jdbc包下的DataSourceTransactionManager即可
使用
- 需要導(dǎo)入spring-tx依賴,通常還需要spring-jdbc。
- 當(dāng)PlatformTransactionManager對(duì)象準(zhǔn)備就緒后,進(jìn)行事務(wù)的AOP配置即可,如下
XML
- <tx:advice>配置需要進(jìn)行事務(wù)管理的方法。
- <aop:config>配置AOP。其子標(biāo)簽使用<aop:advisor>。
注解
- @Transactional:在需要事務(wù)支持的方法上,打上該注解,并配置事務(wù)隔離級(jí)別,傳播行為等
- 開(kāi)啟事務(wù)事務(wù)管理
@EnableTransactionManagement或者<tx:annotation-driven>
到此這篇關(guān)于Spring中核心功能IOC和AOP的詳細(xì)解讀的文章就介紹到這了,更多相關(guān)Spring中的IOC和AOP內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在spring-boot工程中添加spring mvc攔截器
這篇文章主要介紹了在spring-boot工程中添加spring mvc攔截器,Spring MVC的攔截器(Interceptor)不是Filter,同樣可以實(shí)現(xiàn)請(qǐng)求的預(yù)處理、后處理。,需要的朋友可以參考下2019-06-06Java實(shí)現(xiàn)一個(gè)順序表的完整代碼
順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu),一般采用數(shù)組存儲(chǔ)。在數(shù)組上完成數(shù)據(jù)的增刪減改。順序表的底層是一個(gè)數(shù)組2021-04-04Java?遠(yuǎn)程調(diào)用失敗重試的操作方法
這篇文章主要介紹了Java?遠(yuǎn)程調(diào)用失敗重試的操作方法,今天給大家介紹了一下?Spring??的?@Retryable?注解使用,并通過(guò)幾個(gè) demo 來(lái)帶大家編寫(xiě)了自己重試攔截器以及回滾方法,需要的朋友可以參考下2022-09-09Java程序進(jìn)程起來(lái)了但是不打印日志的原因分析
這篇文章主要介紹了Java程序進(jìn)程起來(lái)了但是不打印日志的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04如何只返回實(shí)體類(lèi)中的部分字段問(wèn)題
這篇文章主要介紹了如何只返回實(shí)體類(lèi)中的部分字段問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05MyBatis連接池的深入和動(dòng)態(tài)SQL詳解
這篇文章主要介紹了MyBatis連接池的深入和動(dòng)態(tài)SQL詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02