Spring核心知識備忘總結(修正完善)

Spring簡介

Spring是一個輕量級的IoC和AOP容器框架。

Spring模塊

主要由以下幾個模塊組成:
Spring Core:核心類庫,提供IOC服務;
Spring Context:提供框架式的Bean訪問方式,以及企業級功能(JNDI、定時任務等);
Spring AOP:AOP服務;
Spring DAO:對JDBC的抽象,簡化了數據訪問異常的處理;
Spring ORM:對現有的ORM框架的支持;
Spring Web:提供了基本的面向Web的綜合特性,例如多方文件上傳;
Spring MVC:提供面向Web應用的Model-View-Controller實現。

spring優點:

IOC和DI實現了高內聚低耦合
AOP提高了代碼的複用率,也能使我們開發的時候更專注於核心業務
spring對其他主流框架提供了很好的集成支持


Spring工作原理:IOC(DI)和AOP

Spring IOC 容器:

 負責創建對象,裝配對象,並且管理這些對象的整個生命週期。

IOC理解:

 IOC就是控制反轉,以前我們創建對象是在代碼裏直接new, 耦合度高,IOC把對象的控制權反轉給了spring容器,由容器根據配置文件和註解來創建對象和管理對象之間的依賴,降低了對象之間的耦合度,也有利於代碼的複用。

DI理解:

 DI依賴注入,和IOC是同一個概念的不同角度的描述,就是在Spring創建對象時,動態的將依賴對象注入這個對象中,也就是自動地給創建對象的屬性賦值。

AOP理解:

參考:Spring-AOP實戰

AOP編程允許你把遍佈於應用各層的功能分離出來形成可重用的功能組件。

 AOP就是面向切面,作爲面向對象的一種補充,用於將那些與業務無關,但卻對多個對象產生影響的公共行爲和邏輯,抽取並封裝爲一個可重用的模塊,這個模塊被命名爲“切面”(Aspect),減少系統中的重複代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用於權限認證、日誌、事務處理等公共功能。
 AOP實現的關鍵在於 代理模式,AOP代理主要分爲靜態代理和動態代理。靜態代理的代表爲AspectJ;動態代理則以Spring AOP爲代表。

 1.AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱爲編譯時增強,他會在編譯階段將AspectJ(切面)織入到Java字節碼中,運行的時候就是增強之後的AOP對象。

  2. Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改字節碼,而是每次運行時在內存中臨時爲方法生成一個AOP對象,這個AOP對象包含了目標對象的全部方法,並且在特定的切點做了增強處理,並回調原對象的方法。
 Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理


Spring Bean的生命週期:

參考:https://www.cnblogs.com/zrtqsk/p/3735273.html

Servlet的生命週期:實例化,初始init,接收請求service,銷燬destroy;
在這裏插入圖片描述
Spring上下文中的Bean生命週期與之類似,如下:
在這裏插入圖片描述
(1)實例化Bean:
對於BeanFactory容器,當客戶向容器請求一個尚未初始化的bean時,或初始化bean的時候需要注入另一個尚未初始化的依賴時,容器就會調用createBean進行實例化。對於ApplicationContext容器,當容器啓動結束後,通過獲取BeanDefinition對象中的信息,實例化所有的bean。
(2)設置對象屬性(依賴注入):
實例化後的對象被封裝在BeanWrapper對象中,緊接着,Spring根據BeanDefinition中的信息 以及 通過BeanWrapper提供的設置屬性的接口完成依賴注入。
(3)調用Aware接口方法:Aware接口也是爲了讓Bean能夠感知到自身的一些屬性。
接着,Spring會檢測該對象是否實現了xxxAware接口,並將相關的xxxAware實例注入給Bean:
  ①如果這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的就是Spring配置文件中Bean的id值;
  ②如果這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory()方法,傳遞的是Spring工廠自身。
  ③如果這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文;
(4)調用BeanPostProcessor預初始化方法
 如果想對Bean在初始化前進行一些自定義的處理,那麼可以讓Bean實現了BeanPostProcessor接口,那將會調用postProcessBeforeInitialization(Object obj, String s)方法。由於這個方法是在實例初始化前調用的,所以可以被應用於內存或緩存技術;
(5)調用nitializingBean: afterPropertiesSet方法
(6)調用Bean配置的init-method方法:
 如果配置了Bean的 init-method ,則自動調用配置的初始化方法。
(7)調用BeanPostProcessor後初始化方法:
 Bean實現了BeanPostProcessor接口,則會調用postProcessAfterInitialization方法。
(8)1~6步驟之後bean已經可以正常使用了
(7)DisposableBean:
 當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean這個接口,會調用其實現的destroy()方法;
(9))調用Bean配置的destroy-method方法
 最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。

以上是SpringBean主要的生命週期模型,更完整的Spring Bean生命週期如下:
在這裏插入圖片描述
在這裏插入圖片描述


Spring理論知識參考

網上教程:spring教程
書籍:《Spring實戰 (第4版)》


Spring實戰-Spring框架整合

可以參考我的另外兩篇博文:
開發工具鏈-maven核心教程-Maven整合SSH(Spring,SpringMVC,Hibernate)
框架整合-SpringMVC+Spring+Mybatis


Spring實戰-常用配置

引用屬性文件

參考:Spring裏PropertyPlaceholderConfigurer類的使用

<!-- 引入外部屬性配置文件,供xml以${key}調用 -->
	<bean id="propertyConfigeurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:hibernate.properties</value>
				<value>classpath:redis.properties</value>
			</list>
		</property>
	</bean>

開啓註解

 <context:annotation-config />

<context:annotation-config /> 將隱式地向 Spring容器註冊:AutowiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。

配置包掃描

 <!--配置包掃描-->
  <context:component-scan base-package="com.aop.transaction.pay" />

提示:當使用 <context:component-scan/ > 後,就可以將\ <context:annotation-config/ > 移除了。

配置數據源datasource

1、使用org.springframework.jdbc.datasource.DriverManagerDataSource
 測試環境可採用driverManagerDataSource

	說明:DriverManagerDataSource建立連接是只要有連接就新建一個connection, 根本沒有連接池的作用。 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
              <property name="driverClassName"><value>${jdbc.driverClassName}</value></property> 
              <property name="url"><value>${jdbc.url}</value></property> 
              <property name="username"><value>${jdbc.username}</value></property> 
              <property name="password"><value>${jdbc.password}</value></property> 
 </bean> 

2、使用DBCP 、C3P0、Druid

說明:這3種方式真正使用了連接池技術 

數據庫連接池技術的思想非常簡單:

 將數據庫連接作爲對象存儲在一個Vector數組中,一旦數據庫連接建立後,不同的數據庫訪問請求就可以共享這些連接,這樣,通過循環利用這些已經建立好的數據庫連接,就可以節省我們去不斷創建新連接的是啊進和資源,極大地節省系統資源和時間。

 數據庫連接池的主要操作如下:
(1)建立數據庫連接池對象(服務器啓動)。
(2)按照事先指定的參數創建初始數量的數據庫連接(即:空閒連接數),我們在安裝數據庫的時候就可以指定。
(3)對於一個數據庫訪問請求,直接從連接池中得到一個連接。如果數據庫連接池對象中沒有空閒的連接,且連接數沒有達到最大(即:最大活躍連接數),創建一個新的數據庫連接。
(4)對數據庫進行存取操作。
(5)關閉數據庫,釋放所有數據庫連接(此時的關閉數據庫連接,並非真正關閉,而是將其放入空閒隊列中。如實際空閒連接 數大於初始空閒連接數則釋放連接)。
(6)釋放數據庫連接池對象(服務器停止、維護期間,釋放數據庫連接池對象,並釋放所有連接)。

DBCP數據源配置
 Tomcat 的連接池正是採用該連接池來實現的( tomcat7及以後使用,Tomcat jdbc pool,它能解決dbcp不能解決的一些問題)。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
              <property name="driverClassName"> 
                     <value>${jdbc.driverClassName}</value> 
              </property> 
              <property name="url"> 
                     <value>${jdbc.url}</value> 
              </property> 
              <property name="username"> 
                     <value>${jdbc.username}</value> 
              </property> 
              <property name="password"> 
                     <value>${jdbc.password}</value> 
              </property> 
              <property name="maxActive"> 
                     <value>100</value> 
              </property> 
              <property name="maxIdle"> 
                     <value>2</value> 
              </property> 
              <property name="maxWait"> 
                     <value>100000</value> 
              </property> 
       </bean> 

常見可選項:

  • defaultAutoCommit:設置從數據源中返回的連接是否採用自動提交機制,默認值爲 true;
  • defaultReadOnly:設置數據源是否僅能執行只讀操作, 默認值爲 false;
  • maxActive:最大連接數據庫連接數,設置爲0時,表示沒有限制;
  • maxIdle:最大等待連接中的數量,設置爲0時,表示沒有限制;
  • maxWait:最大等待秒數,單位爲毫秒, 超過時間會報出錯誤信息;
  • validationQuery:用於驗證連接是否成功的查詢SQL語句,SQL語句必須至少要返回一行數據, 如你可以簡單地設置爲:“select count(*) from user”;
  • removeAbandoned:是否自我中斷,默認是 false ;
  • removeAbandonedTimeout:幾秒後數據連接會自動斷開,在removeAbandoned爲true,提供該值;
  • logAbandoned:是否記錄中斷事件, 默認爲 false;

 
C3P0數據源配置
  c3p0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,實現了JDBC3和JDBC2擴展規範說明的 Connection 和Statement 池。目前使用它開源項目有Hibernate,Spring。(Hibernate項目推薦使用)

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"       
        destroy-method="close">      
   <property name="driverClass" value=" ${jdbc.driverClassName} "/>      
  <property name="jdbcUrl" value="${jdbc.url}"/>      
<property name="user" value="${jdbc.username}"/>      
 <property name="password" value="${jdbc.password}"/>      
</bean> 

相對來說,C3P0擁有比DBCP更豐富的可選配置屬性:

  • acquireIncrement:當連接池中的連接用完時,C3P0一次性創建新連接的數目;
  • acquireRetryAttempts:定義在從數據庫獲取新連接失敗後重復嘗試獲取的次數,默認爲30;
  • acquireRetryDelay:兩次連接中間隔時間,單位毫秒,默認爲1000;
  • autoCommitOnClose:連接關閉時默認將所有未提交的操作回滾。默認爲false;
  • automaticTestTable: C3P0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數,那麼屬性preferredTestQuery將被忽略。你 不能在這張Test表上進行任何操作,它將中爲C3P0測試所用,默認爲null;
  • breakAfterAcquireFailure:獲取連接失敗將會引起所有等待獲取連接的線程拋出異常。但是數據源仍有效保留,並在下次調 用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試獲取連接失敗後該數據源將申明已斷開並永久關閉。默認爲 false;
  • checkoutTimeout:當連接池用完時客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出SQLException,如設爲0則無限期等待。單位毫秒,默認爲0;
  • connectionTesterClassName: 通過實現ConnectionTester或QueryConnectionTester的類來測試連接,類名需設置爲全限定名。默認爲 com.mchange.v2.C3P0.impl.DefaultConnectionTester;
  • idleConnectionTestPeriod:隔多少秒檢查所有連接池中的空閒連接,默認爲0表示不檢查;
  • initialPoolSize:初始化時創建的連接數,應在minPoolSize與maxPoolSize之間取值。默認爲3;
  • maxIdleTime:最大空閒時間,超過空閒時間的連接將被丟棄。爲0或負數則永不丟棄。默認爲0;
  • maxPoolSize:連接池中保留的最大連接數。默認爲15;
  • maxStatements:JDBC的標準參數,用以控制數據源內加載的PreparedStatement數量。但由於預緩存的Statement屬 於單個Connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素,如果maxStatements與 maxStatementsPerConnection均爲0,則緩存被關閉。默認爲0;
  • maxStatementsPerConnection:連接池內單個連接所擁有的最大緩存Statement數。默認爲0;
  • numHelperThreads:C3P0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能,通過多線程實現多個操作同時被執行。默認爲3;
  • preferredTestQuery:定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個參數能顯著提高測試速度。測試的表必須在初始數據源的時候就存在。默認爲null;
  • propertyCycle: 用戶修改系統配置參數執行前最多等待的秒數。默認爲300;
  • testConnectionOnCheckout:因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的時候都 將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
    等方法來提升連接測試的性能。默認爲false;
  • testConnectionOnCheckin:如果設爲true那麼在取得連接的同時將校驗連接的有效性。默認爲false。
     
    Druid數據源配置
     阿里出品,淘寶和支付寶專用數據庫連接池,支持所有JDBC兼容的數據庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等,Druid針對Oracle和MySql做了特別優化。下載druid
    參考:https://www.cnblogs.com/wuyun-blog/p/5679073.html
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
  init-method="init" destroy-method="close"> 
  <property name="driverClassName" value="${jdbc.driverClassName}" /> 
  <property name="url" value="${jdbc.url}" /> 
  <property name="username" value="${jdbc.username}" /> 
  <property name="password" value="${jdbc.password}" /> 
  <!-- 配置初始化大小、最小、最大 --> 
  <property name="initialSize" value="1" /> 
  <property name="minIdle" value="1" /> 
  <property name="maxActive" value="10" />
  <!-- 配置獲取連接等待超時的時間 --> 
  <property name="maxWait" value="10000" />
  <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒 --> 
  <property name="timeBetweenEvictionRunsMillis" value="60000" />
  <!-- 配置一個連接在池中最小生存的時間,單位是毫秒 --> 
  <property name="minEvictableIdleTimeMillis" value="300000" />
  <property name="testWhileIdle" value="true" />
  <!-- 這裏建議配置爲TRUE,防止取到的連接不可用 --> 
  <property name="testOnBorrow" value="true" /> 
  <property name="testOnReturn" value="false" />
  <!-- 打開PSCache,並且指定每個連接上PSCache的大小 --> 
  <property name="poolPreparedStatements" value="true" /> 
  <property name="maxPoolPreparedStatementPerConnectionSize" 
   value="20" />
  <!-- 這裏配置提交方式,默認就是TRUE,可以不用配置 -->
  <property name="defaultAutoCommit" value="true" />
  <!-- 驗證連接有效與否的SQL,不同的數據配置不同 --> 
  <property name="validationQuery" value="select 1 " /> 
  <property name="filters" value="stat" /> 
  <property name="proxyFilters"> 
   <list> 
    <ref bean="logFilter" /> 
   </list> 
  </property> 
 </bean>
 <bean id="logFilter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter"> 
  <property name="statementExecutableSqlLogEnable" value="false" /> 
 </bean>

