官方文檔 https://spring.io/projects/spring-framework#learnhtml
控制反轉是一種經過描述並經過第三方去生產或獲取特定對象的方式java
<bean id="user" class="com.pojo.User"> <property name="name" value="張三"/> </bean>
<!-- 第一種根據index參數下標設置 --> <bean id="user1" class="com.pojo.User"> <!-- index指構造方法 , 下標從0開始 --> <constructor-arg index="0" value="張三"/> </bean> <!-- 第二種根據參數名字設置 --> <bean id="user2" class="com.pojo.User"> <!-- name指參數名 --> <constructor-arg name="name" value="李四"/> </bean> <!-- 第三種根據參數類型設置 --> <bean id="user3" class="com.pojo.User"> <constructor-arg type="java.lang.String" value="王五"/>
<!--設置別名:在獲取Bean的時候可使用別名獲取--> <alias name="user" alias="userNew"/>
<!--bean就是java對象,由Spring建立和管理--> <!-- id 是bean的標識符,要惟一,若是沒有配置id,name就是默認標識符 若是配置id,又配置了name,那麼name是別名 name能夠設置多個別名,能夠用逗號,分號,空格隔開 若是不配置id和name,能夠根據applicationContext.getBean(.class)獲取對象; class是bean的全限定名=包名+類名 --> <bean id="hello" name="hello2 h2,h3;h4" class="com.pojo.Hello"> <property name="name" value="Spring"/> </bean>
<import resource="{path}/beans.xml"/>
要求被注入的屬性,必須有set方法,set方法的方法名由set + 屬性首字母大寫,若是屬性是boolean類型,沒有set方法,是 ismysql
常量注入web
<bean id="student" class="com.pojo.Student"> <property name="name" value="小明"/> </bean>
Bean注入spring
<bean id="addr" class="com.pojo.Address"> <property name="address" value="重慶"/> </bean> <bean id="student" class="com.pojo.Student"> <property name="address" ref="addr"/> </bean>
數組注入sql
<bean id="student" class="com.pojo.Student"> <property name="books"> <array> <value>西遊記</value> <value>紅樓夢</value> </array> </property> </bean>
List注入數據庫
<property name="hobbys"> <list> <value>聽歌</value> <value>看電影</value> </list> </property>
Map注入express
<property name="card"> <map> <entry key="中國郵政" value="456456456465456"/> <entry key="建設" value="1456682255511"/> </map> </property>
set注入設計模式
<property name="games"> <set> <value>LOL</value> <value>BOB</value> </set> </property>
Null注入數組
<property name="wife"><null/></property>
Properties注入
<property name="info"> <props> <prop key="性別">男</prop> <prop key="姓名">小明</prop> </props> </property>
導入約束:xmlns:p="http://www.springframework.org/schema/p"
<!--P(屬性: properties)命名空間 , 屬性依然要設置set方法--> <bean id="user" class="com.pojo.User" p:name="小明" p:age="18"/>
導入約束:xmlns:c="http://www.springframework.org/schema/c"
<!--C(構造: Constructor)命名空間 , 屬性依然要設置set方法--> <bean id="user" class="com.pojo.User" c:name="小明" c:age="18"/>
<bean id="user" class="com.pojo.User" autowire="byName"> <property name="str" value="小明"/> </bean>
經過set方法查找
<bean id="user" class="com.pojo.User" autowire="byType"> <property name="str" value="小明"/> </bean>
須要同一類型的對象在Spring容器中惟一
按類型自動裝配
public class User { @Autowired private Cat cat; public Cat getCat() { return cat; } }
此時配置文件內容
<!-- 開啓註解支持 --> <context:annotation-config/> <bean id="cat" class="com.pojo.Cat"/> <bean id="user" class="com.pojo.User"/>
默認爲true,@Autowired(required=false)說明對象能夠爲null
@Autowired是根據類型自動裝配的,加上@Qualifier則能夠根據byName的方式自動裝配
@Qualifier不能單獨使用
@Autowired @Qualifier(value = "cat2") private Cat cat;
- @Resource若有指定的name屬性,先按該屬性進行byName方式查找裝配;
- 其次再進行默認的byName方式進行裝配;
- 若是以上都不成功,則按byType的方式自動裝配。
- 都不成功,則報異常。
@Resource(name = "cat2") private Cat cat;
它們的做用相同都是用註解方式注入對象,但執行順序不一樣。@Autowired先byType,@Resource先byName。
配置文件中引入context約束
<?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"> </beans>
<!--指定註解掃描包--> <context:component-scan base-package="com.pojo"/>
掃描過濾
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>>//包含 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>>//排除
過濾方式
annotation:註解 assignable:指定class或interface的全名 aspectj:AspectJ語法 regex:正則
若是配置了 <context:component-scan>
那麼 <context:annotation-config/>
標籤就能夠不用再xml中配置了,由於前者包含了後者
@Component @Controller:web層 @Service:service層 @Repository:dao層
@Component("user") // 至關於配置文件中 <bean id="user" class="當前註解的類"/> public class User { @Value("小明") // 至關於配置文件中 <property name="name" value="小明"/> public String name; } 或者在set方法上添加@value("值")
@Controller("user") @Scope("prototype") public class User { @Value("小明") public String name; }
@Configuration//表明這是一個配置類 @Import(MyConfig2.class)//導入合併其餘配置類,相似於配置文件中的 inculde 標籤 @ComponentScan("com.pojo")//指定掃描的包 public class MyConfig { @Bean(name="dog2")//經過方法註冊一個bean,這裏的返回值就Bean的類型,方法名就是bean的id!也能夠手動指定名稱 public Dog dog(){ return new Dog(); } }
官方文檔https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html#aop
通知類型 | 名稱 | 說明 |
---|---|---|
前置通知 | Before advice | 鏈接點前執行,除非拋出異常,不然不能阻止方法的繼續執行 |
後置通知(最終通知) | After (finally) advice | 鏈接點執行完成後執行,總會執行 |
正常返回通知 | After returning advice | 在鏈接點正常執行完成後執行,若是鏈接點拋出異常,則不會執行 |
異常返回通知 | After throwing advice | 在鏈接點拋出異常後執行 |
環繞通知 | Around advice | 鏈接點先後執行 |
execution([修飾符] 返回值類型 包名.類名.方法名(參數)) * 表示任意一個 .. 表示多個 xml中鏈接子表達式用 and, or, not 註解中用 &&, ||, !
<!-- 聲明開始aop的配置 --> <aop:config> <!-- 配置切面。 id:惟一標識, ref:引用配置好的通知類bean的id --> <aop:aspect id="txAdvice" ref="txManager"> <!-- 配置切入點表達式。就是指定對哪些類的哪些方法進行加強。 expression:用於定義切入點表達式, id:切入點惟一標識 --> <aop:pointcut expression="execution(表達式)" id="pt1"/> <!-- 前置通知。 method:指定通知類中的加強方法名稱; pointcut:指定切入點表達式 pointcut-ref:指定切入點的表達式的引用 --> <aop:before method="beginTransaction" pointcut-ref="pt1"/> <!-- 正常返回通知 returning:獲取返回值傳遞到對應的參數上,method的方法必須聲明一個名爲retVal的參數 --> <aop:after-returning method="commit" returning="retVal" pointcut-ref="pt1"/> <!-- 異常返回通知 throwing:獲取異常傳遞到對應的參數上,method的方法必須聲明一個名爲dataAccessEx的參數 --> <aop:after-throwing method="rollback" throwing="dataAccessEx" pointcut-ref="pt1"/> <!-- 最終通知 --> <aop:after method="release" pointcut-ref="pt1"/> <!-- 環繞通知 一般狀況獨立使用 --> <aop:advisor method="release" pointcut-ref="pt1"/> </aop:aspect> </aop:config>
@Component("txManager") @Aspect public class AnnotationPointcut { @Before("execution(* com.service.UserServiceImpl.*(..))") public void before(){ System.out.println("---------方法執行前---------"); } @After("execution(* com.service.UserServiceImpl.*(..))") public void after(){ System.out.println("---------方法執行後---------"); } @Around("execution(* com.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("環繞前"); System.out.println("簽名:"+jp.getSignature()); //執行目標方法proceed Object proceed = jp.proceed(); System.out.println("環繞後"); System.out.println(proceed); } }
在 spring 配置文件中開啓 spring 對註解 AOP 的支持
<!-- 開啓 spring 對註解 AOP 的支持 --> <aop:aspectj-autoproxy/>
不使用 XML 的配置方式
@Configuration @ComponentScan(basePackages="...") @EnableAspectJAutoProxy public class SpringConfiguration { }
aop:aspectj-autoproxy:說明
經過aop命名空間的
官方文檔:http://mybatis.org/spring/zh/index.html
Maven依賴
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency>
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
引入Spring配置文件的beans.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
配置數據源替換mybatis的數據源
<!--配置數據源:數據源有很是多,可使用第三方的,也可以使使用Spring的--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
配置SqlSessionFactory,關聯MyBatis
<!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--關聯Mybatis--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/dao/*.xml"/> </bean>
註冊SqlSessionTemplate,關聯SqlSessionFactory
<!--註冊sqlSessionTemplate , 關聯sqlSessionFactory--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--利用構造器注入--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
增長Dao接口的實現類;私有化SqlSessionTemplate
public class UserDaoImpl implements UserMapper { //sqlSession不用咱們本身建立了,Spring來管理 private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
註冊bean實現
<bean id="userDao" class="com.kuang.dao.UserDaoImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
官方文檔http://mybatis.org/spring/zh/sqlsession.html#SqlSessionDaoSupport
原子性(atomicity)
事務是原子性操做,由一系列動做組成,事務的原子性確保動做要麼所有完成,要麼徹底不起做用
一致性(consistency)
一旦全部事務動做完成,事務就要被提交。數據和資源處於一種知足業務規則的一致性狀態中
隔離性(isolation)
可能多個事務會同時處理相同的數據,所以每一個事務都應該與其餘事務隔離開來,防止數據損壞
持久性(durability)
事務一旦完成,不管系統發生什麼錯誤,結果都不會受到影響。一般狀況下,事務的結果被寫到持久化存儲器中
Spring支持7種傳播行爲
spring中事務的隔離級別能夠經過隔離屬性指定
DEFAULT | 使用底層數據庫的默認隔離級別,大部分數據庫,默認隔離級別都是READ_COMMITED |
READ_COMMITED | 只容許事務讀取已經被其餘事務提交的更改,能夠避免髒讀,但不可重複讀和幻讀問題仍然可能出現 |
READ_UNCOMMITED | 容許事務讀取未被其餘事務提交的更改。髒讀,不可重複讀,幻讀均可能會出現 |
REPEATABLE_READ | 確保事務能夠屢次從一個字段中讀取相同的值。在這個事務持續期間,禁止其餘事務對這個字段進行更新,能夠避免髒讀和不可重複讀,可是幻讀的問題依然存在 |
SERIALIZABLE | 確保事務能夠從一個表中讀取相同的行,在這個事務持續期間,禁止其餘事務對該表執行插入,更新,刪除。全部的併發問題都能避免,可是性能比較低。 |
注意:事務的隔離級別須要底層數據庫引擎的支持,而不是應用程序或者框架的支持
導入 aop 和 tx 兩個名稱空間
步驟:
配置事務管理器
<!-- 配置一個事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入 DataSource --> <property name="dataSource" ref="dataSource"></property> </bean>
配置事務的通知引用事務管理器
<!-- 事務的配置 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"></tx:advice>
配置事務的屬性
<!--在 tx:advice 標籤內部 配置事務的屬性 --> <tx:attributes> <!-- 指定方法名稱:是業務核心方法 read-only:是不是隻讀事務。默認 false,不僅讀。 isolation:指定事務的隔離級別。默認值是使用數據庫的默認隔離級別。 propagation:指定事務的傳播行爲。 timeout:指定超時時間。默認值爲: -1。永不超時。 rollback-for:用於指定一個異常,當執行產生該異常時,事務回滾。產生其餘異常,事務不回滾。沒有默認值,任何異常都回滾。 no-rollback-for:用於指定一個異常,當產生該異常時,事務不回滾,產生其餘異常時,事務回滾。沒有默認值,任何異常都回滾。 --> <tx:method name="*" read-only="false" propagation="REQUIRED"/> <tx:method name="find*" read-only="true" propagation="SUPPORTS"/> </tx:attributes>
配置 AOP 切入點表達式
<!-- 配置 aop --> <aop:config> <!-- 配置切入點表達式 --> <aop:pointcut expression="execution(* com.service.impl.*.*(..))" id="pt1"/> </aop:config>
配置切入點表達式和事務通知的對應關係
<!-- 在 aop:config 標籤內部: 創建事務的通知和切入點表達式的關係 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
配置事務管理器並注入數據源
<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
在業務層使用 @Transactional 註解
@Service("accountService") @Transactional(readOnly=true,propagation=Propagation.SUPPORTS) public class AccountServiceImpl implements IAccountService { ... }
在配置文件中開啓 spring 對註解事務的支持
<!-- 開啓 spring 對註解事務的支持 --> <tx:annotation-driven transaction-manager="transactionManager"/>
@Configuration @EnableTransactionManagement public class SpringTxConfiguration { //裏面配置數據源,配置 JdbcTemplate,配置事務管理器 }
在通常狀況下,只有無狀態的Bean才能夠在多線程環境下共享,在Spring中,絕大部分Bean均可以聲明爲singleton做用域,由於Spring對一些Bean中非線程安全狀態採用ThreadLocal進行處理,解決線程安全問題。
ThreadLocal和線程同步機制都是爲了解決多線程中相同變量的訪問衝突問題。同步機制採用了「時間換空間」的方式,僅提供一份變量,不一樣的線程在訪問前須要獲取鎖,沒得到鎖的線程則須要排隊。而ThreadLocal採用了「空間換時間」的方式。
ThreadLocal會爲每個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。由於每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,能夠把不安全的變量封裝進ThreadLocal。
Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是沒法提供事務功能的。真正的數據庫層的事務提交和回滾是經過binlog或者redo log實現的。
Servlet的生命週期:實例化,初始init,接收請求service,銷燬destroy;
Bean生命週期也相似,以下:
實例化Bean:
對於BeanFactory容器,當客戶向容器請求一個還沒有初始化的bean時,或初始化bean的時候須要注入另外一個還沒有初始化的依賴時,容器就會調用createBean進行實例化。對於ApplicationContext容器,當容器啓動結束後,經過獲取BeanDefinition對象中的信息,實例化全部的bean。
設置對象屬性(依賴注入):
實例化後的對象被封裝在BeanWrapper對象中,緊接着,Spring根據BeanDefinition中的信息 以及 經過BeanWrapper提供的設置屬性的接口完成依賴注入。
處理Aware接口:
接着,Spring會檢測該對象是否實現了xxxAware接口,並將相關的xxxAware實例注入給Bean:
若是這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的就是Spring配置文件中Bean的id值;
若是這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory()方法,傳遞的是Spring工廠自身。
若是這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文;
BeanPostProcessor:
若是想對Bean進行一些自定義的處理,那麼可讓Bean實現了BeanPostProcessor接口,那將會調用postProcessBeforeInitialization(Object obj, String s)方法。
InitializingBean 與 init-method:
若是Bean在Spring配置文件中配置了 init-method 屬性,則會自動調用其配置的初始化方法。
若是這個Bean實現了BeanPostProcessor接口,將會調用postProcessAfterInitialization(Object obj, String s)方法;因爲這個方法是在Bean初始化結束時調用的,因此能夠被應用於內存或緩存技術;
以上幾個步驟完成後,Bean就已經被正確建立了,以後就可使用這個Bean了。
DisposableBean:
當Bean再也不須要時,會通過清理階段,若是Bean實現了DisposableBean這個接口,會調用其實現的destroy()方法;
destroy-method:
最後,若是這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。
實例化bean對象
注入bean的全部屬性
設置bean的id
調用BeanNameAware接口的setBeanName(String)方法
設置bean工廠
調用BeanFactoryAware接口的setBeanFactory()方法
設置實例所在的上下文空間
調用ApplicationContextAware接口的setApplicationContext()方法,傳入Spring上下文
調用後置處理器的預初始化方法
調用BeanPostProcessor接口的postProcessorBeforeInitialization()方法
執行InitializingBean的afterPropertiesSet()
調用使用init-method配置的自定義初始化方法
調用後置處理器的後初始化方法
調用BeanPostProcessor接口的postProcessorAfterInitialization()方法
調用DisPosableBean接口的destory()方法
調用使用destroy-method配置的自定義銷燬由方法
使用BeanWrapper
HelloWorld hw=new HelloWorld(); BeanWrapper bw=new BeanWrapperImpl(hw); bw.setPropertyvalue("msg","HelloWorld"); system.out.println(bw.getPropertyCalue("msg"));
使用BeanFactory
InputStream is=new FileInputStream("config.xml"); XmlBeanFactory factory=new XmlBeanFactory(is); HelloWorld hw=(HelloWorld) factory.getBean("HelloWorld"); system.out.println(hw.getMsg());
使用ApplicationConttext
ApplicationContext actx=new FleSystemXmlApplicationContext("config.xml"); HelloWorld hw=(HelloWorld) actx.getBean("HelloWorld"); System.out.println(hw.getMsg());