Spring是一個開源框架,它由Rod Johnson建立。它是爲了解決企業應用開發的複雜性而建立的。Spring使用基本的JavaBean來完成之前只可能由EJB完成的事情。然而,Spring的用途不只限於服務器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用均可以從Spring中受益。java
Spring是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的容器框架。mysql
能夠把Spring看做是一個一站式的整合輕量級的開源框架,能夠整合各層之間的其它框架.web
◆JAVA EE應該更加容易使用。spring
sql
◆面向接口編程,而不是針對類編程。Spring將使用接口的複雜度下降到零。(面向接口編程有哪些複雜度?)數據庫
◆代碼應該易於測試。Spring框架會幫助你,使代碼的測試更加簡單。express
◆JavaBean提供了應用程序配置的最好方法。apache
◆在Java中,已檢查異常(Checked exception)被過分使用。框架不該該迫使你捕獲不能恢復的異常。編程
控制反轉(Inversion of Control,縮寫爲IoC),是面向對象編程中的一種設計原則,能夠用來減低計算機代碼之間的耦合度。其中最多見的方式叫作依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。經過控制反轉,對象在被建立的時候,由一個調控系統內全部對象的外界實體將其所依賴的對象的引用傳遞給它。也能夠說,依賴被注入到對象中。數組
IOC 就是把建立對象的控制權交給Spring來管理,咱們只要向容器提出需求,容器就會按需求提供相應的對象,這就叫"控制反轉"
DI(依賴注入) 多個對象之間會存在相應關係,咱們把其它對象做爲屬性值傳遞給其它的對象作爲其內部的一部分.(設置對象之間的關聯關係)
對象之間產生的一種關聯關係
強耦合:就是在編譯器就產生了關聯關係
硬編碼
不利於維護和擴展
低耦合:就是在運行期間讓對象之間產生關聯
使用反射來建立對來
對象的完整限定名放到配置文件中
解決了硬編碼問題,有利於程序後期的維護和擴展
Spring IOC 容器就能幫們解決以上的問題
下載地址:https://spring.io/
導入Spring jar包
配置Spring核心配置文件(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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!--給Spring容器配置Bean-->
<bean id="student" class="com.hwua.entity.Student"></bean>
</beans>
編寫測試類
package com.hwua.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hwua.entity.Student;
public class SpringTest {
使用反射去建立對象,默認會調用無參構造函數(重點)
<bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean>
使用普通工廠來建立對象
package com.hwua.factory;
import com.hwua.dao.UserDao;
import com.hwua.dao.impl.UserDaoJDBCImpl;
/**
* 普通工廠類
*
* @author Administrator
*
*/
public class BeanFactroy1 {
public UserDao createUserDao() {
return new UserDaoJDBCImpl();
}
}
<bean id="factory1" class="com.hwua.factory.BeanFactroy1"></bean> <bean id="userDaoJDBC" factory-bean="factory1" factory-method="createUserDao"></bean>
- 使用靜態工廠來建立bean對象
```java
package com.hwua.factory;
import com.hwua.dao.UserDao;
import com.hwua.dao.impl.UserDaoJDBCImpl;
import com.hwua.dao.impl.UserDaoMyBatisImpl;
/**
* 靜態工廠類
*
* @author Administrator
*
*/
public class BeanFactroy2 {
public static UserDao createUserDao() {
return new UserDaoMyBatisImpl();
}
}
<bean id="userDaoMyBatis" class="com.hwua.factory.BeanFactroy2" factory-method="createUserDao"></bean>
構造器注入(會用)
<!--能夠直接給value屬性賦值,但必須值的順序按照構造函數參數的順序,不然課程存在出錯 -->
<constructor-arg value="1"></constructor-arg>
<constructor-arg value="陳豪"></constructor-arg>
<constructor-arg value="20"></constructor-arg>
<!--能夠指定index屬性來控制參數賦值的順序 -->
<constructor-arg value="陳豪" index="1"></constructor-arg>
<constructor-arg value="1" index="0"></constructor-arg>
<constructor-arg value="20" index="2"></constructor-arg>
<!--咱們可使用index,name,type來控制構造器注入參數的順序-->
<constructor-arg value="20" type="java.lang.Integer" index="2"></constructor-arg>
<constructor-arg value="陳豪" type="java.lang.String"></constructor-arg>
<constructor-arg value="1" type="java.lang.Integer" name="age"></constructor-arg>
記住只要記住使用name,根據參數的名來來進行賦值
<constructor-arg value="1" name="age"></constructor-arg>
set注入方式(重點)
基本類型數據的注入使用value屬性來注入,自定義的引用數據類型使用ref來給屬性注入
set注入必需要有五參數構造函數,內部經過反射機制調用無參構造函數來建立對象的.
屬性必需要有對象的get和set方法
<!--給Spring容器配置Bean -->
<bean id="student" class="com.hwua.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="zhangsan"></property>
<property name="age" value="30"></property>
<property name="car" ref="car"></property>
</bean>
<bean id="car" class="com.hwua.entity.Car">
<property name="brand" value="奔馳"></property>
<property name="price" value="300000"></property>
</bean>
P命名空間注入方式(Spring2.5之後纔有)
引入P命名空間
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
編寫注入語法
<!--構造器注入--> <bean id="student" class="com.hwua.entity.Student" c:_0="1" c:_1="張三" c:_2="30" c:_3-ref="car"></bean> <bean id="car" class="com.hwua.entity.Car" c:_0="奔馳" c:_1="300000"></bean> <!--屬性注入--> <bean id="car" class="com.hwua.entity.Car" p:brand="奔馳" p:price="300000"></bean> <bean id="student" class="com.hwua.entity.Student" p:id="1" p:name="張三" p:age="30" p:car-ref="car"></bean>
spel(Spring Expression Language)注入方式
<bean id="car" class="com.hwua.entity.Car"> <property name="brand" value="#{'奔馳'}"></property> <property name="price" value="#{300000}"></property> </bean> <bean id="student" class="com.hwua.entity.Student"> <property name="id" value="#{1+1}"></property> <property name="name" value="#{'zhangsan'}"></property> <property name="age" value="#{30}"></property> <property name="car" value="#{car}"></property> </bean>
複雜類型數據的注入
package com.hwua.entity; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class ComplexData { private List<String> list; private Set<String> set; private Map<String, String> map; private Properties properties; private Car[] arr; public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Set<String> getSet() { return set; } public void setSet(Set<String> set) { this.set = set; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public Car[] getArr() { return arr; } public void setArr(Car[] arr) { this.arr = arr; } }
```java <?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> <bean id="car1" class="com.hwua.entity.Car"> <property name="brand"> <!-- <value><![CDATA[1<2]]></value> --> <null/> </property> <property name="price" value="40000"></property> </bean> <bean id="car2" class="com.hwua.entity.Car"> <property name="brand" value="寶馬"></property> <property name="price" value="30000"></property> </bean> <bean id="car3" class="com.hwua.entity.Car"> <property name="brand" value="奧迪"></property> <property name="price" value="25000"></property> </bean> <bean id="data" class="com.hwua.entity.ComplexData"> <!--List類型的屬性注入 --> <property name="list"> <list> <value>chenhao1</value> <value>chenhao2</value> <value>chenhao3</value> <value>chenhao4</value> </list> </property> <!--Set類型的屬性注入 --> <property name="set"> <set> <value>chenhao4</value> <value>chenhao5</value> <value>chenhao6</value> <value>chenhao7</value> </set> </property> <!--Map類型的屬性注入 --> <property name="map"> <map> <entry key="jack" value="老王"></entry> <entry key="frank" value="老陳"></entry> <entry key="mary" value="老李"></entry> </map> </property> <!--Properties類型數據的屬性注入 --> <property name="properties"> <props> <prop key="frank">老王</prop> <prop key="jack">老陳</prop> <prop key="mary">老李</prop> </props> </property> <!--數組類型數據的屬性注入 --> <property name="arr"> <array> <ref bean="car1"/> <ref bean="car2"/> <ref bean="car3"/> </array> </property> </bean> </beans> ```
- ClassPathXmlApplicationContext :加載類路徑下的spring配置文件,相對定位 - FileSystemXmlApplicationContext:加載文件系統路徑下的Spring配置文件,絕對定位(不推薦) - AnnotationConfigApplicationContext:加載註解配置類,也就是說之後的配置信息寫到一個配置類中,不用xml文件來做爲配置文件(後續會講)
- singleton 在容器中始終只產生一個對象 - prototype 在向容器獲取屢次bean的時候,會產生多個bean對象 - request 在request做用域中存入一個bean對象 - session 在session做用域中存入一個bean對象
單例時對象的生命週期
建立時機: 容器建立的時候對象建立
銷燬時機: 容器關閉的時候對象就銷燬
多例時對象的生命週期
建立時機: 當要從容器中獲取指定對象的時候,就會由Spring來建立一個對象,延遲建立
銷燬時機:當對象長時間不被使用,或對象的引用爲null的時候,有JVM的垃圾回收器來執行回收
BeanFactory 是ApplicationContext接口的父接口,ApplicationContext功能更強大
ApplicationContext 容器 在建立的時候就會對配置文件的scope爲singleton的對象進行統一建立,而BeanFactory容器在建立的時候不會對做用範圍爲singleton的對象進行統一建立,而是在獲取對象的時候在建立.
@Component
@Service
@Controller
@Resposity
以上的功能徹底同樣,只是註解名字不一樣,在分層架構中可讀性更好
@AutoWired 自動注入,默認是根據類型來注入的,也就是說它會從容器中找惟一相關類型的對象注入進來
@Primary 當有兩個相同類型對象的時候,以@Primary修飾的對象來進行注入
@Qualifier 每每 配合@AutoWired 來使用,先以類型來注入,當類型相同的時候再按@Qualifier指定的名字來注入,不能單獨使用的
@Autowired @Qualifier("userDaoMyBatis")
@Resource 等同於@Autowired 和 @Qualifier的組合,假設不給name屬性,默認按類型來注入,給name屬性值,那就按名字來注入
以上幾個註解都只能注入bean數據類型的數據,基礎類型的數據(8大基本數據類型和String),是不能用上述註解來注入的
@Scope("prototype") 註解 來聲明對象在容器中做用範圍,主要值sigleton和prototype
@PostConstruct 修飾的方法就是對象建立時要執行的方法
@PreDestroy 修飾的方法是在對象銷燬的時候要執行的方法
@Value 註解給屬性賦值基本類型的數據
Spring 整合 DBUtils 和 C3P0
<?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-4.3.xsd"> <!--用來讀取屬性文件 --> <context:property-placeholder location="classpath:db.properties"/> <!--配置業務層對象 --> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!--配置一個DAO對象 --> <bean id="userDao" class="com.hwua.dao.impl.UserDaoImpl"> <property name="qr" ref="runner"></property> </bean> <!--配置QueryRunner對象 --> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <!--配置數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
Spring 整合 JDBCTemplate 和 DRUID
<?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-4.3.xsd"> <!--用來讀取屬性文件 --> <context:property-placeholder location="classpath:db.properties"/> <!--配置業務層對象 --> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!--配置一個DAO對象 --> <bean id="userDao" class="com.hwua.dao.impl.UserDaoImpl"> <property name="jTemplate" ref="jdbcTemplate"></property> </bean> <!--配置jdbcTemplate對象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置DRUID數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
Spring 整合junit
咱們通常經過main方法來運行程序
單元測試類其實它底層繼承了一個main方法,當運行的時候會調用運行那些有@Test修飾的方法
咱們可使用Spring整合Junit來簡化單元測試的複雜度
1. 導入Spring-test jar包
package com.hwua.test; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.hwua.config.SpringConfig; import com.hwua.pojo.Student; import com.hwua.service.StudentService; import com.hwua.service.impl.StudentServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes= {SpringConfig.class}) public class UserTest { @Autowired private StudentService stuService = null; @Test public void testSaveUser() throws Exception { // 建立SpringIOC容器,context 就是IOC容器的引用 Student stu = new Student(); stu.setSname("hello"); stu.setSsex("男"); stu.setSno("no110"); stuService.saveStudent(stu); } @Test public void testQueryUserById() throws Exception { // 建立SpringIOC容器,context 就是IOC容器的引用 Student stu = stuService.findStudentById(1); System.out.println(stu); } @Test public void testQueryAllUsers() throws Exception { List<Student> stuList = stuService.findAllStudents(); System.out.println(stuList); } @Test public void testUpdateStudentById() throws Exception { // 建立SpringIOC容器,context 就是IOC容器的引用 Student stu = stuService.findStudentById(1); stu.setSname("瑪麗"); stu.setSsex("女"); stuService.UpdateStudent(stu); } @Test public void testDeleteStudentById() throws Exception { // 建立SpringIOC容器,context 就是IOC容器的引用 stuService.deleteStudentById(9); } }
Spring 整合 MyBatis 和 DRUID
導入MyBatis的jar包,mybatis-spring整合包,spring-jdbc 事務處理包
<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.hwua</groupId> <artifactId>Spring_Day3_Spring_MyBatis</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.5.RELEASE</version> </dependency> </dependencies> </project>
applicationContext配置文件
第一種配置方式,有spring配置文件和mybatis配置文件 <?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-4.3.xsd"> <!--讀取類路徑下的配置db.properties文件 --> <context:property-placeholder location="classpath:db.properties"/> <context:component-scan base-package="com.hwua.service"></context:component-scan> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean> <!--Spring整合MyBatis --> <bean id="sqlSessionFatory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property> </bean> <!--配置掃描Mapper接口類型的數據,把接口類型的對象放入Spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--配置包掃描 --> <property name="basePackage" value="com.hwua.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFatory"></property> </bean> <!--配置DRUID數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans> MyBatis配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--配置一個別名包掃描 --> <typeAliases> <package name="com.hwua.pojo"/> </typeAliases> <mappers> <package name="com.hwua.mapper"/> </mappers> </configuration> 第二種:省略MyBatis配置文件的方式 <?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-4.3.xsd"> <!--讀取類路徑下的配置db.properties文件 --> <context:property-placeholder location="classpath:db.properties"/> <bean id="userService" class="com.hwua.service.impl.UserServiceImpl"></bean> <!--Spring整合MyBatis --> <bean id="sqlSessionFatory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="typeAliasesPackage" value="com.hwua.pojo"></property> <property name="mapperLocations" value="classpath:com/hwua/mapper/*.xml"></property> </bean> <!--配置掃描Mapper接口類型的數據,把接口類型的對象放入Spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--配置包掃描 --> <property name="basePackage" value="com.hwua.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFatory"></property> </bean> <!--配置DRUID數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
@Configuration: 代表這個類是配置類
@ComponentScan:組件包掃描
@Bean:修飾方法,會執行工廠方法,並把返回的對象放到IOC容器中
@PropertySource 讀取屬性文件的註解
@Import 導入其它的配置類
總結:實際開發中是xml和註解混合配置,自定義的bean對象推薦使用註解,由於簡單方便,而第三方或框架自帶的類推薦使用xml來進行配置
主配置類 package com.hwua.config; import java.beans.PropertyVetoException; import javax.annotation.Resource; import javax.sql.DataSource; import org.apache.commons.dbutils.QueryRunner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 配置類的做用就等同於配置文件 * @author Administrator * */ @Configuration @ComponentScan(basePackages= {"com.hwua.service","com.hwua.dao"}) @PropertySource("classpath:db.properties") @Import(JDBCConfiguration.class)//導入其它配置類 public class SpringConfiguration { //使用工廠方法去建立一個QueryRunner對象,工廠方法中的參數會自動從IOC容器中找到相應的對象進行自動注入 @Bean("runner") public QueryRunner createQueryRunner(@Autowired DataSource ds) { return new QueryRunner(ds); } } 從配置類 package com.hwua.config; import java.beans.PropertyVetoException; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.mchange.v2.c3p0.ComboPooledDataSource; @Configuration public class JDBCConfiguration { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String jdbcUrl; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") public DataSource createDataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(jdbcUrl); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } }
實現轉帳案例
發現案例中的問題:
一個業務方法中的多個功能都是在本身的鏈接對象上來單獨開啓事務的,因此多個獨立的dao不能使用同一個事務來進行處理,致使轉帳數據不一致.
解決案例中的問題
ThreadLocal :用來給當前線程上綁定鏈接對象,一個請求用的就是一個線程,那麼只要多個dao從線程上獲取綁定的鏈接對象就能進行統一的事務處理.
動態代理 : 做用:在類上沒有修改其源碼的狀況下,經過代理對象來對其功能進行加強.
jdk動態代理:對實現接口的對象來建立代理類對象
cglib動態代理:對沒有實現接口的對象,經過對其建立子類對象來實現代理
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。
日誌記錄,性能統計,安全控制,事務處理,異常處理等等。
做用: 在運行期間,在不修改源碼的前提下對已有方法進行功能加強
優點:代碼的複用,提升開發效率維護方便
joinPoint(鏈接點):目標對象,全部能夠被加強的方法。
Advice(通知/加強):具體加強功能的類
PointCut(切入點):目標對象的方法,將要被加強的具體的方法。
Introduction(引入):聲明某個方法或字段。(不用)
Target(目標對象):被代理的對象,也就是要被加強功能的哪一個對象
AOP 代理(AOp Proxy) Spring 幫咱們建立的代理類對象
Weaving(織入):將通知應用到切入點的過程。
Aspect(切面):切入點+通知的組合。
前置通知[Before advice] 在業務方法執行以前執行的加強功能
後置通知[After returning advice]:在業務方法執行以後執行的加強功能
異常通知[After throwing advice]:在業務方法報錯時要執行的加強功能
最終通知[After (finally) advice]:在業務方法執行後,無論有沒有異常,最終都要執行的加強功能
環繞通知[Around advice]:環繞通知圍繞在切入點先後,好比一個方法調用的先後。這是最強大的通知類型,能夠手動經過代碼來控制通知的類型.
把通知類對象放到Spring容器中
使用aop:config標籤來開始aop的配置
使用aop:aspect標籤來配置切面
id 屬性隨意給個名字
ref: 引用的是通知類對象的id
使用aop:xxx標籤來配置通知類型
method 指的是通知類對象中具體執行的加強方法
pointcut 指定具體的切入點,要編寫切入點表達式,使用切入點表達式,必需要導入AspectJ的jar包
語法: [訪問修飾符] 返回類型 包名.包名...類名.方法名(參數1,參數2) - 返回類型可使用通配符*,表明返回任意類型 - 包名可使用通配符*, 包名隨意*.*.* *.*.. ..表明任意的子包 - 類名可使用通配符*,表明任意類 - 方法名可使用通配符,表明任意方法 - 參數可使用.. 表明任意多個參數(0到多) void com.hwua.service.impl.StudentServiceImpl.saveStudent() 常規寫法: * com.hwua.service.impl.StudentServiceImpl.*(..) <!--開始配置aop --> <aop:config> <!--配置全局切入點,這個切入點能夠被能當前全部切面中的通知來使用 --> <aop:pointcut expression="execution( * com.hwua.service.impl.*.*(..))" id="p1" /> <!--配置一個切面 --> <aop:aspect id="logAdvise" ref="log"> <aop:before method="logBefore" pointcut-ref="p1" /> <aop:after-returning method="logAfterReturning" pointcut-ref="p1" /> <aop:after-throwing method="logAfterThrowing" pointcut-ref="p1" /> <aop:after method="logAfter" pointcut-ref="p1" /> <!--配置局部切入點,這個切入點只能給當前切面中的通知來使用 --> <!-- <aop:pointcut expression="execution( * com.hwua.service.impl.*.*(..))" id="p1"/> --> </aop:aspect> </aop:config>
aop:before 配置前置通知
aop:after-returning 配置後置通知
aop:after-throwing 配置異常通知
aop:after 配置最終通知
aop:around 配置環繞通知:經過手動編寫代碼來實現業務的功能增強.就是能夠手動來進行設置
配置了環繞通知後,在執行的時候默認不執行業務方法,Spring框架爲咱們提供了一個接口:ProceedingJoinPoint,當通知中的方法被執行的時候,Spring會把傳過來ProceedingJoinPoint類型的一個對象,裏面包含了業務方法的相關信息
public Object around(ProceedingJoinPoint pjp) { Object res = null; Object[] args = pjp.getArgs();// 獲取業務方法中的參數列表 try { logBefore(); res = pjp.proceed(args);// 手動的去指定業務方法 logAfterReturning(); } catch (Throwable e) { logAfterThrowing(); e.printStackTrace(); } finally { logAfter(); } // 執行業務方法 return res; }
package com.hwua.utils; import java.sql.SQLException; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 事務管理器 簡單的說就要要對業務方法加強的事務功能 * * @author Administrator * */ @Component("txManager") @Aspect//切面類 public class TXManager { @Pointcut("execution(* com.hwua.service.impl.AccountServiceImpl.*(..))") private void p1() { } @Autowired private ConnectionUtils connectionUtils = null; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } //開啓事務 @Before("p1()") public void beginTransAction() { System.out.println("前置通知"); try { connectionUtils.getConnection().setAutoCommit(false);//開啓事務 } catch (SQLException e) { e.printStackTrace(); } } //提交事務 @AfterReturning("p1()") public void commit() { System.out.println("後置通知"); try { connectionUtils.getConnection().commit();//提交事務 } catch (SQLException e) { e.printStackTrace(); } } //回滾事務 @AfterThrowing("p1()") public void rollback() { System.out.println("異常通知"); try { connectionUtils.getConnection().rollback();//回滾事務 } catch (SQLException e) { e.printStackTrace(); } } //釋放鏈接 @After("p1()") public void release() { System.out.println("最終通知"); try { connectionUtils.getConnection().close();//關閉鏈接 connectionUtils.release();//把鏈接對象從當前線程解綁 } catch (SQLException e) { e.printStackTrace(); } } }
注意點:使用註解配置AOP會出現執行順序的問題,先執行前置通知,最終通知,(後置通知或異常通知)
使用註解配置AOP咱們推薦使用配置環繞通知.
<!--開啓AOP註解配置的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.hwua.utils; import java.sql.SQLException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 事務管理器 簡單的說就要要對業務方法加強的事務功能 * * @author Administrator * */ @Component("txManager") @Aspect//切面類 public class TXManager { @Pointcut("execution(* com.hwua.service.impl.AccountServiceImpl.*(..))") private void p1() { } @Autowired private ConnectionUtils connectionUtils = null; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } //開啓事務 /*@Before("p1()")*/ public void beginTransAction() { System.out.println("前置通知"); try { connectionUtils.getConnection().setAutoCommit(false);//開啓事務 } catch (SQLException e) { e.printStackTrace(); } } //提交事務 /*@AfterReturning("p1()")*/ public void commit() { System.out.println("後置通知"); try { connectionUtils.getConnection().commit();//提交事務 } catch (SQLException e) { e.printStackTrace(); } } //回滾事務 /*@AfterThrowing("p1()")*/ public void rollback() { System.out.println("異常通知"); try { connectionUtils.getConnection().rollback();//回滾事務 } catch (SQLException e) { e.printStackTrace(); } } //釋放鏈接 /*@After("p1()")*/ public void release() { System.out.println("最終通知"); try { connectionUtils.getConnection().close();;//關閉鏈接 connectionUtils.release();//把鏈接對象從當前線程解綁 } catch (SQLException e) { e.printStackTrace(); } } @Around("p1()") public Object around(ProceedingJoinPoint pjp) { Object obj=null; Object[] args = pjp.getArgs();//獲取業務方法的參數 try { beginTransAction(); obj=pjp.proceed(args);//業務方法 commit(); } catch (Throwable e) { rollback(); e.printStackTrace(); }finally { release(); } return obj; } }
原子性:強調事務操做的單元是一個不可分割的總體.
一致性:事務操做先後數據要保持一致
隔離性:一個事務在執行的過程當中,不該該受其它事務的影響
持久性:事務一旦提交,數據就永久持久化到數據庫中,不能回滾
髒讀:一個事務讀取到另外一個事務未提交的數據
不可重複讀:一個事務讀取到另外一個已提交事務的update數據,致使屢次讀取數據產生不一致
幻讀:一個事務讀取到另外一個已提交事務的insert數據,致使屢次讀取數據產生不一致.
讀未提交(Read Uncommited):髒讀,不可重複讀,幻讀,都避免不了
讀已提交(Read committed):解決髒讀,(不可重複讀,幻讀解決不了)
可重複讀(Repeatable read):解決髒讀,不可重複讀,(幻讀解決不了)
串行化(serializable):解決上述三個問題,效率比較低,會致使一個失去就等待另外一個事務處理完畢.
1.查看數據庫的隔離級別: select @@transaction_isolation; 2.設置數據庫隔離級別: set session transaction isolation level 隔離級別
Spring 內部已經幫咱們設計好了一個事務管理對象,咱們只要配置這個事務管理器對象就能幫業務方法實現實現事務的管理.
導入jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring-vesion}</version> </dependency>
配置spring自帶的事務管理器對象
<!--配置事務管理器對象 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
配置一個事務通知
<tx:advice id="txAdvise" transaction-manager="transactionManager"> </tx:advice>
配置切面
<aop:config> <!--配置切入點 --> <aop:pointcut expression="execution(* com.hwua.service.impl.AccountServiceImpl.*(..))" id="p1"/> <!--把通知切入到指定的切入點 --> <aop:advisor advice-ref="txAdvise" pointcut-ref="p1"/> </aop:config>
對事務通知進行屬性的設置
isolation: 設置事務隔離級別 propagation: 事務的傳播行爲 - REQUIRED 必需要有事務,針對增刪改的操做 - SUPPORTS 若是當前有事務則加入,若是沒有則不用事務。 read-only="true" 表明查詢 rollback-for: 指定碰到相應的異常就回滾事務,不配默認就是碰到異常就回滾 no-rollback-for: 指定碰到相應的異常不回滾事務,不配默認就是碰到異常就回滾 timeout:不設置就表明默認-1永不超時,設置數字單位是秒 <!--配置一個事務通知--> <tx:advice id="txAdvise" transaction-manager="transactionManager"> <!--配置事務的屬性 --> <tx:attributes> <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" /> <tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice>
開啓事務的註解支持
<!--開啓事務註解的支持 --> <tx:annotation-driven/>
在指定方法上設置事務處理@Transactional
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
課堂練習: 修改銀行轉帳代碼:
Spring 整合 jdbcTemplate實現聲明式事務事務處理
Spring 整合 MyBatis來實現聲明式事務處理
下載spring整合web應用程序的jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.5.RELEASE</version> </dependency>
咱們去註冊一個監聽器去監聽Servlet應用加載,只要監聽到加載,咱們就應該加載配置文件來建立容器
<!--配置全局參數 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Servlet對象時在Servlet容器中,而咱們建立的業務層,數據訪問層對象都放在了IOC容器中,也就是不一樣容器間不能直接訪問.咱們可使用一個工具類來獲得IOC容器的引用
ApplicationContext context=null; @Override public void init() throws ServletException { //建立了IOC容器的引用 context=WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); } //取IOC容器中bean對象 UserService us = (UserSerice)context.getBean("userService");