[TOC]
Spring模塊

- Core Container: 核心容器 spring-context:包含DI, IOC, springEl表達式
- Test: 加載Spring 上下文的Spring測試
- 數(shù)據(jù)訪問與集成
- web 與遠(yuǎn)程調(diào)用
- AOP 提供對AOP的支持
核心技術(shù)
DI 依賴注入 : IOC容器(控制反轉(zhuǎn)),利用依賴關(guān)系注入的方式,實現(xiàn)對象之間的解耦。
AOP 面向切面編程:把應(yīng)用各處可重用的功能分離出來形成重用組件。
在運行時,動態(tài)地將代碼切入到類的指定方法、指定位置上
bean容器
bean 工廠
-
應(yīng)用上下文(基于bean工廠,提供應(yīng)用框架級別的應(yīng)用)
ApplicationContext 是BeanFactory 子類,加強了很多功能。如果使用簡單的BeanFactory,大量的功能將失效,比如:transactions 和AOP . 避免直接用BeanFactory

應(yīng)用上下文
AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicatonContext
-
XmlWebApplicationContext
應(yīng)用上下文通過getBean()獲取bean
bean生命周期

回調(diào)機制
三種方式實現(xiàn):
- 實現(xiàn)InitializingBean 和實現(xiàn)DisposableBean 回調(diào)接口
- 自定義init()和destroy()方法 并XML 面配置 Init-method=”” ; Destroy-method=””
- @PostConstruct and @PreDestroy 注解。
容器擴展點
實現(xiàn)BeanPostProcessor接口
在Spring容器完成實例化、配置、初始化bean之前之后執(zhí)行自定義邏輯。
可以配置多個BeanPostProcessor實例,可以設(shè)置BeanPostProcessors的order屬性來控制其執(zhí)行次序。
points:
- 接口中的兩個方法都要將傳入的bean返回,而不能返回null
- 不要將BeanPostProcessor標(biāo)記為延遲初始化,因為如果這樣做,Spring容器將不會注冊它們.
- BeanPostProcessors容器級別的。當(dāng)使用容器繼承時BeanPostProcessors不會繼承。
注冊方式:
- xml:
<bean class="com.spring.containerExtension.MyBeanPostProcessor" />
- 注解:@Component 修飾自定義的BeanPostProcessor類。并加到context里面。(聲明時或者@ComponentScan)
使用注解的方式因為@Configuration類本身也會注冊為一個bean定義,還有一些系統(tǒng)的bean 所以不好控制有效范圍, 通過xml 方式容易控制。
e.g. @Required 就是用RequiredAnnotationBeanPostProcessor 實現(xiàn)的。
BeanFactoryPostProcessor
此接口的語法和BeanPostProcessor類似,用于讀取配置元數(shù)據(jù)并且在容器實例化bean之前修改bean配置。這個也是在自己容器內(nèi)有效,也不會繼承。
延遲初始化
ApplicationContext的各種實現(xiàn)默認(rèn)的初始化處理過程,都是盡早的創(chuàng)建、配置所有的單例bean。lazy-initialized bean告訴Ioc容器,只有在第一次請求的時候采取初始化,而不是在啟動容器時初始化。
通過設(shè)置<beans/>元素的default-lazy-init屬性,可以設(shè)置容器級別的延遲加載。
普通bean 需要的時候初始化。
裝配bean
定義bean
xml 顯式定義
-
注解顯式定義
@Configuration @Bean @Component @Repository @Service @Controller +組件掃描ComponentScan
@Configuration
@ComponentScan("com.spring.baseIOC")
public class ConfigWithAnnotationTest {
@Bean //修飾方法
@Qualifier("bean1") //如果全局只有一個String 需要被注入,那這里可以省略Qualifier
String method(@Qualifier("bean2") String a){
return new String(a);
}
@Scope("prototype")
@Bean(name = "bean2")
@Qualifier("bean2_qual")
String method2(){
return new String("cvszz22");
}
@Autowired //聲明這個field 會被自動注入
@Qualifier("SomeBeanAnnotation")
private SomeBeanAnnotation someBean;
}
注入bean
-
xml 顯式配置
構(gòu)造器注入 屬性注入
-
注解顯式配置
@Autowired
隱式的bean發(fā)現(xiàn)機制和自動裝配
注解注入在XML注入之前執(zhí)行,因此同時使用這兩種方式注入時,XML配置會覆蓋注解配置。
xml自動裝配
autowire="byName"
- no 默認(rèn)非自動裝配
- byName
- byType
- constructor
設(shè)置<bean/>元素的autowire-candidate屬性為false;可以排除他們自動注入到其他bean,但是它本身是可以被自動注入的。
通過設(shè)置<bean/>袁術(shù)的primary屬性為true來指定單個bean定義作為主候選bean。
常用注解
@Autowired注解可以應(yīng)用在setter, 構(gòu)造函數(shù)上或者屬性上,默認(rèn)required 屬性是 true 的。
@Required注解應(yīng)用于bean的setter方法, 聲明在配置時必須賦值
@Qualifier注解??梢詫ualifier關(guān)聯(lián)指定參數(shù).
public void prepare(@Qualifier("main")MovieCatalog movieCatalog){};
qualifier值,只是為了縮小類型匹配的范圍;他們并不能作為引用的bean的唯一標(biāo)示符。
@PostConstruct @PreDestroy bean的回調(diào)
@Repository, @Service, @Controller 各層代碼注解,分別注解dao , service, layers表現(xiàn)層. 他們默認(rèn)id是類名首字母小寫
@Repository能自動轉(zhuǎn)換異常。 @Component是他們的元注解。
@Scope("prototype") 指定bean作用域scope
@Import注解也允許從其他配置類(@Configuration)中加載@Bean定義。 @Import(ConfigA.class) 這樣相當(dāng)與使用AnnotationConfigApplicationContext 或者@ConponentScan @ImportResource("classpath:/com/acme/properties-config.xml") 配置類(@Configuration)中引用XML配置。
@Configuration注解的類表明該類的主要目的是作為bean定義的源。 @Configuration類本身也會注冊為一個bean定義,通過CGLIB創(chuàng)建一個子類。類內(nèi)所有的@Bean注解的方法也會注冊為bean定義。類不能是final.
@Bean注解用于表明一個方法將會實例化、配置、初始化一個新對象,該對象由Spring IoC容器管理。
@Primary該bean首選
@ComponentScan 自動探測類和自動注冊bean定義
注解方式:在@Configuration注解的類上增加@ComponentScan,其basePackages屬性就是上面兩個類的所在的父級包路徑。
xml: <context:component-scan base-package="org.example"/> 使用<context:component-scan>將會隱式的啟用<context:annotation-config>
@Configuration 只是聲明該類為bean源,還需要@ConponentScan 去加載到context 里面。或者在聲明AnnotationConfigApplicationContext 的時候都加到里面。
bean 作用域
| singleton | 默認(rèn)的。一個bean定義,在一個IoC容器內(nèi)只會產(chǎn)生一個對象。 |
| prototype原型 | 每次都創(chuàng)建一個新的實例 |
| request | 每個HTTP請求都產(chǎn)生新的實例。Spring web上下文環(huán)境中有效。 |
| session | 生命周期在HTTP 會話期間。Spring web上下文環(huán)境中才有效。 |
一般來說 ,原型bean用于有狀態(tài)bean,單例bean用于無狀態(tài)bean。一個數(shù)據(jù)訪問對象(DAO)通常不會配置成原型作用域,因為通常DAO不會持有任何會話狀態(tài)。
如果想將HTTP request作用域(request,session,globalSession)bean注入給其他singleton bean,就得給作用域bean(request或者session)注入一個AOP代理用來替換作用域bean。容器會創(chuàng)建一個代理對象,該對象擁有和原對象完全相同的public方法。
因為容器初始化的時候singleton 需要加載,但此時session bean還未存在
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/> //代理只會攔截public方法調(diào)用。非public方法不會“呼叫轉(zhuǎn)移”給實際的作用域bean。
</bean>
若是在Java代碼中配置bean,使用@Scope注解并配置其proxyMode屬性.默認(rèn)配置是沒有代理ScopedProxyMode.NO, 但是你可以設(shè)置ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES。 @Scope(value ="session", proxyMode =ScopedProxyMode.TARGET_CLASS)
@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.TARGET_CLASS)
高級裝配
Environment環(huán)境
Environment環(huán)境在容器中是一個抽象的集合,是指應(yīng)用環(huán)境的2個方面: profiles 和 properties.
profile
bean定義profile是核心容器內(nèi)的一種機制,該機制能在不同環(huán)境中注冊不同的bean。
比如:
- 開發(fā)期,QA或者產(chǎn)品上使用來自JNDI的不同數(shù)據(jù)源
- 開發(fā)期使用監(jiān)控組件,當(dāng)部署以后則關(guān)閉監(jiān)控組件
Profile定義
- @Profile可以用于元數(shù)據(jù)注解,注解方法(@Bean 注解的方法),注解類(@Configuration。 i.g. @Profile("production")
- XML中的beans 也有profile 屬性。 profile="dev"
profile定義的bean都將被忽略,除非該profile被激活
如果給定的profile,使用了NOT操作(!)前綴,若當(dāng)前profile未被激活則注解元素將會注冊。對于@Profile({"p1", "!p2"}),在profile 'p1'被激活或者'p2'未激活時,發(fā)生注冊。
激活profile
1.編程式開啟方式
AnnotationConfigApplicationContext ctx =new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
2.使用spring.profiles.active屬性激活配置 。
spring.profiles.active="profile1,profile2"
3.servlet初始化參數(shù),web上下文參數(shù),環(huán)境變量等等
PropertySource
Spring的環(huán)境抽象可以檢索一系列的property sources屬性配置文件。
Spring的StandardEnvironment配置了2個PropertySource對象-其一是JVM系統(tǒng)properties(System.getProperties()),另一個是一組系統(tǒng)環(huán)境變量(System.getenv())。
@Component
@PropertySource(value ="classpath:com/spring/Property/quartz.properties")
public class MyProperty{
//為了支持spring el 表達式
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
@ContextConfiguration(classes = {MyProperty.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class PropertyTest {
@Autowired
private Environment env;
@Test
public void test(){
System.out.print(env);
}
}
條件化的bean
使用@Conditional可以用到帶有@Bean 注解的方法上,指向一個實現(xiàn)了Condition接口的類,根據(jù)matches方法返回的boolean值確定是否注冊這個bean
@Conditional(MyConditional.class)
@Bean(name = "b2")
public ClientServiceImpl getService() {
return ...;
}
方法替換
設(shè)置replaced-method元素,就可用其他實現(xiàn)來替換已經(jīng)部署的bean中存在的方法實現(xiàn)。
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<replaced-method name="computeValue" replacer="replacementComputeValue">
<!--替換computerValue方法-->
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
<!--ReplacementComputeValue 需要實現(xiàn)org.springframework.beans.factory.support.MethodReplacer 接口-->
方法注入
在容器范圍內(nèi)查找一個bean作為方法返回結(jié)果
<bean id="A" class="com.spring.lookup_method.UserManager" >
<lookup-method name="getUser" bean="B" />
</bean>
<bean id="B" class="com.spring.lookup_method.User"/>
public abstract class UserManager {
protected abstract User getUser();
public void run(){
User user = getUser();
System.out.println(user);
}
}
class User{
String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
事務(wù)
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE)
事務(wù)傳播行為
| PROPAGATION_SUPPORTS | 有事務(wù)就用,沒有就不用 |
| PROPAGATION_NOT_SUPPORTED | 不用事務(wù),如果當(dāng)前有就掛起事務(wù) |
| PROPAGATION_MANDATORY | 有就用,沒有就拋異常 |
| PROPAGATION_NEVER | 有事務(wù)就跑出異常 |
| PROPAGATION_REQUIRED | 有就用,沒有就新建 |
| PROPAGATION_REQUIRES_NEW | 新建一個,如果當(dāng)前有就掛起當(dāng)前事務(wù) |
| Propagation.NESTED | 沒有就新建,有就當(dāng)前事務(wù)中再嵌套事務(wù)運行 — 嵌套事務(wù)中可以定義儲存點,因此可以獨立于外部的Transaction而進行rollback |
數(shù)據(jù)庫的隔離級別
數(shù)據(jù)庫事務(wù)隔離級別:一個事務(wù)對數(shù)據(jù)庫的修改與并行的另一個事務(wù)的隔離程度。
并發(fā)訪問數(shù)據(jù)庫存在的問題
- 臟讀:事務(wù)T1更新了一行記錄,還未提交所做的修改,這個T2讀取了更新后的數(shù)據(jù),然后T1執(zhí)行回滾操作,取消剛才的修改,所以T2所讀取的行就無效,也就是臟數(shù)據(jù)。
- 不可重復(fù)讀?。菏聞?wù)T1讀取一行記錄,緊接著事務(wù)T2修改了T1剛剛讀取的記錄,然后T1再次查詢,發(fā)現(xiàn)與第一次讀取的記錄不同,這稱為不可重復(fù)讀。
- 幻讀:事務(wù)T1讀取一條指定where條件的語句,返回結(jié)果集。此時事務(wù)T2插入一行新記錄,恰好滿足T1的where條件。然后T1使用相同的條件再次查詢,結(jié)果集中可以看到T2插入的記錄,這條新紀(jì)錄就是幻想。
不可重復(fù)讀和臟讀的區(qū)別是,臟讀是某一事務(wù)讀取了另一個事務(wù)未提交的臟數(shù)據(jù),而不可重復(fù)讀和幻讀是讀取了前一事務(wù)提交的數(shù)據(jù)。
不可重復(fù)讀的重點是修改,幻讀的重點在于新增或者刪除
四種隔離級別
- Read uncommitted (讀未提交):最低級別,任何情況都無法保證。
- Read committed (讀已提交):可避免臟讀的發(fā)生。
- Repeatable read (可重復(fù)讀):可避免臟讀、不可重復(fù)讀的發(fā)生。
- Serializable (串行化):可避免臟讀、不可重復(fù)讀、幻讀的發(fā)生。
MySQL數(shù)據(jù)庫中,支持上面四種隔離級別,默認(rèn)的為Repeatable read (可重復(fù)讀);而在Oracle數(shù)據(jù)庫中,只支持Read committed (讀已提交)和Serializable (串行化)級別這兩種級別,其中默認(rèn)的為Read committed級別。
設(shè)置數(shù)據(jù)庫的隔離級別一定要是在開啟事務(wù)之前,隔離級別的設(shè)置只對當(dāng)前鏈接有效。
例子:

左面是事務(wù)T1,右面是事務(wù)T2,因為T2級別為SERIALIZABLE,所以即使事務(wù)T1在提交了數(shù)據(jù)之后,事務(wù)T2還是看不到T1提交的數(shù)據(jù),幻想讀和不可重復(fù)讀都不允許了。
那如何能查看到T1新增的記錄呢? 上面T1和T2是并發(fā)執(zhí)行,在T1執(zhí)行insert的時候如果事務(wù)T2已經(jīng)開始了,因為T2級別是SERIALIZABLE,所以T2所查詢的數(shù)據(jù)集是T2事務(wù)開始前數(shù)據(jù)庫的數(shù)據(jù)。即事務(wù)T1在事務(wù)T2開始之后的insert和update操作的影響都不會影響事務(wù)T2?,F(xiàn)在重新開啟一個事務(wù)T3 就可以看到T1新增的記錄了。
AOP
五種類型的通知
- @Before
- @After
- @AfterReturning 成功執(zhí)行后
- @AfterThrowing 拋異常后
- @Around
Spring 的AspectJ自動代理只是使用AspectJ作為創(chuàng)建切面的指導(dǎo),切面仍然是基于代理的。本質(zhì)上,它仍然是Spring 基于代理的切面。如果要使用全部AspectJ功能需要不依賴Spring 來創(chuàng)建切面并且在運行時使用AspectJ
@Aspect
@Component
public class MyInterceptor implements Ordered {
//第一個* 代表任意返回值, .. 代表任意參數(shù)
//符合條件的才被切入
@Pointcut("execution(* com.spring.aop.MyService.*(..))")
private void anyMethod(){}//定義一個切入點
@Before("anyMethod() && args(name,param2)")
public void doAccessCheck(String name,int param2){
System.out.println(name);
System.out.println(param2);
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")
public void doAfter(){
System.out.println("后置通知");
}
}
<context:component-scan base-package="com.spring.aop" />
<!--打開aop -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="serviceBean" class="com.spring.aop.MyService">
</bean>
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/aop.xml");
MyService bean = (MyService) ctx.getBean("serviceBean");
bean.update("param1",2);
}
切面的織入順序
order 的值越小,說明越先被執(zhí)行
@Component
@Aspect
public class MyAspectJ implements Ordered{
...
@Override
public int getOrder() {
return 1;
}
}
<!-- 注解方式配置事物 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>
這樣就實現(xiàn)了我們自己寫的aop在事務(wù)介入之前就執(zhí)行了!
JSR-330
Spring3.0開始,Spring提供了對JSR-330標(biāo)準(zhǔn)注解(依賴注入)的支持。這些注解以Spring注解相同的方式被掃描。
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
| Spring | javax.inject.* | |
|---|---|---|
| @Autowired | @Inject | @Inject 沒有required屬性 |
| @Component | @Named | |
| @Scope("singleton") | @Singleton | JSR-330默認(rèn)的作用域類似于Spring的prototype原型作用域。為了保持Spring的一致性,在Spring容器中的JSR-330的bean聲明,默認(rèn)是singleton單例。除了singleton,若要設(shè)置作用域,得使用Spring的@Scope注解。javax.inject也提供了一個@Scope注解。然而,這個注解僅僅是為了讓你創(chuàng)建自定義注解用的譯注,也就是元注解的源碼注解。 |
| @Qualifier | @Named | |
| @Value | 無等價注解 | |
| @Required | 無等價注解 | |
| @Lazy | 無等價注解 |
others
spring-web 初始化配置 (spring-mvc 不需要這個)
為了支持request,sesssion,global session這種級別bean的作用域(web作用域bean),在定義bean之前需要一些初始化的配置。
若使用 Spring Web MVC訪問這些作用域bean,實際上是使用Srping DispatcherServlet類或者DispatcherPortlet類處理request,則無需特別配置:DispatcherServlet 和 DispatcherPortlet已經(jīng)暴露了所有的相關(guān)狀態(tài)。
非Spring的DispacherServlet處理請求(比如,JSF或者Struts2)。
- 小于2.5 的Servlet web容器: RequestContextFilter
- Servlet 2.5的web容器則需要注冊org.springframework.web.context.request.RequestContextListener ServletRequestListener。
- Servlet 3.0+,這些設(shè)置可以通過編程式方式使用WebApplicationInitializer接口完成。
DispatcherServlet,RequestContextListener,RequestContextFilter都是做相同的事,也就是綁定HTTP request對象到服務(wù)的Thread線程中,并開啟接下來 用到的session-scoped功能。
SSH 需要上面的spring + spring-web +struts2配置。如果是用spring-mvc 只需spring + spring-mvc 自己的servlet (DispatcherServlet)配置就可以了。