百度百科的介紹java
Spring官方網址: http://spring.io/mysql
咱們常常說的Spring其實指的是 Spring Framework (Spring 框架)程序員
耦合性(Coupling),也叫耦合度,是對模塊間關聯程度的度量。web
在軟件工程中,耦合指的就是對象之間的依賴性。對象之間的耦合越高,維護成本越高。所以對象的設計應使類與架構之間的耦合最小。軟件設計中一般用耦合度和內聚度做爲衡量模塊獨立程度的標準。劃分模塊的一個準則就是高內聚低耦合。spring
內聚標誌一個模塊內各個元素彼此結合的緊密程度,它是信息隱蔽和局部化概念的天然擴展。內聚是從功能角度來度量模塊內的聯繫,一個好的內聚模塊應當適當作一件好事。sql
內聚和耦合是密切相關的,同其餘模塊存在高耦合的模塊意味着低內聚,而高內聚的模塊意味着該模塊同其餘模塊之間是低耦合。在進行軟件設計時,應力爭作到高內聚,低耦合。數據庫
IoC(核心中的核心):Inverse of Control,控制反轉。對象的建立權力由程序反轉給Spring框架。express
AOP:Aspect Oriented Programming,面向切面編程。在不修改目標對象的源代碼狀況下,加強IoC容器中Bean的功能。apache
DI:Dependency Injection,依賴注入。在Spring框架負責建立Bean對象時,動態的將依賴對象注入到Bean組件中!!編程
Spring容器:指的就是IoC容器。
所謂的IoC容器就是指的是Spring中Bean工廠裏面的Map存儲結構(存儲了Bean的實例)。
ApplicationContext接口()
實現了BeanFactory接口
實現ApplicationContext接口的工廠,能夠獲取到容器中具體的Bean對象
BeanFactory工廠(是Spring架構早期的建立Bean對象的工廠接口)
實現BeanFactory接口的工廠也能夠獲取到Bean對象
其實經過源碼分析,無論是BeanFactory仍是ApplicationContext,其實最終的底層BeanFactory都是DefaultListableBeanFactory
ApplicationContext和BeanFactory的區別?
建立Bean對象的時機不一樣:
BeanFactory採起延遲加載,第一次getBean時纔會初始化Bean。
ApplicationContext是加載完applicationContext.xml時,就建立具體的Bean對象的實例。(只對BeanDefition中描述爲時單例的Bean,才進行餓漢堡式加載)
ClassPathXmlApplicationContext:
它是從類的根路徑下加載配置文件 推薦使用這種
FileSystemXmlApplicationContext:
它是從磁盤路徑上加載配置文件,配置文件能夠在磁盤的任意位置。
AnnotationConfigApplicationContext:
當咱們使用註解配置容器對象時,須要使用此類來建立Spring容器。它用來讀取註解。
ApplicationContext context=new ClassPathXmlApplicationContext(xml路徑);
web.xml中配置ContextLoaderListener接口,並配置ContextConfigLocation參數
web容器啓動以後加載web.xml,此時加載ContextLoaderListener監聽器(實現了ServletContextListener接口,該接口的描述請見下面的《三類八種監聽器》)
ContextLoaderListener監聽器會在web容器啓動的時候,出發ContextInitialized()方法
ContextInitialized()方法會調用initWebApplicationContext()方法,該方法負責建立Spring容器(DefaultListableBeanFactory)
【Web三類八種監聽器】
監聽域對象的生命週期
ServletContextListener:
建立:服務器啓動
銷燬:服務器正常關閉
spring ContextLoaderListener(服務器啓動時負責加載spring配置文件)
HttpSessionListener
建立:第一次訪問request.getHttpSession()
銷燬:調用invalidate();非法關閉;過時
ServletRequestListener
建立:每一次訪問
銷燬:相應結束
監聽域對象的屬性:(添加、刪除、替換)
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
監聽HttpSession中JavaBean的改變:
HttpSessionBindingListener(HttpSession和JavaBean對象的綁定和解綁)
HttpSessionActivationListener(HttpSession的序列化,活化,純化)
參考資料中的源碼中的工程《Spring-sourcecode》
1.web服務器(tomcat)啓動會加載web.xml(啓動ContextLoaderListener監聽器):
2.建立web環境中的Spring容器
3.ContextLoader類中建立Spring容器並初始化容器中的Bean實例
4.configureAndRefreshWebApplicationContext方法中調用初始化Bean的refresh方法
該圖主要是分析上面第三步驟中【建立Spring容器】的圖示
源碼來源於AbstractApplicationContext類:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); //1.建立真正的Spring容器(DefaultListableBeanFactory) //2.加載BeanDefition(描述要初始化的Bean的信息) //3.將BeanDefition註冊到BeanDefitionRegistry // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); //執行實現了BeanFactoryPostProcessor接口的Bean //好比PropertyPlaceHolderConfigurer(context:property-placeholer)就是此處被調用的,替換掉BeanDefition中的佔位符(${})中的內容 // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //註冊BeanPostProcessor(後置處理器) //好比容器自動裝載了一個AutowiredAnnotationBeanPostProcessor後置處理器(實現@Autowired註解功能) // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); //初始化非懶加載方式的單例Bean實例 // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
ApplicationContext context = new ClassPathXmlApplicationContext(「spring.xml」)
ClassPathXmlApplicationContext類:重載的構造方法依次調用,進入下面代碼
AbstractApplicationContext的refresh方法:初始化spring容器的核心代碼
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //一、 Prepare this context for refreshing. prepareRefresh(); //建立DefaultListableBeanFactory(真正生產和管理bean的容器) //加載BeanDefition並註冊到BeanDefitionRegistry //經過NamespaceHandler解析自定義標籤的功能(好比:context標籤、aop標籤、tx標籤) //二、 Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //三、 Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { //四、 Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); //實例化並調用實現了BeanFactoryPostProcessor接口的Bean //好比:PropertyPlaceHolderConfigurer(context:property-placeholer) //就是此處被調用的,做用是替換掉BeanDefinition中的佔位符(${})中的內容 //五、 Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //建立並註冊BeanPostProcessor到BeanFactory中(Bean的後置處理器) //好比:AutowiredAnnotationBeanPostProcessor(實現@Autowired註解功能) // RequiredAnnotationBeanPostProcessor(實現@d註解功能) //這些註冊的BeanPostProcessor //六、 Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); //七、 Initialize message source for this context. initMessageSource(); //八、 Initialize event multicaster for this context. initApplicationEventMulticaster(); //九、 Initialize other special beans in specific context subclasses. onRefresh(); //十、 Check for listener beans and register them. registerListeners(); //建立非懶加載方式的單例Bean實例(未設置屬性) //填充屬性 //初始化實例(好比調用init-method方法) //調用BeanPostProcessor(後置處理器)對實例bean進行後置處理 //十一、 Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //十二、 Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
子流程入口(AbstractRefreshableApplicationContext類的方法)
咱們看到有兩種解析方案,先看看parseDefaultElement方法
至此,整個XML文檔的解析工做,包括bean標籤以及自定義標籤如何解析爲BeanDefinition信息的過程,咱們已經瞭解了。
後續具體想了解哪一個自定義標籤的處理邏輯,能夠自行去查找xxxNamespaceHandler類進行分析。
POM文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kkb</groupId> <artifactId>spring</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring 核心組件中的4個依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.0.7.RELEASE</version> </dependency> <!-- 單元測試Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <plugins> <!-- 配置Maven的JDK編譯級別 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
Spring配置文件(只編寫配置文件頭)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
具體實現
在Spring的XML配置文件中配置一個bean標籤,該標籤最終會被加載爲一個BeanDefition對象(描述對象信息)
思路:
編寫UserService接口的實現類
將UserService實現類交給Spring IoC容器管理
從Spring IoC容器中獲取UserService實現類
編寫接口:UserService
編寫實現類:UserServiceImpl
編寫XML文件:applicationContext.xml
編寫單元測試代碼:TestSpring
用於配置對象讓Spring來建立
默認狀況下它調用的是類中的無參構造函數。若是沒有無參構造函數則不能建立成功。
id:給對象在容器中提供一個惟一標識。用於獲取對象。
class:指定類的全限定類名。用於反射建立對象。默認狀況下調用無參構造函數。
scope:指定對象的做用範圍。
singleton:默認值,單例的(在整個容器中只有一個對象)。
prototype:多例的。
request:web項目中,Spring建立一個Bean的對象,將對象存入到request域中。
session:web項目中,Spring建立一個Bean的對象,將對象存入到session域中。
global session:web項目中,應用在Portlet環境,若是沒有Portlet環境那麼globalSession至關於session。
init-method:指定類中的初始化方法名稱。
destroy-method:指定類中銷燬方法名稱。好比DataSource的配置中通常須要指定destroy-method="close"。
單例對象:scope="singleton"
一個應用只有一個對象的實例。它的做用範圍就是整個應用
生命週期:
對象出生:當應用加載,建立容器時,對象就被建立了。
對象活着:只要容器在,對象一直活着。
對象死亡:當應用卸載,銷燬容器時,對象就被銷燬了。
多例對象:scope="prototype"
每次訪問對象時,都會從新建立對象實例。
生命週期:
對象出生:當使用對象時,建立新的對象實例。
對象活着:只要對象在使用中,就一直活着。
對象死亡:當對象長時間不用時,被java的垃圾回收期回收了。
第一種:使用默認無參構造函數(重點)
在默認狀況下:它會根據默認無參構造函數來建立類對象。
若是bean中沒有默認無參構造函數,將會建立失敗
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl">
第二種:靜態工廠(瞭解)
/** * 模擬一個靜態工廠,建立業務層實現類 */ public class StaticFactory { public static UserService createUserService(){ return new UserServiceImpl(); } }
<!--此種方法時: 使用StaticFactory類中的靜態方法createUserService建立對象,並存入Spring容器 id屬性:指定bean的id,用於從容器中獲取 class屬性:指定靜態工廠的全限定類名 factory-method屬性:指定生產對象的靜態方法 --> <bean id="userService" class="com.chenyanbin.spring.factory.StaticFactory" factory-method="createUserService"></bean>
第三種:實例工廠(瞭解)
/** * 模擬一個實例工廠,建立業務層實現類 * 此工廠建立對象,必須現有工廠實例對象,再調用方法 */ public class InstanceFactory { public UserService createUserService(){ return new UserServiceImpl(); } }
<!-- 此種方式是: 先把工廠的建立交給spring來管理。 而後在使用工廠的bean來調用裏面的方法 factory-ben屬性:用於指定實例工廠bean的id。 factory-method屬性:用於指定實例工廠中建立對象的方法。 --> <bean id="instanceFactory" class="com.chenyanbin.factory.InstanceFactory"></bean> <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService"></bean>
依賴指的就是Bean實例中的屬性
屬性分爲:簡單類型(8種基本類型和String類型)的屬性、POJO類型的屬性、集合數組類型的屬性。
依賴注入:Dependency Injection。它是Spring框架核心IoC的具體實現。
咱們的程序在編寫時,經過控制反轉,把對象的建立交給了Spring,可是代碼中不可能出現沒有依賴的狀況。
IoC解耦只是下降他們的依賴關係,但不會消除。例如:咱們的業務層仍會調用持久層的方法。那這種業務層和持久層的依賴關係,在使用Spring以後,就讓Spring來維護了。
簡單的說,就是坐等框架把持久層對象傳入業務層,而不用咱們本身去獲取。
顧名思義,就是使用類中的構造函數,給成員變量賦值。
注意,賦值的操做不是咱們本身作的,而是經過配置的方式,讓Spring框架來爲咱們注入。
具體代碼以下:
public class UserServiceImpl implements UserService { private int id; private String name; public UserServiceImpl(int id, String name) { this.id = id; this.name = name; } @Override public void saveUser() { System.out.println("保存用戶:id爲"+id+",name爲"+name+" Service實現"); } }
<!--使用構造函數的方式,給Service中的屬性傳值要求:類中須要提供一個對應參數列表的構造函數。 涉及的標籤:constructor-arg index:指定參數在構造函數參數列表的索引位置 name:指定參數在構造函數中的名稱 value:它能賦的值是基本數據類型和String類型 ref:他能賦的值是其餘bean類型,也就是說,必須得是在配置文件中配置過的bean --> <bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl"> <constructor-arg name="id" value="1"></constructor-arg> <constructor-arg name="name" value="zhangsan"></constructor-arg> </bean>
set方法注入又分爲手動裝配方式注入和自動裝配方式注入。
手動裝配方式(XML方式):bean標籤的子標籤property,須要在類中指定set方法。
自動裝配方式(註解方式):@Autowired註解、@Resource註解。
@Autowired:一部分功能是查詢實例,從spring容器中根據類型(java類)獲取對應的實例。另外一部分功能就是賦值,將找到的實例,裝配和另外一個實例的屬性值。(注意事項:一個java類型在同一個spring容器中,只能有一個實例)
@Resource:一部分功能是查詢實例,從spring容器中根據Bean的名稱(Bean標籤的名稱)獲取對應的實例。另外一部分功能就是賦值,將找到的實例,裝配給另外一個實例的屬性值。
使用p名稱空間注入數據(本質上仍是調用set方法)
1.步驟一:須要先引入p名稱空間
在schema的名稱空間中加入該行:
xmlns:p="http://www.springframework.org/schema/p"
2.步驟二:使用p名稱空間的語法
p:屬性名=""
p:屬性名-ref=""
3.步驟三:測試
<bean id="person" class="com.chenyanbin.spring.demo.Person" p:pname="隔壁老王" p:car2-ref="car2" /> <bean id="car2" class="com.chenyanbin.spring.demo.Car2" />
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl">
<property name="id" value="1"></property>
<property name="name" value="zhangsan"></property>
</bean>
ref就是reference的縮寫,是引用的意思
<bean id="userService" class="com.chenyanbin.spring.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.chenyanbin.spring.dao.UserDaoImpl"></bean>
1.若是是數組或者List集合,注入配置文件的方式是同樣的
<bean id="collectionBean" class="com.chenyanbin.demo5.CollectionBean">
<property name="arrs">
<list>
<!--若是集合內是簡單類型,使用value子標籤,若是是POJO類型,則使用bean標籤-->
<value>張三</value>
<value>李四</value>
<value>王五</value>
</list>
</property>
</bean>
2.若是是Set集合,注入的配置文件方式以下:
<property name="sets"> <set> <!--若是集合內是簡單類型,使用value子標籤,若是是POJO類型,則使用bean標籤--> <value>哈哈</value> <value>喜喜</value> </set> </property>
3.若是是Map集合,注入的配置方式以下
<property name="map"> <map> <entry key="老王" value="18" /> <entry key="王五" value="19" /> </map> </property>
4.若是是Properties集合的方式,注入的配置以下:
<property name="pro"> <props> <prop key="userName">root</prop> <prop key="userPassword">123</prop> </props> </property>
學習基於註解的IoC配置,你們腦海裏首先得有一個認識,即註解配置和xml配置要實現的功能都是同樣的,都是要下降程序間的耦合。只是配置的形式不同。
關於實際的開發中到底使用xml仍是註解,每家公司有着不一樣的使用習慣。因此這兩種配置方式都須要掌握。
在講解註解配置時,採用上一章節的案例,把Spring的xml配置內容改成使用註解逐步實現。
至關於:<bean id="" class=""></bean>
@Component註解
做用:
把資源讓Spring來管理。至關於在XML中配置一個bean。
屬性:
value:指定bean的id。
若是不指定value屬性,默認bean的id時當前類的類型。首字母小寫。
@Controller、@Service、@Repository註解
他們三個註解都是針對@Component的衍生註解
他們的做用及屬性都是一摸同樣的。他們只不過是提供了更加明確的語義化。
@Controller:通常用於表現層的註解。
@Service:通常用於業務層的註解。
@Repository:通常用於持久層的註解。
細節:若是註解中有且只有一個屬性要賦值時,且名稱是value,value在賦值是能夠不寫。
至關於:<property name="" ref="">
@Autowired
默認按類型裝配(byType)
這個註解是spring自身的
默認狀況下必需要求依賴對象必須存在,若是要容許null值,能夠設置它的required屬性爲false,如:@Autowired(required=false)
如何咱們想使用名稱裝配能夠結合@Qualifier註解進行使用
@Qualifier
在自動按照類型注入的基礎之上,再按照Bean的id注入。
它在給字段注入時不能獨立使用,必須和@Autowire一塊兒使用;可是給方法參數注入時,能夠單獨使用。
@Resource
默認按照名稱(byName)進行裝配,名稱能夠經過name屬性進行指定,若是沒有指定name屬性,當註解寫在字段上時,默認取字段名進行按照名稱查詢,當找不到域名稱匹配的bean時才按照類型進行裝配。
可是須要注意的是,若是name屬性一旦指定,就只會按照名稱進行裝配。
推薦使用@Resource註解,由於這個註解是屬於J2EE的,減小了與spring的耦合。這樣代碼看起來就比較優雅。
至關於:<property name="" value="">
@Value
給基本類型和String類型注入值
可使用佔位符獲取屬性文件中的值。
@Value("${name}") //name是properties文件中的key
用例
UserServiceImpl.java
@Value("${id}") private int id; @Override public void saveUser() { // TODO Auto-generated method stub System.out.println("IoC演示之UerService:"+id); }
applicationContext.xml
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 組件掃描器,主要是spring使用,用來掃描帶有指定註解的類,將這些加載成BeanDefinition --> <context:component-scan base-package="com.cyb.spring.service" /> <!-- 佔位符,location:路徑 --> <context:property-placeholder location="classpath:data.properties"/> </beans>
data.properties
id=123
@Scope
至關於:<bean id="" class="" scope="">
做用:
指定bean的做用範圍
屬性:
value:指定範圍的值
取值:singleton prototype request session globalsession
至關於:<bean id="" class="" init-method="" destroy-method="">
@PostConstruct和@PreDestroy
註解的優點:
配置簡單,維護方便(咱們找到類,就至關於找到了對應的配置)。
XML的優點:
修改時,不用改源碼。不涉及從新編譯和部署。
Spring管理Bean方式的比較
到這裏,基於註解的IoC配置已經完成,可是你們都發現了一個問題:咱們依然離不開spring的xml配置文件,那麼能不能不寫這個applicationContext.xml,全部配置都用註解來實現呢?
須要注意如下,咱們選擇那種配置的原則是簡化開發和配置方便,而非追求某種技術。
想想能不能將如下這些bean的配置都從xml中去掉,而且最終將xml也去掉。
若是能夠,那麼咱們就能夠脫離xml配置了。
<!--開啓註解並掃描指定包中帶有註解的類-->
<context:component-scan base-package="com.chenyanbin.spring.service" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" value="dataSource"></property>
</bean>
以前建立ApplicationContext都是經過讀取XML文件進行建立的。 ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
介紹:
從Spring3.0,@Configuration用於定義配置類,可替換XML配置文件
至關於<beans>根標籤
配置類內部包含有一個或多個被@Bean註解的方法,這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進行掃描,並用於構建bean定義,初始化Spring容器。
屬性:
value:用於指定配置類的字節碼
示例代碼:
@Configuration public class SpringConfiguration { //spring容器初始化時,會調用配置類的無參構造函數 public SpringConfiguration(){ System.out.println(「容器啓動初始化。。。」); } }
介紹:
@Bean標註在方法上(返回某個實例的方法),等價於spring配置文件中的<bean>
做用:
註冊bean對象
主要用來配置非自定義的bean,好比DruidDataSource、SqlSessionFactory
屬性:
name:給當前@Bean註解方法建立的對象指定一個名稱(即bean的id)。
若是不指定,默認與標註的方法名相同
@Bean註解默認做用域爲單例singleton做用域,可經過@Scope("prototype")設置爲原型做用域;
示例代碼:
public class SpringConfiguration { //spring容器初始化時,會調用配置類的無參構造函數 public SpringConfiguration(){ System.out.println(「容器啓動初始化。。。」); } @Bean @Scope(「prototype」) public UserService userService(){ return new UserServiceImpl(1,「張三」); } }
介紹:
至關於context:component-scan標籤
組件掃描器,掃描@Component、@Controller、@Service、@Repository註解的類。
該註解是編寫在類上面的,通常配合@Configuration註解一塊兒使用。
屬性:
basePackages:用於指定要掃描的包
value:和basePackages做用同樣
示例代碼:
Bean類(Service類):
@Service public class UserServiceImpl implements UserService { @Override public void saveUser() { System.out.println("保存用戶 Service實現"); } }
配置類:
@Configuration @ComponentScan(basePackages="com.kkb.spring.service") public class SpringConfiguration { public SpringConfiguration() { System.out.println("容器初始化..."); } // @Bean // @Scope("prototype") // public UserService userService() { // return new UserServiceImpl(1,"張三"); // } }
介紹
加載properties配置文件
編寫在類上面
至關於 context:property-placeholder 標籤
屬性
value[]:用於指定properties文件路徑,若是在類路徑下,須要寫上classpath
示例代碼
配置類:
@Configuration @PropertySource(「classpath:jdbc.properties」) public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** *建立一個數據源,並存入 spring 容器中 *@return */ @Bean(name="dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass(driver); ds.setJdbcUrl(url); ds.setUser(username); ds.setPassword(password); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
properties文件:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///spring jdbc.username=root jdbc.password=root
問題:
當系統中有多個配置類時怎麼辦?想想以前使用XML配置的時候時如何解決該問題的。
介紹
用來組合多個配置類
至關於spring配置文件中的Import標籤
在引入其餘配置類時,能夠不用再寫@Configuration註解。當前,寫上也沒問題。
屬性
value:用來指定其餘配置類的字節碼文件
示例代碼
@Configuration @ComponentScan(basePackages = "com.kkb.spring") @Import({ JdbcConfig.class}) public class SpringConfiguration { } @Configuration @PropertySource("classpath:jdbc.properties") public class JdbcConfig{ }
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class); UserService service = context.getBean(UserService.class); service.saveUser();
<web-app> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context. support.AnnotationConfigWebApplicationContext </param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value> com.kkb.spring.test.SpringConfiguration </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app>
分模塊開發場景描述
表現層:spring配置文件,只想管理表現層的Bean
業務層:spring配置文件,只想管理業務層的Bean,而且進行事務控制
持久層:spring配置文件,只想管理持久層的Bean,而且還有須要管理數據源的Bean
爲了方便管理項目中不一樣層的Bean對象,通常都是將一個spring配置文件,分解爲多個spring配置文件。
分解以後的spring配置文件如何一塊兒被加載呢?
一種就是同時指定多個配置文件的地址一塊兒加載
另外一種就是:定義一個import.xml文件,經過import標籤將其餘多個spring配置文件導入到該文件中,tomcat啓動時只須要加載import.xml就能夠。
@Autowired註解,它是如何生效的?AutowiredBeanPostProcessor類
XML配置方式
註解+XML配置方式
@Component
@Controller
@Service
@Repository
@Autowired
@Resource
純註解方式
@Configuration
@Bean
@ComponentScan
@PropertySource
@Import
在測試類中,每一個測試方法都有如下兩行代碼:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService service1 = context.getBean(UserService.class);
咱們使用單元測試要測試的是業務問題,以上兩端代碼明顯不是業務代碼。
可是這兩行代碼的做用是獲取容器,若是不寫的話,直接會提示空指針異常。因此又不能輕易刪掉。
針對上述問題,咱們須要的是程序能自動幫咱們建立容器。一旦程序能自動爲咱們建立spring容器,咱們就無需手動建立了,問題也就解決了。
但緊接的問題就是Juit它自己不認識spring,更沒法幫助建立spring容器了,不過好在Junit給咱們暴露一個註解(@RunWith),可讓咱們替換掉它的運行器。
這時,咱們須要依靠spring框架,由於它提供了一個運行器,能夠讀取配置文件(或註解)來建立容器。咱們只需告訴它配置文件在哪就好了。
添加spring-test包便可
<!-- spring單元測試組件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> </dependency>
Spring的運行器是SpringJunit4ClassRunner
package com.cyb.spring.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.cyb.spring.configuration.SpringConfiguration; import com.cyb.spring.service.UserService; //@RunWith:Junit自身的註解,它的做用是能夠指定一個新的運行器 //SpringJUnit4ClassRunner.class:spring提供的單元測試運行器 @RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration:SpringJUnit4ClassRunner運行器須要的上下文配置信息,方便建立spring容器 //classes:純註解方式時,讀取配置類 //locations:xml方式時,讀取配置文件 @ContextConfiguration(classes = SpringConfiguration.class) //@ContextConfiguration(locations = "classpath:applicationContext.xml") public class TestSpring2 { @Resource private UserService service; //執行@Test方法以前調用該方法 @Test public void test1() { service.saveUser(); } }
什麼是AOP?
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程
AOP是一種編程範式,隸屬於軟工範疇,指導開發者如何組織程序結構
AOP最先由AOP聯盟的組織提出的,制定了一套規範,spring將AOP思想引入到框架中,必須遵照AOP聯盟的規範
經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術
AOP是OOP的延續,是軟件開發中的一個熱點,也是spring框架中的一個重要內容,是函數式編程的一種衍生範式
利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率
做用:
AOP採用橫向抽取機制,取代了傳統縱向繼承體系重複性代碼(性能監視、事務管理、安全檢查、緩存)
在程序運行期間,不修改源碼對已有方法進行加強。
將業務邏輯和系統處理的代碼(關閉鏈接、事務管理、操做日誌記錄)解耦。
優點:
AspectJ是一個java實現的AOP框架,它可以對java代碼進行AOP編譯(通常在編譯期進行),讓java代碼具備AspectJ的AOP功能(固然須要特殊的編譯器)
能夠這樣說AspectJ是目前實現AOP框架中最成熟,功能最豐富的語言,更幸運的是,AspectJ與java程序徹底兼容,幾乎是無縫關聯,所以對於有java編程基礎的工程師,上手和使用很是容易。
瞭解AspectJ應用到java代碼的過程(這個過程稱爲織入),對於織入這個概念,能夠簡單理解爲Aspect(切面)應用到目標函數(類)的過程。
對於這個過程,通常分爲動態織入和靜態織入,動態織入的方式是在運行時動態將要加強的代碼織入到目標類中,這樣每每時經過動態代理技術完成的。如java JDK的動態代理(Proxy,底層經過反射實現)或者CGLIB的動態代理(底層經過繼承實現),Spring AOP採用的就是基於運行時加強的代理技術。
AspectJ採用的就是靜態織入的方式。AspectJ主要採用的是編譯期織入,這個期間使用AspectJ的acj編譯器(相似javac)把aspect類編譯成class字節碼後,在java目標類編譯時織入,即先編譯aspect類再編譯目標類。
Spring AOP是經過動態代理技術實現的,而動態代理是基於反射設計的。
動態代理技術的實現方式有兩種:基於接口的JDK動態代理和基於繼承的CGLib動態代理。
JDK動態代理
目標對象必須實現接口
使用Proxy類來生成代理對象的一些代碼以下
/** * 使用JDK的方式生成代理對象 * @author Administrator */ public class MyProxyUtils { public static UserService getProxy(final UserService service) { // 使用Proxy類生成代理對象 UserService proxy = (UserService) Proxy.newProxyInstance( service.getClass().getClassLoader(), service.getClass().getInterfaces(), new InvocationHandler() { // 代理對象方法一執行,invoke方法就會執行一次 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("save".equals(method.getName())){ System.out.println("記錄日誌..."); // 開啓事務 } // 提交事務 // 讓service類的save或者update方法正常的執行下去 return method.invoke(service, args); } }); // 返回代理對象 return proxy; } }
CGLib動態代理
目標對象不須要實現接口
底層是經過繼承目標對象產生代理子對象(代理子對象中繼承了目標對象的方法,並能夠對該方法進行加強)
/** * 使用CGLib動態代理技術實現 * 它是基於繼承的方式實現的 * @param service * @return */ public static UserService getProxyByClib(UserService service) { //建立加強器 Enhancer enhancer=new Enhancer(); //設置須要加強類的類對象 enhancer.setSuperclass(UserServiceImpl.class); //設置回調函數 enhancer.setCallback(new MethodInterceptor() { //methodProxy:代理以後的對象的方法引用 @Override public Object intercept(Object object, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { Long start= System.currentTimeMillis(); System.out.println("記錄程序開始時間。。。"+start); //由於代理對象是目標對象的子類 //該行代碼,實際調用的是目標對象的方法 //object:代理對象 Object object2=methodProxy.invokeSuper(object, arg); Long end= System.currentTimeMillis(); System.out.println("記錄程序結束時間。。。"+end); System.out.println("記錄總時長.."+(end-start)); return object2; } }); return (UserService)enhancer.create(); }
a、開發階段(咱們作的)
編寫核心業務代碼(開發主線):大部分程序員來作,要求熟悉業務需求。
把公司代碼抽取出來,製做成通知。(開發階段最後在作):AOP編程人員來作。
在配置文件中,聲明切入點與通知間的關係,即切面。:AOP編程人員來作。
b、運行階段(Spring框架完成的)
Spring框架監控切入點方法的執行。一旦監控到切入點方法被運行,使用代理機制,動態建立目標對象的代理對象,根據通知類別,在代理對象的對應位置,將通知對應的功能織入,完成完整的代碼邏輯運行。
編寫接口和實現類(目標對象)
UserService接口
UserServiceImpl實現類
配置目標類,將目標類交給Spring IoC容器管理
實現步驟
POM.XML
<!-- 基於AspectJ的aop依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency>
編寫通知(加強類,一個普通的類)
配置通知,將通知類交給Spring IoC容器管理
配置AOP切面
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
切入點表達式
execution([修飾符] 返回值 包名.類名.方法名(參數))
execution:必須有
修飾符:可省略
返回值類型:必需要,可是可使用*通配符
包名:
多級包之間使用.分割
包名可使用*代替,多級包名可使用多個*代替
若是想省略中間的報名可使用..
類名:
可使用*代替
也能夠寫成*DaoImpl
方法名:
也可使用*代替
也能夠寫成add*
參數:
參數使用*代替
若是有多個參數,可使用..代替
通知類型(五種):前置通知、後置通知、最終通知、環繞通知、異常拋出通知。
前置通知:
執行時機:目標對象方法以前執行通知
配置文件:<aop:before method="before" pointcut-ref="myPointcut" />
應用場景:方法開始時能夠進行校驗
後置通知:
執行時機:目標對象方法以後執行通知,有異常則不執行了
配置文件:<aop:after-returning method="afterReturning" pointcut-ref="myPointcut" />
應用場景:能夠修改方法的返回值
最終通知:
執行時機:目標對象方法以後執行通知,有沒有異常都會執行
配置文件:<aop:after method="after" pointcut-ref="myPointcut" />
應用場景:例如像釋放資源
環繞通知:
執行時機:目標對象方法以前和以後都會執行。
配置文件:<aop:around method="around" pointcut-ref="myPointcut" />
應用場景:事務、統計代碼執行時機
異常拋出通知:
執行時機:在拋出異常後通知
配置文件:<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" />
應用場景:包裝異常
實現步驟
編寫切面類(注意不是通知類,由於該類中能夠指定切入點)
配置切面類
<context:component-scan back-package="com.chenyanbin.spring" />
開啓AOP自動代理
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-demo7</groupId> <artifactId>spring-demo7</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- Spring IoC組件依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> </dependency> <!-- 單元測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- spring單元測試組件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> </dependency> <!-- 基於AspectJ的aop依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> </dependencies> <build> <plugins> <!-- 配置Maven的JDK編譯級別 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
applicationContext.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 組件掃描器,掃描切面類 --> <context:component-scan base-package="com.cyb.spring"></context:component-scan> <!-- 開啓AOP自動代理 --> <aop:aspectj-autoproxy /> </beans>
@Around
做用:
把當前方法當作是環繞通知屬性。
value:
用於指定切入點表達式,還能夠指定切入點表達式的引用。
使用@PointCut註解在切面類中定義一個通用的切入點,其餘通知能夠引用該切入點
@Configuration
@ComponentScan(basePackages="com.chenyanbin")
@EnableAspectJAutoProxy
public class SpringConfiguration{
}
POM.XML
編寫測試代碼
@Test public void run1(){ // 建立鏈接池,先使用Spring框架內置的鏈接池 DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///spring "); dataSource.setUsername("root"); dataSource.setPassword("root"); // 建立模板類 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); // 完成數據的添加 jdbcTemplate.update("insert into t_account values (null,?,?)", "測試",10000); }
步驟一:Spring管理內置的鏈接池
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_day03"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
步驟二:Spring管理模板類
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
步驟三:編寫測試程序
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Demo2{ @Resource(name="jdbcTemplate") private JdbcTemplate jdbcTemplate; @Test public void run(){ jdbcTemplate.update("insert into t_account values (null,?,?)", "測試2",10000); } }
管理DBCP鏈接池 * 先引入DBCP的2個jar包 * com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar * com.springsource.org.apache.commons.pool-1.5.3.jar 若是是maven環境,須要填寫GAV座標 * 編寫配置文件 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring "/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> 管理C3P0鏈接池 * 先引入C3P0的jar包 * com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar * 編寫配置文件 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql:///spring"/> <property name="user" value="root"/> <property name="password" value="root"/> </bean>
增刪改查的操做 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo { @Resource(name="jdbcTemplate") private JdbcTemplate jdbcTemplate; @Test // 插入操做 public void demo1(){ jdbcTemplate.update("insert into account values (null,?,?)", "冠希",10000d); } @Test // 修改操做 public void demo2(){ jdbcTemplate.update("update account set name=?,money =? where id = ?", "思雨",10000d,5); } @Test // 刪除操做 public void demo3(){ jdbcTemplate.update("delete from account where id = ?", 5); } @Test // 查詢一條記錄 public void demo4(){ Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 1); System.out.println(account); } @Test // 查詢全部記錄 public void demo5(){ List<Account> list = jdbcTemplate.query("select * from t_account", new BeanMapper()); for (Account account : list) { System.out.println(account); } } } class BeanMapper implements RowMapper<Account>{ public Account mapRow(ResultSet rs, int arg1) throws SQLException { Account account = new Account(); account.setId(rs.getInt("id")); account.setName(rs.getString("name")); account.setMoney(rs.getDouble("money")); return account; } }
1. 步驟一:建立WEB工程,引入須要的jar包 * IOC的6個包 * AOP的4個包 * C3P0的1個包 * MySQL的驅動包 * JDBC的2個包 * 整合JUnit測試包 2. 步驟二:引入配置文件 * 引入配置文件 * 引入log4j.properties * 引入applicationContext.xml <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql:///spring "/> <property name="user" value="root"/> <property name="password" value="root"/> </bean> 3. 步驟三:建立對應的包結構和類 * com.kkb.demo1 * AccountService * AccountServlceImpl * AccountDao * AccountDaoImpl 4. 步驟四:引入Spring的配置文件,將類配置到Spring中 <bean id="accountService" class="com.kkb.demo1.AccountServiceImpl"> </bean> <bean id="accountDao" class="com.kkb.demo1.AccountDaoImpl"> </bean> 5. 步驟五:在業務層注入DAO ,在DAO中注入JDBC模板(強調:簡化開發,之後DAO能夠繼承JdbcDaoSupport類) <bean id="accountService" class="com.kkb.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <bean id="accountDao" class="com.kkb.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> 6. 步驟六:編寫DAO和Service中的方法 public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { public void outMoney(String out, double money) { this.getJdbcTemplate().update("update t_account set money = money = ? where name = ?", money,out); } public void inMoney(String in, double money) { this.getJdbcTemplate().update("update t_account set money = money + ? where name = ?", money,in); } } 7. 步驟七:編寫測試程序. @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Demo1 { @Resource(name="accountService") private AccountService accountService; @Test public void run1(){ accountService.pay("冠希", "美美", 1000); } }
事務:指的是邏輯上一組操做,組成這個事務的各個執行單元,要麼一塊兒成功,要麼一塊兒失敗!
讀問題解決,設置數據庫隔離級別
1. 說明:Spring爲了簡化事務管理的代碼:提供了模板類 TransactionTemplate,因此手動編程的方式來管理事務,只須要使用該模板類便可!! 2. 手動編程方式的具體步驟以下: 1. 步驟一:配置一個事務管理器,Spring使用PlatformTransactionManager接口來管理事務,因此我們須要使用到他的實現類!! <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> 2. 步驟二:配置事務管理的模板 <!-- 配置事務管理的模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> 3. 步驟三:在須要進行事務管理的類中,注入事務管理的模板 <bean id="accountService" class="com.itheima.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> 4. 步驟四:在業務層使用模板管理事務: // 注入事務模板對象 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void pay(final String out, final String in, final double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { // 扣錢 accountDao.outMoney(out, money); int a = 10/0; // 加錢 accountDao.inMoney(in, money); } }); }
聲明式事務管理又分紅兩種方式
準備轉帳環境
業務層:
AccountService
AccountServiceImpl
持久層
AccountDao
AccountDaoImpl
spring配置
單元測試代碼:
配置事務管理的AOP
平臺事務管理器:DataSourceTransactionManager
事務通知:<tx:advice id="" transaction-manager="" />
AoP配置
<aop:config>
<aop:advisor advice-ref="" pointcut="" />
</aop:config>
Service類上或者方法上加註解:
類上加@Transactional:表示該類中全部方法都被事務管理
方法上加@Transactional:表示只有改方法被事務管理
開始事務註解: