重新回顧一遍Spring孤荣,希望能夠溫故知新甸陌,有新的見解。
學(xué)習(xí)Spring的好處
- 方便解耦盐股,簡化開發(fā)
通過Spring提供的IoC容器钱豁,我們可以講對(duì)象之間的依賴關(guān)系交由Spring進(jìn)行控制,避免硬編碼鎖造成的過度程序耦合疯汁。有了Spring牲尺,用戶不必再為單實(shí)例模式類、屬性文件解析等這些很底層的需求編寫代碼幌蚊,可以更專注于上層的應(yīng)用谤碳。 - AOP編程的支持
通過Spring提供的AOP功能,方便進(jìn)行面向切面編程溢豆,許多不容易用傳統(tǒng)OOP實(shí)現(xiàn)的功能可以通過AOP輕松實(shí)現(xiàn)蜒简。 - 聲明式事務(wù)的支持
在Spring中,我們可以從單調(diào)煩悶的事務(wù)管理代碼中解脫出來漩仙,通過聲明式方式靈活地進(jìn)行事務(wù)管理搓茬。 - 方便程序的測試
可以用非容器依賴的編程方式進(jìn)行幾乎所有的測試工作,Spring對(duì)Junit4的支撐队他,可以通過注解方便地進(jìn)行測試卷仑。 - 方便繼承各種優(yōu)秀框架
Spring不排斥各種優(yōu)秀的開源框架,相反麸折,Spring可以降低各種框架的使用難度锡凝,Spring提供了對(duì)各種優(yōu)秀框架(如Struts,Hibernate垢啼,Hessian窜锯,Quartz等)的支撐。 - 降低Java EE API 的使用難度
Spring對(duì)很多難用的Java EE API 提供了一個(gè)薄薄的封裝層膊夹,通過Spring的簡易封裝衬浑,這些Java EE API 的使用難度大幅下降。 - Java源碼是學(xué)習(xí)的典范
Spring源碼設(shè)計(jì)精妙放刨、結(jié)構(gòu)清晰工秩、匠心獨(dú)運(yùn),處處體現(xiàn)著大師對(duì)Java設(shè)計(jì)模式靈活運(yùn)用以及對(duì)Java技術(shù)的高深造詣进统。Spring框架源碼無疑是Java技術(shù)的最佳實(shí)踐范例助币。
關(guān)鍵字: IoC,DI螟碎,AOP眉菱,Spring容器
IoC:控制反轉(zhuǎn)(Inversion of Control),創(chuàng)建對(duì)象的控制權(quán)不是由用戶創(chuàng)建掉分,而是反轉(zhuǎn)給Spring框架來管理俭缓。是一種設(shè)計(jì)思想克伊,不是技術(shù),滿足這種思想的技術(shù)可以有很多種华坦,Spring IoC是其中一種愿吹。
DI:依賴注入(Dependency Injecetion),IoC是指將對(duì)象的創(chuàng)建權(quán)反轉(zhuǎn)給了Spring容器惜姐,而DI則是Spring框架負(fù)責(zé)創(chuàng)建Bean對(duì)象是犁跪,動(dòng)態(tài)地將依賴對(duì)象注入到Bean組件中。DI和IoC是相輔相成歹袁,缺一不可坷衍。
AOP:Aspect Oriented Programming,面向切面編程条舔。在不修改目標(biāo)對(duì)象的源代碼情況下枫耳,增強(qiáng)IoC容器中Bean的功能。
Spring容器:IoC容器逞刷,存儲(chǔ)Spring生成的Bean對(duì)象嘉涌,底層也是一個(gè)BeanFactory。
Spring的兩大特性之一:IoC / DI
1. Spring IoC/DI 基于xml方式的應(yīng)用
IoC配置
創(chuàng)建ApplicationContext是通過讀取applicationContext.xml文件夸浅,然后通過解析xml標(biāo)簽獲取響應(yīng)的信息創(chuàng)建applicationContext對(duì)象的仑最。同理,在applicationContext.xml中通過讀取bean標(biāo)簽帆喇,也可以創(chuàng)建響應(yīng)的對(duì)象警医,從而交給Spring管理。
<bean id="person" class="com.pillow.domain.Person"
init-method="doInit" destroy-method="doDestroy" scope="prototype">
</bean>
- bean標(biāo)簽的作用:
用于配置被Spring容器管理的bean的信息
默認(rèn)情況下它調(diào)用的是類中的 無參構(gòu)造器坯钦。如果沒有無參構(gòu)造器則不能創(chuàng)建成功预皇。 - bean標(biāo)簽的屬性:
- id:給對(duì)象在容器中提供一個(gè)唯一標(biāo)識(shí),用于獲取對(duì)象婉刀。
- class:指定類的全限定名吟温。用于反射創(chuàng)建對(duì)象。默認(rèn)情況下調(diào)用 無參構(gòu)造器突颊。
- init-method:指定類中的初始化方法鲁豪,在創(chuàng)建對(duì)象后馬上執(zhí)行。
- destroy-method:指定類的銷毀方法名稱律秃,在銷毀對(duì)象之前執(zhí)行爬橡。比如DataSource的配置中一般需要指定destroy-method='close'。
- scope:指定對(duì)象的作用范圍:
- singleton:默認(rèn)值棒动,單例(在整個(gè)容器中只有一個(gè)對(duì)象)
- prototype:多例的糙申,每次訪問對(duì)象時(shí),都會(huì)重新創(chuàng)建對(duì)象實(shí)例船惨。
DI配置
依賴注入可通過構(gòu)造函數(shù)注入柜裸,setter方法注入缕陕。
構(gòu)造函數(shù)注入,該方式需要滿足指定參數(shù)的構(gòu)造 :
<bean id="person" class="com.pillow.domain.Person"
init-method="doInit" destroy-method="doDestroy" scope="prototype">
<constructor-arg name="id" value="2"/>
<constructor-arg name="name" value="pillow"/>
</bean>
setter方式注入一:手動(dòng)裝配(xml方式):
須配置bean標(biāo)簽的子標(biāo)簽property疙挺,bean對(duì)象中需存在setter方法榄檬。
<bean id="person" class="com.pillow.domain.Person"
init-method="doInit" destroy-method="doDestroy" scope="prototype">
<property name="name" value="張飛"/>
</bean>
2. Spring IoC/DI 基于xml和注解混合的使用
IoC配置
第一步:在spring配置文件中配置context:component-scan標(biāo)簽,指定掃描的范圍衔统。帶有第二步注解的類都能被掃描到。
<context:component-scan base-package="com.pillow.service"></context:component-scan>
第二步:在類上加上注解@Component海雪,或者它衍生的注解@Controller锦爵,@Service,@Repository奥裸。這四個(gè)注解的作用都是一樣的险掀,只是編碼時(shí)用于區(qū)分層次。
@Component 注解
這個(gè)注解的作用就是把該類交給Spring管理湾宙,相當(dāng)于在xml中配置了一個(gè)bean樟氢。
屬性value:指定bean的id,如果不指定value屬性侠鳄,默認(rèn)bean的id是當(dāng)前類的類名埠啃,首字母小寫∥岸瘢‘’
DI配置
setter方式注入二: 自動(dòng)裝配(注解方式)
- @Autowired:從Spring容器中根據(jù)Bean的類型(Class)獲取實(shí)例碴开,將找到的實(shí)例裝配給另一個(gè)實(shí)例的屬性。注意:一個(gè)Java類型(Class)在同一個(gè)Spring容器中只能有一個(gè)實(shí)例博秫。
- @Resource:從Spring容器中根據(jù)Bean的名稱(name)獲取實(shí)例潦牛,然后進(jìn)行賦值
- @Inject:根據(jù)類型進(jìn)行自動(dòng)裝配,如果需要根據(jù)名稱則需要配合@named挡育“屯耄可以用作在變量、setter方法即寒、構(gòu)造函數(shù)上橡淆。
3. Spring IoC/DI 基于純注解的使用
Spring 純注解的使用和SpringBoot是無縫銜接的,有些在Springboot中經(jīng)常用到的注解蒿叠,實(shí)際上是Spring的明垢,比如@Configration,@ComponentScan等市咽。
@Configration 注解
相當(dāng)于Spring的xml配置文件痊银,可替換掉使用xml方式的配置文件。
配置類內(nèi)部包含有一個(gè)或多個(gè)被@Bean注解的方法施绎,這些方法將會(huì)被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進(jìn)行掃描溯革,并用于構(gòu)建bean對(duì)象贞绳,初始化Spring容器。
@Configuration
public class SpringConfiguration {
// Spring容器初始化時(shí)致稀,會(huì)調(diào)起配置類的無參構(gòu)造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
@Bean 注解
相當(dāng)于<bean>標(biāo)簽冈闭,作用是注冊(cè)bean對(duì)象,用來配置非自定義的bean抖单,比如DruidDataSource萎攒,SqlSessionFactory,實(shí)際也可以用于自定義bean矛绘。
@Bean標(biāo)注在方法上耍休,將該方法返回的實(shí)例交給Spring管理
@Bean(name = "common")
@Scope("prototype")
public CommonProperties constantsProperties() {
return new CommonProperties();
}
屬性 name:給當(dāng)前@Bean注解方法創(chuàng)建的對(duì)象指定一個(gè)名稱,注入時(shí)可根據(jù)該名稱注入货矮。如果不指定羊精,默認(rèn)與注解的方法名相同。
@Bean注解默認(rèn)作用域?yàn)閱卫齭ingleton囚玫,可通過@Scope("prototype")設(shè)置為多例喧锦。
@ComponentScan 注解
相當(dāng)于<context:component-scan>標(biāo)簽,用于掃描@Component抓督、@Controller等注解的類拓型。
該注解貼在類上面趾牧,一般是和@Configration注解一起使用儒喊。
@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
public class SpringConfiguration {
// Spring容器初始化時(shí)痢缎,會(huì)調(diào)起配置類的無參構(gòu)造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
屬性:
basePackages:用于指定要掃描的包。如果不指定涌穆,則默認(rèn)掃同包名下的注解怔昨。
value:和basePackages一樣。
@PropertySource 注解
相當(dāng)于<context:property-placeholder>注解宿稀,編寫類上趁舀,作用是加載properties配置文件。
@Configuration
@PropertySource("classpath:config.properties")
public class ConfigProperties {
@Value("${baseUrl}")
private String baseUrl;
}
屬性 value[]:用于指定properties文件路徑祝沸,如果是在類路徑下矮烹,需要指定classpath。
@Import 注解
相當(dāng)于spring配置文件中的<import>標(biāo)簽罩锐,用于組合多個(gè)配置類奉狈,可以不用再寫@Configration注解。實(shí)際上寫上也沒關(guān)系涩惑,Spring可以同時(shí)加載多個(gè)@Configration注解仁期。
@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
@Import({JdbcConfiguration.class})
public class SpringConfiguration {
// Spring容器初始化時(shí),會(huì)調(diào)起配置類的無參構(gòu)造器
public SpringConfiguration() {
System.out.println("spring容器初始化....");
}
}
@PropertySource("classpath:config.properties")
public class JdbcConfiguration {
}
屬性 value:用來指定其他配置類的字節(jié)碼文件,可以指定多個(gè)跛蛋。
創(chuàng)建純注解方式上下文容器
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(SpringConfiguration.class);
ConfigProperties configProperties = applicationContext.getBean(ConfigProperties.class);
Spring的兩大特性之二: AOP
AOP是一種編程思想熬的,跟IoC類型,并不是一種技術(shù)赊级,Spring AOP是實(shí)現(xiàn)了這種思想的一種技術(shù)押框。
最大作用就是將系統(tǒng)邏輯(權(quán)限驗(yàn)證,事務(wù)管理理逊,記錄操作日志等)和業(yè)務(wù)邏輯隔離開橡伞,提高了程序的可重用性,降低耦合度晋被。在Spring AOP中骑歹,它甚至將事務(wù)邏輯都幫你寫好了。
AOP術(shù)語:Joinpoint墨微,Pointcut,Advice扁掸,Aspect
Joinpoint:連接點(diǎn)翘县,被攔截到需要被增強(qiáng)的方法。
Pointcut:切入點(diǎn)谴分,多個(gè)連接點(diǎn)的集合锈麸,一般指哪些包中的哪些類。
Advice:通知/增強(qiáng)牺蹄,攔截到Joinpoint之后要做的事情忘伞,可以分為前置增強(qiáng),后置增強(qiáng)沙兰,異常增強(qiáng)氓奈,最終增強(qiáng),環(huán)繞增強(qiáng)鼎天,就是在執(zhí)行Joinpoint的前后左右添加新的增強(qiáng)功能舀奶。
Aspect:切面:Pointcut + Advice,哪些地方斋射,在什么時(shí)候育勺,做什么增強(qiáng)。
Target:目標(biāo)對(duì)象罗岖,代理的目標(biāo)對(duì)象
Weaving:織入涧至,是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來創(chuàng)建新的代理對(duì)象的過程。
Proxy:代理桑包,一個(gè)類被AOP織入增強(qiáng)后南蓬,產(chǎn)生的代理類。
總結(jié):Joinpoint,某一個(gè)方法蓖康,多個(gè)Joinpoint集合成Pointcut铐炫。Pointcut + Advice 組合成Aspect。把Advice 織入(Weaving)到Target中蒜焊,得出Proxy對(duì)象倒信。
靜態(tài)代理和動(dòng)態(tài)代理
靜態(tài)代理 :在Spring容器啟動(dòng)的時(shí)候就已經(jīng)確定好了代理類,只能服務(wù)于一種類型的對(duì)象泳梆。
動(dòng)態(tài)代理 :在程序運(yùn)行期間由JVM通過反射產(chǎn)生鳖悠,不存在字節(jié)碼文件。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定优妙。
1. Spring 基于AspectJ的AOP使用之xml方式
首先要編寫增強(qiáng)類乘综,也就是要做什么增強(qiáng)。在一個(gè)增強(qiáng)類里面可以寫多個(gè)增強(qiáng)方法套硼,但是增強(qiáng)的時(shí)候只會(huì)選擇其中一個(gè)執(zhí)行卡辰。
public class MyAdvice {
public void log(){
System.out.println("記錄日志....");
}
}
其次配置增強(qiáng),將增強(qiáng)類交給Spring容器管理
<bean name="myAdvice" class="com.pillow.advice.MyAdvice"></bean>
最后配置aop切面
<aop:config>
<!-- ref: 配置通知邪意、增強(qiáng) -->
<aop:aspect ref="myAdvice">
<aop:before method="log" pointcut="execution(void com.pillow.service.PersonServiceImpl.save(*))"></aop:before>
</aop:aspect>
</aop:config>
切入點(diǎn)表達(dá)式:execution([修飾符] 返回值類型 包名.類名.方法名(參數(shù)))
返回值類型:可以用 * 通配符
包名:可以用 * 代替一級(jí)包的通配符九妈,可以用 .. 省略中間包名
類名:可以用 * 通配符,也可以寫成 *ServiceImpl
方法名:可以用 * 通配符雾鬼,也可以寫成 *SaveImpl
參數(shù):可以用 * 通配符萌朱,多個(gè)參數(shù),可以使用 .. 代替
通知:同一個(gè)方法可以配置多個(gè)通知
通知類型:前置通知策菜,后置通知晶疼,最終通知,環(huán)繞通知又憨,異常拋出通知
前置通知
執(zhí)行時(shí)機(jī):目標(biāo)對(duì)象方法之前執(zhí)行
配置文件:<aop:before ...>
后置通知
執(zhí)行時(shí)機(jī):目標(biāo)對(duì)象方法之后之后翠霍,拋出異常則不會(huì)執(zhí)行
配置文件:<aop:after-returning ...>
最終通知
執(zhí)行時(shí)機(jī):目標(biāo)對(duì)象方法之后執(zhí)行通知,有沒有異常都會(huì)執(zhí)行蠢莺,順序在后置通知之后
配置文件:<aop:after ...>
環(huán)繞通知
執(zhí)行時(shí)機(jī):目標(biāo)對(duì)象方法之前和之后都會(huì)執(zhí)行壶运,包含before和after-returning
配置文件:<aop:around ...>
異常拋出通知
執(zhí)行時(shí)機(jī):在拋出異常時(shí)通知
配置文件:<aop:after-throwing ...>
2. Spring 基于AspectJ的AOP使用之xml和注解混合方式
編寫切面類,注意不是通知類浪秘,是切面類蒋情,包含了通知和切入點(diǎn)。
@Component
@Aspect
public class MyAspect {
@Before(value = "execution(* *..*.*save(*))")
public void beforce() {
System.out.println("前置通知耸携。棵癣。。");
}
@After(value = "execution(* *..*.*save(*))")
public void after() {
System.out.println("最終通知夺衍。狈谊。。");
}
@AfterReturning(value = "execution(* *..*.*save(*))")
public void afterReturn() {
System.out.println("后置通知。河劝。壁榕。");
}
}
@Component 注解 再配置組件掃描,將該切面類交給Spring管理赎瞎。
<context:component-scan base-package="com.pillow.*"></context:component-scan>
最后開啟AOP自動(dòng)代理
<!-- AOP基于注解的配置牌里,開啟自動(dòng)代理 -->
<aop:aspectj-autoproxy/>
2. Spring 基于AspectJ的AOP使用之純注解方式
純注解方式重點(diǎn)在于轉(zhuǎn)換混合方式配置組件掃描和開啟自動(dòng)代理兩種xml配置。
@Configuration
@ComponentScan(basePackages = "com.pillow")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
@EnableAspectJAutoProxy 開啟自動(dòng)代理
Spring聲明式事務(wù)
事務(wù)务甥,真正的實(shí)現(xiàn)是由底層的數(shù)據(jù)庫進(jìn)行實(shí)現(xiàn)的牡辽,而Spring是對(duì)數(shù)據(jù)庫底層進(jìn)行一些簡單的設(shè)置。配置好了以后敞临,Spring會(huì)讀取配置态辛,傳遞給數(shù)據(jù)庫,使數(shù)據(jù)庫進(jìn)行一些響應(yīng)的改變挺尿。
需要引入依賴包 spring-tx
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
事務(wù)的四大特性:一致性奏黑,原子性,隔離性编矾,持久性
隔離性將會(huì)導(dǎo)致一系列并發(fā)問題:
臟讀:事務(wù)A讀取到了事務(wù)B未提交的數(shù)據(jù)攀涵,事務(wù)B回滾后,事務(wù)A的數(shù)據(jù)就是錯(cuò)的洽沟。
不可重復(fù)度:事務(wù)A第一次讀取數(shù)據(jù)時(shí),事務(wù)B未開始蜗细。事務(wù)B執(zhí)行了update操作裆操,并提交的事務(wù),A再讀取時(shí)炉媒,發(fā)現(xiàn)兩次讀取的數(shù)據(jù)不一致踪区。
幻讀:事務(wù)A第一次讀取時(shí),事務(wù)B未開始吊骤。事務(wù)B執(zhí)行了insert缎岗,delete操作,并提交的事務(wù)白粉,A再讀取時(shí)传泊,發(fā)現(xiàn)兩次讀取的數(shù)據(jù)不一致。
針對(duì)不同的并發(fā)問題制定了不同的隔離級(jí)別
read uncommitted:讀未提交鸭巴,不能解決任何問題
read committed:讀已提交眷细,可以解決臟讀。Oracle默認(rèn)級(jí)別鹃祖。
repeatable read:可重復(fù)度溪椎,可以解決臟讀和不可重復(fù)讀。Mysql默認(rèn)級(jí)別,Mysql中幻讀也不會(huì)出現(xiàn)校读。
Serializable:串行化沼侣,事務(wù)提交了一個(gè)才進(jìn)行下一個(gè),可以解決所有問題歉秫,性能也最差蛾洛。
1. Spring聲明式事務(wù) 基于xml方式的應(yīng)用
<!-- 配置事務(wù)三步驟:1. 配置平臺(tái)事務(wù)管理器,注入數(shù)據(jù)源 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事務(wù)三步驟:2. 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="list*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置事務(wù)三步驟:3. 配置切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.pillow.service.*ServiceImpl.*(..))" id="txPoint" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
</aop:config>
2. Spring聲明式事務(wù) 基于xml和注解混合的使用
在service類上或者方法上加注解:@Transactional
@Transactional
加在類上端考,表明該類所有方法都被事務(wù)管理
加在方法上雅潭,表明僅僅該方法被事務(wù)管理
<!-- 配置事務(wù) -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 開啟事務(wù)注解 -->
<tx:annotation-driven transaction-manager="txManager"/>
3. Spring聲明式事務(wù) 基于純注解的使用
純注解方式在于解決混合用法的開啟事務(wù)注解的xml處理。
@EnableTransactionManagement 貼在配置類上即可却特。
@ComponentScan(basePackages = "com.pillow")
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfiguration {
}