druid還提供一個監控頁面,通過以下配置即可訪問:
http://localhost:8080/druid (端口根據自己具體情況修改)

<!--druid web監控配置-->
<servlet> 
     <servlet-name>DruidStatView</servlet-name> 
     <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class> 
 </servlet> 
 <servlet-mapping> 
     <servlet-name>DruidStatView</servlet-name> 
     <url-pattern>/druid/*</url-pattern> 
 </servlet-mapping> 
 <filter> 
  <filter-name>druidWebStatFilter</filter-name> 
  <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class> 
  <init-param> 
   <param-name>exclusions</param-name> 
   <param-value>/public/*,*.js,*.css,/druid*,*.jsp,*.swf</param-value> 
  </init-param> 
  <init-param> 
   <param-name>principalSessionName</param-name> 
   <param-value>sessionInfo</param-value> 
  </init-param> 
  <init-param> 
   <param-name>profileEnable</param-name> 
   <param-value>true</param-value> 
  </init-param> 
 </filter> 
 <filter-mapping> 
  <filter-name>druidWebStatFilter</filter-name> 
  <url-pattern>/*</url-pattern> 
 </filter-mapping>

 
druid、dbcp、c3p0區別及適用場景:
參考:https://blog.csdn.net/wawa3338/article/details/81380662
1.druid與c3p0、dbcp區別:
 druid被稱爲是專門爲監控而生的數據庫連接池配置,他可以監控sql的執行情況,以及性能,然後根據結果來優化sql或者做性能調優。若有這種監控需求可以選用druid。

2.c3p0與dbcp區別:

c3p0 dbcp
對數據連接的處理方式 提供最大空閒時間 提供最大連接數
什麼時候連接掛斷 當連接超過最大空閒連接時間時 當連接數超過最大連接數時
連接資源是否釋放 自動回收連接 需要自己手動釋放資源
效率 效率比較高
原理 維護多個連接對象Connection,在web項目要連接數據庫時直接使用它維護的對象進行連接,省去每次都要創建連接對象的麻煩。提高效率和減少內存使用

3、使用org.springframework.jndi.JndiObjectFactoryBean
說明:JndiObjectFactoryBean 能夠通過JNDI獲取DataSource

<!--spring jnd連接池數據源配置-->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> 
              <property name="jndiName"><value>java:comp/env/jdbc/roseindiaDB_local</value></property> 
 </bean> 
<!--tomcat resource配置, 默認採用dbcp-->
<Resource 
    name="oracleDataSource" 
    auth="Container" 
    type="javax.sql.DataSource" 
    maxActive="50" 
    maxIdle="10" 
    maxWait="10000" 
    username="lead_oams" 
    password="p" 
    driverClassName="oracle.jdbc.OracleDriver" 
    url="jdbc:oracle:thin:@192.168.1.229:1521:lead"/>

補充-c3p0配置各種數據庫JNDI數據源示例(待續)

DBCP與C3P0配置項區別:

DBCP c3p0
用戶名 username user username
密碼 password password
URL url jdbcUrl
驅動類名 driverClassName driverClass driverClassName
<Context>
    <!-- 使用C3P0配置針對MySQL數據庫的JNDI數據源 -->
    <Resource 
        name="jdbc/MysqlDataSource" 
        auth="Container"
        factory="org.apache.naming.factory.BeanFactory" 
        type="com.mchange.v2.c3p0.ComboPooledDataSource"
        driverClass="com.mysql.jdbc.Driver"
        idleConnectionTestPeriod="60"
        maxPoolSize="50" 
        minPoolSize="2"
        acquireIncrement="2" 
        user="root" 
        password="root"
        jdbcUrl="jdbc:mysql://192.168.1.144:3306/leadtest"/>
        
    <!-- 使用C3P0配置針對Oracle數據庫的JNDI數據源 -->
    <Resource 
        name="jdbc/OracleDataSource" 
        auth="Container"
        factory="org.apache.naming.factory.BeanFactory" 
        type="com.mchange.v2.c3p0.ComboPooledDataSource"
        driverClass="oracle.jdbc.OracleDriver"
        idleConnectionTestPeriod="60"
        maxPoolSize="50" 
        minPoolSize="2"
        acquireIncrement="2" 
        jdbcUrl="jdbc:oracle:thin:@192.168.1.229:1521:lead"
        user="lead_oams"
        password="p"/>
        
        
    <!--使用C3P0配置針對SQLServer數據庫的JNDI數據源-->
    <Resource 
        name="jdbc/SqlServerDataSource"
        auth="Container"
        factory="org.apache.naming.factory.BeanFactory" 
        type="com.mchange.v2.c3p0.ComboPooledDataSource"
        driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
        idleConnectionTestPeriod="60"
        maxPoolSize="50" 
        minPoolSize="2"
        acquireIncrement="2" 
        jdbcUrl="jdbc:sqlserver://192.168.1.51:1433;DatabaseName=demo"
        user="sa" 
        password="p@ssw0rd"/>
</Context>

拓展:spring+hibernate配置多個數據源以及使用
思路:配置多個數據源、多個SessionFactory及事務,獲取對應的session來操作數據庫。


 

配置sessionFactory

<!--Hibernate-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" lazy-init="false">
        <!-- 注入datasource,給sessionfactoryBean內setdatasource提供數據源 -->
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
        <!-- //加載實體類的映射文件位置及名稱 -->
        <property name="mappingLocations" value="classpath:com/demo/ssm/po/*.hbm.xml"></property>
 </bean>  

<!-- mybatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
	<!-- 自動掃描mapping.xml文件 -->
	<property name="mapperLocations" value="classpath:com/leon/ssm/mapper/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring會自動查找其下的類 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.leon.ssm.mapper" />
		<property name="sqlSessionFactoryBeanName"
			value="sqlSessionFactory"></property>
	</bean>

配置事務管理

     1.配置SessionFactory(見上)
     2.配置事務容器TransactionManager
     3.配置事務傳播規則
     4.配置事務入口

(1)配置聲明式事務

 <!-- 配置Spring聲明式事務 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean> 
    <!-- 配置事務事務傳播屬性 -->
     <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagtion="REQUIRED"/>
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>
    <!-- 配置事務切點,並把切點和事務屬性關聯起來 -->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.demo.ssm.daoImpl.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
</beans>

(2)配置註解式事務

 <!--配置事物管理-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
 
    <!-- 配置註解方式管理事務 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

(3)aop攔截器實現事務管理

<!-- aop代理 -->
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <!-- 設置事務管理其 -->
        <property name="transactionManager" ref="transactionManager" />
        <!--設置事物屬性-->
        <property name="transactionAttributes">
            <props>
                <!-- 設置攔截方法,可以使用通配符 -->
                <!-- 可以設置事務的傳播行爲,隔離級別,多個值用逗號隔開 -->
                <prop key="transfer*">PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ</prop>
                <prop key="add*">PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ</prop>
                <prop key="insert*">PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ</prop>
            </props>
        </property>
    </bean>
    <!-- 定義BeanNameAutoProxyCreatorf進行Spring的事務處理 -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 針對指定的bean自動生成業務代理 -->
        <property name="beanNames" value="*Service" />
        <!-- 這個屬性爲true時,表示被代理的是目標類本身而不是目標類的接口[cglib] -->
        <property name="proxyTargetClass">
            <value>true</value>
        </property>
        <!-- 依賴注入上面定義的事務攔截器transactionInterceptor -->
        <property name="interceptorNames">
            <list>
                <value>transactionInterceptor</value>
            </list>
        </property>
    </bean>

補充-spring事務相關知識

事務傳播行爲

  • PROPAGATION_REQUIRED 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。
  • PROPAGATION_SUPPORTS 支持當前事務,如果當前沒有事務,就以非事務方式執行。
  • PROPAGATION_MANDATORY 使用當前的事務,如果當前沒有事務,就拋出異常。
  • PROPAGATION_REQUIRES_NEW 新建事務,如果當前存在事務,把當前事務掛起。
  • PROPAGATION_NOT_SUPPORTED 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  • PROPAGATION_NEVER 以非事務方式執行,如果當前存在事務,則拋出異常。
  • PROPAGATION_NESTED 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

事務隔離級別

  • ISOLATION_DEFAULT:這是個 PlatfromTransactionManager 默認的隔離級別,使用數據庫默認的事務隔離級別。
  • ISOLATION_READ_UNCOMMITTED:讀未提交,允許另外一個事務可以看到這個事務未提交的數據。
  • ISOLATION_READ_COMMITTED:讀已提交,保證一個事務修改的數據提交後才能被另一事務讀取,而且能看到該事務對已有記錄的更新。
  • ISOLATION_REPEATABLE_READ:可重複讀,保證一個事務修改的數據提交後才能被另一事務讀取,但是不能看到該事務對已有記錄的更新。
  • ISOLATION_SERIALIZABLE:一個事務在執行的過程中完全看不到其他事務對數據庫所做的更新。

補充-數據庫事務特性
事務的特性
①原子性:是指事務是一個不可分割的工作單元,事務中的操作要麼都發生,要麼都不發生。
eg:張三給李四轉錢,要麼張三的錢減少李四的增多,要麼兩個人的錢都不變。
②一致性:是指事務前後數據的完整性要保持一致。
eg:本來 張三有1000元 李四有1000元 一共2000。張三給李四轉賬100元,成功:張三900元,李四1100元 一共2000元。
③隔離性:是指是指多個用戶併發訪問數據庫的時候,一個用戶的事務不能被其他用戶的事務所幹擾,多個併發事務之間數要相互隔離。
④持久性:是指一個事務一旦提交,他對數據庫中數據的改變就是就是永久性的。
eg:張三 1000 李四 1000 張三給李四轉100 張三提交了,數據回滾不了了

不考慮隔離性,會引發下列問題
①髒讀:是指一個事務讀到另一個事務未提交的數據
②不可重複讀:在一個事務中,兩次查詢到的結果不一致(針對 update 操作)
③虛讀(幻讀):在一個事務中,兩次查詢到的結果不一致(針對 insert 操作)

通過設置事務隔離級別來解決讀的問題
①讀未提交(Read uncommitted):最低級別,上述情況都不能避免
②讀已提交(Read committed):可避免 髒讀 發生。Oracle默認隔離級別
③可重複讀(Repeatable read):可避免 髒讀、不可重複讀 發生。Mysql默認隔離級別
④串行化(Serializable):可避免 髒讀、不可重複讀、虛讀 發生


Spring實戰-常用註解

參考:《springMVC詳解以及註解說明》

@Autowired

 按類型自動裝配:它可以對類成員變量、方法及構造函數進行標註,完成自動裝配的工作。(在Spring 2.5 引入了 @Autowired 註釋)
(1)對成員變量使用 @Autowired 後,可以省略set ,get方法。
(2)對方法或構造函數進行標註,將查找被標註的方法的入參類型的 Bean,並調用方法自動注入這些 Bean。

	 spring推薦在構造函數上註解,因爲不考慮父類,初始化的順序:靜態變量或靜態語句塊–>實例變量或初始化語句塊–>構造方法–>@Autowired。

特殊情況:
a.@Autowired(required = false),這等於告訴 Spring:在找不到匹配 Bean 時也不報錯(一般在開發期才用)。
b.@Qualifier 註釋指定注入 Bean 的名稱,這樣歧義就消除
@Autowired
@Qualifier(“userJdbcImps”)
private UserRepository userRepository;
c.類似JSR-250 規範定義的註釋@Resource(詳見讓jsr-250生效的方法)

   與@Resource 區別:@Resource默認先按名稱匹配,不依賴spring框架。

使用@Autowired的原理:
  其實在啓動spring IoC時,容器自動裝載了一個AutowiredAnnotationBeanPostProcessor後置處理器,當掃描到@Autowied、@Resource或@Inject時,就會在IoC容器自動查找需要的bean,並裝配給該對象的屬性

 <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>  

注意事項:
   在使用@Autowired時,首先在容器中查詢對應類型的bean
    如果查詢結果剛好爲一個,就將該bean裝配給@Autowired指定的數據
    如果查詢的結果不止一個,那麼@Autowired會根據名稱來查找。
    如果查詢的結果爲空,那麼會拋出異常。解決方法時,使用required=false

@PostConstruct 、@PreDestroy(JSR-250常見方法)

 標註了 @PostConstruct 註釋的方法將在類實例化後調用,而標註了@PreDestroy的方法將在類銷燬之前調用。

@Component

 @Component是通用標註,不推薦使, 定義Bean,在類定義處使用, 通過 @Scope 指定 Bean 的作用範圍。
 @Component 有一個可選的入參,用於指定 Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義爲“boss”。
注意:在使用 @Component 註釋後,Spring 容器必須啓用類掃描機制以啓用註釋驅動 Bean 定義和註釋驅動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進行了擴展,提供了這一功能

<context:component-scan base-package="com.xxx.xx"/>

特殊語義的註釋:@Controller、@Service、@Respository

 @Controller標註web控制器,@Service標註Servicec層的服務,@Respository標註DAO層的數據訪問,他們包含@Component。

@Scope

 在使用XML 、註解定義Bean 時,可以通過bean 的scope 屬性來定義一個Bean 的作用範圍,默認是單例模式,即scope=“singleton”,效率較高。另外scope還有prototype、request、session、global session作用域。scope="prototype"多例,每次請求都新建一個實例,可防止輸出之前請求返回值。

@RequestMapping (用於springMVC)

@RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作爲父路徑。
RequestMapping註解有六個屬性:

1、 value, method;
value: 指定請求的實際地址,指定的地址可以是URI Template 模式(後面將會說明);
method: 指定請求的method類型, GET、POST、PUT、DELETE等;

2、 consumes,produces;
consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;
produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

3、 params,headers;
params: 指定request中必須包含某些參數值是,才讓該方法處理。
headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。

@RequestParam

用於參數綁定,實例:
xxxMethod(@RequestParam(“id”)int itemId) 中的將 id 綁定到 itemId 這個 URL 參數上
其他參數綁定常用的註解:

  • A、處理requet uri 部分(這裏指uri template中variable,不含queryString部分)的註解: @PathVariable;
  • B、處理request header部分的註解: @RequestHeader, @CookieValue;
  • C、處理request body部分的註解:@RequestParam, @RequestBody;
  • D、處理attribute類型是註解: @SessionAttributes, @ModelAttribute;

@ModelAttribute

  • @ModelAttribute 聲明在屬性上,表示該屬性的value 來源於model裏保存的數據,並被保存到model 裏
  • @ModelAttribute 聲明在方法上,表示該方法的返回值被保存到model 裏

@RequestBody

 該註解用於讀取Request請求的body部分數據,一般情況下來說常用其來處理application/json類型的請求數據。通過@requestBody可以將請求體中的JSON字符串綁定到相應的bean上,也可以將其分別綁定到對應的字符串上。
 例如說以下情況:

 $.ajax({
        url:"/login",
        type:"POST",
        data:'{"userName":"admin","pwd","admin123"}',
        content-type:"application/json charset=utf-8",
        success:function(data){
          alert("request success ! ");
        }
    });

    @requestMapping("/login")
    public void login(@requestBody String userName,@requestBody String pwd){
      System.out.println(userName+" :"+pwd);
    }

  這種情況是將JSON字符串中的兩個變量的值分別賦予了兩個字符串,但是呢假如我有一個User類,擁有如下字段:
      String userName;
      String pwd;
  那麼上述參數可以改爲以下形式:@requestBody User user 這種形式會將JSON字符串中的值賦予user中對應的屬性上,需要注意的是,JSON字符串中的key必須對應user中的屬性名,否則是請求不過去的。

@ResponseBody

 該註解用於將Controller的方法返回的對象,通過適當的HttpMessageConverter轉換爲指定格式後,寫入到Response對象的body數據區。
 使用時機:返回的數據不是html標籤的頁面,而是其他某種格式的數據時(如json、xml等)使用, 比如開發rest接口返回json數據。

@Transient(用於ORM框架,如Hibernate)

表示該屬性並非一個到數據庫表的字段的映射,ORM框架將忽略該屬性.

	區別java中的transient修飾符: 被transient的變量不能被序列化,一個靜態變量不管是否被transient修飾,均不能被序列化。

總結:註解配置和 XML 配置的適用場合

  對於類級別且不會發生變動的配置可以優先考慮註釋配置;而對於那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。
 合理地使用 Spring 2.5 的註釋配置,可以有效減少配置的工作量,提高程序的內聚性。但是這並不意味着傳統 XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數據源、緩存池、持久層操作模板類、事務管理等內容的配置上,XML
配置依然擁有不可替代的地位。


Spring Boot

 springboot並不是什麼新型的框架,而是整合了spring,springmvc等框架,默認了很多配置,從而減少了開發者的開發時間。
 雖然公司暫時沒用它,但我覺得它應該是未來使用spring的趨勢所在,應該提前接觸一下。

Hello World

一、maven構建項目

1、訪問http://start.spring.io/
2、選擇構建工具Maven Project、Spring Boot版本1.3.6以及一些工程基本信息,點擊“Switch to the full version.”java版本選擇1.7,可參考下圖所示:
在這裏插入圖片描述
3、點擊Generate Project下載項目壓縮包
4、解壓後,使用eclipse,Import -> Existing Maven Projects -> Next ->選擇解壓後的文件夾-> Finsh,OK done!
項目結構介紹
在這裏插入圖片描述
如上圖所示,Spring Boot的基礎結構共三個文件:

src/main/java 程序開發以及主程序入口
src/main/resources 配置文件
src/test/java 測試程序

另外,spingboot建議的目錄結構如下:

com
  +- example
    +- myproject
      +- Application.java
      |
      +- domain
      |  +- Customer.java
      |  +- CustomerRepository.java
      |
      +- service
      |  +- CustomerService.java
      |
      +- controller
      |  +- CustomerController.java
      |

Application.java 建議放到根目錄下面,主要用於做一些框架配置
domain目錄主要用於實體(Entity)與數據訪問層(Repository)
service 層主要是業務類代碼
controller 負責頁面訪問控制

採用默認配置可以省去很多配置,當然也可以根據自己的喜歡來進行更改
最後,啓動Application main方法,至此一個java項目搭建好了!
 
二、引入web模塊

1、pom.xml中添加支持web的模塊:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

pom.xml文件中默認有兩個模塊:
spring-boot-starter :核心模塊,包括自動配置支持、日誌和YAML;
spring-boot-starter-test :測試模塊,包括JUnit、Hamcrest、Mockito。

2、編寫controller內容:

@RestController
public class HelloWorldController {
@RequestMapping("/hello")
public String index() {
return “Hello World”;
}
}

	@RestController 的意思就是controller裏面的方法都以json格式輸出,不用再寫什麼jackjson配置的了!(這個spring4纔有,也可以用@Controller、@ResponeBody實現同樣功能)

3、啓動主程序,打開瀏覽器訪問http://localhost:8080/hello,就可以看到效果了,有木有很簡單!
 
三、如何做單元測試

 打開的src/test/下的測試入口,編寫簡單的http請求來測試;使用mockmvc進行,利用MockMvcResultHandlers.print()打印出執行結果。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class HelloWorldControlerTests {
    private MockMvc mvc;
    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build();
    }
    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }
}

四、開發環境的調試

 熱啓動在正常開發項目中已經很常見了吧,雖然平時開發web項目過程中,改動項目啓重啓總是報錯;但springBoot對調試支持很好,修改之後可以實時生效,需要添加以下的配置:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
 
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
            </configuration>
        </plugin>
</plugins>
</build>

 該模塊在完整的打包環境下運行的時候會被禁用。如果你使用java -jar啓動應用或者用一個特定的classloader啓動,它會認爲這是一個“生產環境”。

總結:SpringBoot繼承了spring之前所有的優點,簡化了使用Spring的過程,簡直是廣大程序員的福音~:)

參考:SpringBoot從入門到精通教程
 
後記: 花了2個多小時,簡單總結了一下spring,算是自我鞏固一下基礎吧。

Thank you for reading.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章