Spring中IOC和AOP的核心組成架構詳解
IoC配置
注冊bean
XML
<bean> 標簽
屬性
- id
- class
- init-method
- destroy-method
- scope
子標簽
- <constructor-arg>:構造器注入 --> c命名空間注入
- <property>:set方法注入 --> p命名空間注入
注解
- @Component:注解在類上,配合自動掃描(@ComponentScan),完成注冊
- @Controller
- @Service
- @Repository
- @Bean:注解在方法上,方法的返回值對象會注冊到Spring容器(常用于創(chuàng)建第三方jar包中的類對象)。這些被@Bean標注的方法需要放到Spring配置類當中(被@Configuration標注的類)
其他注解
- @ComponentScan:啟用自動掃描,掃描指定包下帶有@Component注解的類(對應XML中的<context:component-scan>)
- @Configuration:將某個類標記為配置類
- @Import:組合多個配置類
- @Scope:指定bean的作用范圍。與@Component或@Bean組合使用。值是字符串,可以用ConfigurableBeanFactory中的常量,或WebApplicationContext中的常量
- @PostContruct:自定義初始化函數(shù),約等XML的init-method。(也可通過InitializingBean接口實現(xiàn))
- @PreDestroy:自定義銷毀函數(shù),約等于XML的destroy-method。(也可通過DisposableBean接口實現(xiàn))
裝配bean
@Autowired
屬于Spring,按類型注入, required 屬性默認為 true 。由Spring中的 AutowiredAnnotationBeanPostProcessor 進行處理。
注:帶 @Autowired 注解的方法,會被Spring自動調用。
@Resource
屬于JavaEE(JSR250),當指定了 name 屬性時,按照名稱( bean 的 id )注入;否則,按照類型注入
@Inject
屬于JavaEE(JSR330),需導入額外的 jar 包,按照類型注入
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
@Value
用來注入基本類型和 String 類型的值。
可以用 ${} ,來獲取 properties 文件中的屬性。( properties 文件要加載到Spring中)
關于把 properties 文件加載到Spring
- 創(chuàng)建一個 PropertyPlaceholderConfigurer 對象到Spring容器中,設置 location 的值為 properties 文件的路徑
可通過 <context:property-placeholder> 標簽進行簡化
- @PropertySource
用 @PropertySource 結合 @Component 或者 @Configuration
注意: properties 文件中的屬性,會被加載到Spring的Environment對象中,多個 properties 中的同名屬性,會發(fā)生覆蓋
高級裝配
環(huán)境與Profile
@Profile 與 @Configuration 或 @Component , @Bean 等結合使用。
可以控制僅在某一 profile 下激活對應的類。如下面的配置類僅在 dev 環(huán)境下生效
@Profile("dev") @Configuration public class DevConfig { @Bean public PetService petService() { return new PetService(); } }
如何指定當前激活的 profile ?
依賴于2個獨立屬性: spring.profiles.active 和 spring.profiles.default 。
若設置前者,則根據(jù)前者來確定;若沒設置前者,則根據(jù)后者來確定。若2者都未指定,則只會激活那些沒有定義在某一 profile 下的類
有多種方式來設置這2個屬性
- 作為環(huán)境變量(如在maven的profile標簽下配置上述2個屬性,并在maven打包時用 -P 參數(shù)指定maven的profile)
<profiles> <!-- 開發(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應用的上下文參數(shù)(SpringMvc)
<!-- web,xml 中 --> <!-- 省略其他部分 --> <context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param>
作為DispatcherServlet的啟動參數(shù)(SpringMvc)
- 在 web.xml 中配置 servlet 時,通過 <init-param> 指定
- 集成測試時,在測試類上用 @ActiveProfiles 注解進行指定
- 通過JVM啟動參數(shù) -Dspring.profiles.active=dev
條件化bean
在滿足某些條件時,才創(chuàng)建某些 bean 。通過 @Conditional 注解和 Condition 接口實現(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, 則表示條件滿足, 會創(chuàng)建對應的bean; 若返回 false, 則不會創(chuàng)建對應bean * **/ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 當Spring的Environment中存在名為 magic 的屬性, 才認為滿足條件 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(); } }
測試
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 表達式值為 true ,如下
若添加 magic 屬性,則 MagicBean 會被創(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(); } }
再次運行測試, magicBean == null 表達式值為 false
Condition 接口中的 matches 方法提供了2個參數(shù): ConditionContext 和 AnnotatedTypedMetadata
通過 ConditionContext
- 借助getRegistry方法,可以檢查bean的定義
- 借助getBeanFactory方法,可以檢查bean是否存在,檢查bean的屬性等
- 借助getEnvironment方法,檢查環(huán)境變量等
- …
通過 AnnotatedTypedMetadata ,可以檢查注解的情況
Spring自帶的的 @Profile 注解,也是通過 @Conditional 這種方式實現(xiàn)的
處理歧義性
依賴注入時,當同一個類型,有多個候選 bean 時,如何解決?
- 將其中一個 bean 標記為首選
使用 @Primary 注解( @Primary 與 @Component 搭配,或 @Primary 與 @Bean 搭配)(只能有一個首選 bean ,當出現(xiàn)多個首選 bean 時,Spring仍然不知道該注入哪一個)
- 使用限定符
@Qualifier 是主要的限定符使用方式,可以搭配 @Autowired ,或者 @Inject 。
一個 bean 的限定符,默認情況下是這個 bean 的 id ;當然,也可以為 bean 設置自定義的限定符,如
@Component @Qualifier("cold") // 也可以與@Bean注解結合使用 public class IceCream implements Dessert { }
一個 bean 可以有多個限定符,不同的 bean 可以有相同的限定符。當依賴注入時,如果通過一個限定符仍然能匹配多個候選者,那么可以指定更多的限定符,如
@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)重復注解,Java 8以后,當這個注解被標記為 @Repeatable 時,可以重復。
可以創(chuàng)建自定義的限定符注解來解決這個問題,如
@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配置
術語
- JoinPoint:連接點。共3種:方法級別,字段級別,構造器級別。SpringAop僅支持方法級別,AspectJ支持全部3種。
- PointCut:切入點。連接點的匹配條件。
- Advice:通知。根據(jù)執(zhí)行時機,分為前置通知,后置通知,環(huán)繞通知,等。
- Aspect:切面。等于切入點+通知
- Weaving:織入。分為靜態(tài)織入,動態(tài)織入。SpringAop采用動態(tài)織入(動態(tài)代理),AspectJ采用靜態(tài)織入。
- Target:目標對象。即被AOP攔截的原始對象。
- Proxy:代理對象。
- Introduction:引介??蓪δ繕藢ο筇砑有碌姆椒?。
使用 XML
<aop:config> <aop:pointcut id="pointcut" expression="within(org..*.PetService)"/> <!-- 切入點 --> <aop:aspect ref="xmlAspect"> <!-- 切面, 使用id為xmlAspect的對象 --> <aop:before method="log" pointcut-ref="pointcut"/> <!-- 使用切面對象中的log方法, 對切入點應用前置通知 --> </aop:aspect> </aop:config>
注解
- 定義切面
- @Aspect @Component
- 切面中定義切入點和通知
切入點
- @Pointcut :定義一個切入點,在其他地方可引用。
切入點表達式
- execution:匹配方法。格式為[修飾符] 返回值 包名.類名.方法名(形參列表) [異常]
- within:匹配類
- args:匹配參數(shù)
- this:匹配代理對象(可指定為接口,會攔截到接口實現(xiàn)類)
- target:匹配目標對象
- bean:匹配Spring容器中的bean
- @annotation:匹配帶某一注解的方法
- @within:匹配帶某一注解的類(要求注解的RetentionPolicy至少為CLASS級別)
- @target:匹配帶某一注解的類(要求注解的RetentionPolicy至少為RUNTIME)
- @args:匹配帶某一注解的參數(shù)
切入點表達式中可以用通配符和邏輯運算符
通配符
- *:匹配任意數(shù)量字符
- ..:匹配多級包名, 或任意參數(shù)
- +:匹配指定類及其子類
邏輯運算符: && , || , ! (在XML中需要換成 and , or , not )
通知
- @Before
- @After:無論目標對象的方法是否執(zhí)行成功,都會執(zhí)行通知。
- @Around
環(huán)繞通知,最強大的通知。其方法形參中必須包含一個 ProceedingJoinPoint 對象,可以通過這個對象上的 getArgs 方法獲取被攔截的目標對象的方法入?yún)ⅲㄟ^ proceed 方法執(zhí)行原目標對象的方法。
- @AfterReturning :僅在目標對象的方法執(zhí)行成功后,執(zhí)行通知。
- @AfterThrowing :僅在目標對象的方法執(zhí)行拋出異常時,執(zhí)行通知。
- 開啟AOP自動代理
@EnableAspectJAutoProxy 或者 <aop:aspectj-autoproxy/>
事務支持
3大核心接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
需要
- 一個PlatformTransactionManager對象(這個對象需要一個DataSource對象)
- 事務的AOP配置
通常使用Mybatis時,采用spring-jdbc包下的DataSourceTransactionManager即可
使用
- 需要導入spring-tx依賴,通常還需要spring-jdbc。
- 當PlatformTransactionManager對象準備就緒后,進行事務的AOP配置即可,如下
XML
- <tx:advice>配置需要進行事務管理的方法。
- <aop:config>配置AOP。其子標簽使用<aop:advisor>。
注解
- @Transactional:在需要事務支持的方法上,打上該注解,并配置事務隔離級別,傳播行為等
- 開啟事務事務管理
@EnableTransactionManagement或者<tx:annotation-driven>
到此這篇關于Spring中核心功能IOC和AOP的詳細解讀的文章就介紹到這了,更多相關Spring中的IOC和AOP內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在spring-boot工程中添加spring mvc攔截器
這篇文章主要介紹了在spring-boot工程中添加spring mvc攔截器,Spring MVC的攔截器(Interceptor)不是Filter,同樣可以實現(xiàn)請求的預處理、后處理。,需要的朋友可以參考下2019-06-06