您好,登錄后才能下訂單哦!
Spring是一個開源框架,為簡化企業級應用開發而生。
Spring是一個 IOC 和 AOP 容器框架。
Spring 是輕量級的、面向切面編程、依賴注入的一站式框架。
導入
commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
IOC:反轉控制器,思想是反轉資源獲取的方向,傳統資源查找方式是組件向容器發起請求查找資源,IOC 則是容器主動的將資源推送給它所管理的組件。
DI:依賴注入,組件預定義的方式接受資源注入。
在 Spring IOC 容器讀取 Bean 配置創建 Bean 實例之前,必須要對其進行實例化,只有實例化后,才可以從 IOC 容器中取出并使用。
Spring 提供了兩種類型的 IOC:
BeanFactory:IOC 容器的基本實現。
ApplicationContext:提供了更多的特性,是 BeanFactory 的子接口,在初始化上下文階段就實例化了所有單例Bean。
AppliactionContext 主要實現類
ClassPathXmlApplicationContext:從類路徑下加載配置文件。
FileSystemXmlApplicationContext:從文件系統下加載配置文件。
ConfigurableApplicationContext:ApplicationContext 的擴展類,新增 refresh 和 close 方法,使其具有啟動、刷新和關閉上下文的功能。
WebApplicationContext:Web 應用專屬。
基于 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">
<bean id="car" class="com.kernel.bean.Car"/>
</beans>
基于注解的方式
Spring 能從 classpath 中自動掃描、偵測和實例化具有特定注解的組件,包括:
@Component: 基本注解,標識一個受 Spring 管理的組件。
@Respository:標識持久層組件。
@Service:標識業務層組件。
@Controller:標識表現層組件。
對于掃描到的組件,Spring 有特定的命名策略,當然可以在注解中通過 value 屬性指定組件名。
當在組件中使用了注解后,還需要在配置文件中聲明\<context:component-scan>:
base-package:掃描組件基類包,Spring 容器將掃描該包下的所有類,當需要掃描多個包時,用逗號隔開。
resource-pattern:僅掃描基類中的特定類。
use-default-filters:默認為ture,掃描@Component、@Respository、@Service、@Controller 組件。
\<context:exclude-filter>:子節點表示要排除的的目標類。
\<context:include-filter>:子節點標識要包含的目標類,需要的 use-default-filters 配合使用。
類別 | 說明 | |
---|---|---|
annotation | com.kernel.XxxAnnotation | 所有標注了XxxAnnotation的類,該類型采用目標類是否標注了某個注解進行過濾 |
assignable | com.kernel.XxxService | 所有繼承或擴展XxxService的類,該類型采用了目標類是否繼承或擴展某個特定類進行過濾 |
aspectj | com.kernel.*Service | 所有類名義Service結束的類及繼承或擴展它們的類,該類型采用AspectJ表達式進行過濾 |
regex | com.kernel.anno.* | 所有com.kernel.anno包下的類。該類型采用正則表達式,根據類的類名進行過濾 |
custom | com.kernel.XxxTypeFilter | 采用XxxTypeFilter通過代碼的方式定義過濾原則。該類必須實現org.springframework.core.type.TypeFilter接口 |
Spring 還可以進行組件裝配
\<context:component-scan> 元素還會自動注冊 AutowiredAnnotationBeanPostProcessor 實例,該實例可以自動裝配具有 @Autowired、@Resource 和 @Inject 注解的屬性。
構造器、普通字段、具有參數的方法都可以應用 @Autowired。
默認,所有被設置為該注解的屬性都要被設置,否則會拋出異常,可以使用該注解的 required 屬性設為false。
當 IOC 中存在多個類型兼容的 Bean 時,自動裝配失敗,此時可以在 @Qualifier 注解中提供 Bean 的名稱。
@Authwired 注解應用在數組類型的屬性上,此時 Spring 將會把所有匹配的 Bean 進行自動裝配。
@Authwired 注解應用在集合屬性上, 此時 Spring 讀取該集合的類型信息,然后自動裝配所有與之兼容的 Bean。
@Authwired 注解用在 Map 上時, 若該 Map 的鍵值為 String,那么 Spring 將自動裝配與之 Map 值類型兼容的 Bean,此時 Bean 的名稱作為鍵值。
通過全類名
<bean id="car" class="com.kernel.bean.Car"/>
通過工廠方法
靜態工廠
調用靜態工廠創建 Bean 是將對象創建的過程封裝到靜態方法中,當調用者需要時,只需調用靜態方法就可以返回對象。
要聲明靜態工廠創建 Bean,在 class 屬性中指定靜態工廠,同時在 \<factory-method> 元素中指定可以返回對象的靜態方法,最后使用 \<constructor-arg> 元素為該方法傳遞參數。
<bean id="car" class="com.kernel.bean.factory.StaticCarFactory" factory-method="getCar">
<constructor-arg name="name" value="奧迪"/>
</bean>
實例工廠
調用實例工廠創建 Bean 是將對象封裝到另一個對象的實例中,當調用者需要時,只需調用實例方法就可以返回對象。
要聲明實例工廠創建 Bean,首先要聲明一個實例工廠 Bean,然后在 \<factory-bean> 元素中指定實例工廠 Bean,在\<factory-method> 元素中指定工廠提供的方法,最后使用 \<constructor-arg> 元素為該方法傳遞參數。
<bean id="carFactory" class="com.kernel.bean.factory.InstanceCarFactory"/>
<bean id="car" factory-bean="carFactory" factory-method="getCar">
<constructor-arg name="name" value="奧迪"/>
</bean>
Bean Factory
String 有兩種 Bean,一種是普通的 Bean,另一種是 Bean Factory,它與普通的 Bean 不同,它返回的類型是是 getObject方法所返回的對象類型。
<bean id="car" class="com.kernel.bean.CarFactoryBean"/>
屬性注入
<?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">
<bean id="car" class="com.kernel.bean.Car">
<!--屬性注入必須提供屬性所對應的的set方法-->
<!--屬性注入使用<property>元素,使用name屬性指定Bean的屬性名稱,value屬性或<value>子節點指定屬性值-->
<!--若value屬性中包含特殊字符,可以使用<![CDATA[]]把屬性值包裹起來-->
<property name="brand" value="奧迪"/>
<property name="price" value="300000"/>
<property name="speed">
<value>200</value>
</property>
</bean>
</beans>
構造器注入
<bean id="user" class="com.kernel.bean.User">
<!--使用構造器注入必須提供與之對應的構造函數-->
<!--按索引匹配入參-->
<constructor-arg name="username" index="0" value="kernel"/>
<!--按索引匹配入參-->
<constructor-arg name="password" index="1"value="123456"/>
<!--按類型匹配入參-->
<constructor-arg name="realName" type="java.long.String"value="灰白色"/>
<!--引用注入-->
<property name="car" ref="car"/>
</bean>
當 Bean 實例僅僅給一個特定的屬性使用時,可以將其聲明為內部 Bean,內部 Bean 聲明在<property>或者<value>元素中,不需要設置 id 和 name,不允許在其他地方使用。
<bean id="user" class="com.kernel.bean.User">
<constructor-arg name="username" value="kernel"/>
<constructor-arg name="password" value="123456"/>
<constructor-arg name="realName" value="灰白色"/>
<property name="car">
<bean class="com.kernel.bean.Car">
<property name="brand" value="奔馳"/>
<property name="price" value="400000"/>
<property name="speed" value="180"/>
</bean>
</property>
</bean>
java.util.List 通過 \<list> 定義,標簽里可以使用 \<value> 指定簡單的常量值,使用 \<ref> 指定 Bean。
java.util.Set通過 \<Set> 定義,規則和 \<list> 一樣。
java.util.Map 通過 \<map> 定義,標簽里可以使用多個 \<entry> 作為子標簽,每個標簽包含一個 \<key> 和 \<value>。
java.util.Properties 通過 \<props> 定義,標簽里使用多個 \<prop> 作為字標簽,每個標簽包含一個 \<key> 和\<value>。
<bean id="user" class="com.kernel.bean.User">
<property name="username" value="kernel"/>
<property name="password" value="123456"/>
<property name="realName" value="灰白色"/>
<property name="cars">
<list>
<ref bean="car"/>
<ref bean="car"/>
</list>
</property>
<property name="childs">
<set>
<ref bean="car"/>
</set>
</property>
<property name="scores">
<map>
<entry key="語文" value="100"></entry>
<entry key="數學" value="99"/>
</map>
</property>
<property name="properties">
<props>
<prop key="url">jdbc:mysql:///test</prop>
</props>
</property>
</bean>
使用基本的集合標簽不能將集合作為單獨的 Bean 定義,導致其他的 Bean 無法引入,可以使用 util schema 的集合標簽定義獨立的集合 Bean。
<?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:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<bean id="car" class="com.kernel.bean.Car">
<property name="brand" value="奧迪"/>
<property name="price" value="300000"/>
<property name="speed" value="180"/>
</bean>
<util:list id="cars">
<ref bean="car"/>
<ref bean="car"/>
<ref bean="car"/>
</util:list>
<bean id="user" class="com.kernel.bean.User">
<property name="username" value="kernel"/>
<property name="password" value="123456"/>
<property name="realName" value="灰白色"/>
<property name="cars" ref="cars"/>
<property name="childs">
<set>
<ref bean="car"/>
</set>
</property>
<property name="scores">
<map>
<entry key="語文" value="100"></entry>
<entry key="數學" value="99"/>
</map>
</property>
<property name="properties">
<props>
<prop key="url">jdbc:mysql:///test</prop>
</props>
</property>
</bean>
</bean
為了簡化 XML 的配置,Spring2.5 開始引入了一個新的 p 命名空間,可以通過<bean>元素屬性的方式配置屬性。
<bean id="car" class="com.kernel.bean.Car" p:brand="奧迪" p:price="300000" p:speed="180"/>
可以使用<bean>的 autowire 屬性設置自動裝配,有兩種方式:
byName:根據名稱裝配,屬性名和要裝配 Bean 名稱必須完全相同,否則裝配失敗。
byType:根據類型裝配,如果存在兩個類型相同的 Bean,則裝配失敗。
<bean id="address" class="com.kernel.bean.Address" p:city="上海" p:street="廣川"/>
<bean id="car" class="com.kernel.bean.Car" p:brand="奧迪" p:price="300000" p:speed="180"/>
<bean id="person" class="com.kernel.bean.Person" autowire="byName"/>
缺點:對于一個 Bean,不能選擇只裝配其中的某一個屬性,不夠靈活,再一個是要么選擇 byName,要么選擇 byType,兩者不能混搭。
繼承關系
Spring 允許繼承 Bean 的配置,子 Bean 從父 Bean 繼承配置,如果父 Bean 只想作為模板,可以將父 bean 設為 abstract,并不是所有的 Bean 配置都會被繼承,如果父 Bean 的 class 屬性沒有配置,它必須為抽象 Bean。
<bean id="address" class="com.kernel.bean.Address" p:city="上海" p:street="廣川" abstract="true"/>
<bean id="address1" parent="address"/>
依賴關系
Soring 允許設置前置依賴,可以通過 depends-on 屬性設置依賴 Bean,多個 Bean 通過逗號或空格隔開,依賴 Bean 在本 Bean 實例化之間實例化。
<bean id="person" class="com.kernel.bean.Person" depends-on="car address1" p:car-ref="car" p:address-ref="address1"/>
在 Spring 中,允許在<bean>元素的 scope 屬性設置 Bean 的作用域。
singleton:在初始化 IOC 容器的時候就會創建一個實例,再次獲取,都會返回這一個實例。
prototype:在初始化 IOC 容器的時候不會創建實例,每次獲取都會返回一個不同的實例。
request:每次請求 HTTP 都會返回一個新的 Bean 實例,僅適用于 WebApplicationContext。
seesion:同一個 HTTP Session 使用一個 Bean 實例,僅適用于WebApplicationContext。
配置 Bean 時,有時候需要在配置文件中混入系統部署的細節信息,而這些部署文件要和 Bean 配置文件分離。Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactory 后置處理器,這個處理器允許將部署信息存放到外置文件中,可以在 Bean 配置中使用 ${var} 的方式引入屬性值。
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
</bean>
Spring 表達式語言,支持運行時查詢和操作對象的表達式語言。
以#{}作為定界符。
支持算數、比較、邏輯運算符,支持字符串拼接和 if、else。
通過 SpEL 可以實現:
引用 bean
調用方法以及引入對象的屬性
計算表達式的值
正則表達式的匹配
<bean id="car" class="com.kernel.bean.Car">
<property name="brand" value="奧迪"/>
<property name="price" value="#{T(Integer).MAX_VALUE}"/>
<property name="speed" value="#{T(Integer).MIN_VALUE}"/>
</bean>
<bean id="address" class="com.kernel.bean.Address">
<property name="city" value="#{car.price > 300000?'上海':'高唐'}"/>
</bean>
<bean id="person" class="com.kernel.bean.Person">
<property name="name" value="#{car.brand}"/>
<property name="car" value="#{car}"/>
<property name="address" value="#{address}"/>
</bean>
Spring IOC 可以管理 Bean 的生命周期并允許在特定點執行定制任務。
Spring ICO 對 Bean 的生命周期管理的過程:
構造 Bean 實例。
為 Bean 設置屬性。
初始化 Bean。
當容器關閉時,調用 Bean 的銷毀方法。
在 Bean 的生命中可以通過 init-method 和 destroy-method 指定初始化和銷毀方法。
Bean 的后置處理器允許在調用初始化方法之前對 Bean 進行額外處理。
Bean 的后置處理器對所有 Bean 實例逐一處理,對于 Bean 后置處理器來講,必須實現 BeanPostProcessor,
Spring 將每個 Bean 傳遞給兩個方法:
Object postProcessBeforeInitialization:在初始化方法調用前執行。
Object postProcessAfterInitialization:在初始化方法調用后執行。
添加 Bean 后置處理器后 Bean 的生命周期
構建 Bean 實例。
為 Bean 設置屬性。
將 Bean 傳遞給 Bean 后置處理器的 postProcessBeforeInitialization 方法。
初始化 Bean。
將 Bean 傳遞給 Bean 后置處理器的 postProcessAfterInitialization 方法。
當容器關閉時,調用 Bean 的銷毀方法。
<bean id="car" class="com.kernel.bean.Car" p:brand="奧迪" p:price="300000" p:speed="200" init-method="init" destroy-method="destroy"/>
<!--配置Bean后置處理器-->
<bean class="com.kernel.MyBeanPostProcessor"/>
創建兩個泛型類,并配置兩者的依賴關系,對于繼承這兩個類的子類,如果泛型相同,則會繼承這種依賴關系。
當程序在運行期間需要給程序的運行增加一些特定需求,例如日志,可以修改代碼實現,但是要修改的函數過多,就會特別麻煩。可以使用動態代理實現:
使用一個代理將對象包裹起來,讓代理對象代替原對象執行方法。
public class ArithmeticCalculatorProxy {
// 被代理對象
private ArithmeticCalculator target = null;
public void ArithmeticCalculator(ArithmeticCalculator target) {
this.target = target;
}
public ArithmeticCalculator getProxy(){
// 代理對象
ArithmeticCalculator proxy;
// 代理對象用哪個類加載器加載
ClassLoader classLoader = target.getClass().getClassLoader();
// 代理對象的類型
Class[] interfaces = new Class[]{ArithmeticCalculator.class};
// 當代理對象執行其中的方法時的邏輯
InvocationHandler h = new InvocationHandler() {
/**
* @param proxy 要返回的代理對象
* @param method 正在被調用的方法
* @param args 傳參
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("The method" + method.getName() + "begins with" + Arrays.asList(args));
Object result = method.invoke(args);
return result;
}
};
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader, interfaces, h);
return proxy;
}
}
面向切面編程(AOP)是對面向對象編程(OOP)的一個補充,AOP 的主要關注對象是切面(aspect)。
面向切面編程時,仍需定義公共功能,但可以明確定義功能在哪里,以什么方式應用,并且無需更改受影響的類。
AOP 的好處是:
每個事物邏輯位于一個文件,代碼不分散,便于維護和升級。
業務模塊更簡潔,只包含核心業務代碼。
AOP 術語:
切面:公共功能,在上例中切面就是日志功能。
通知:切面必須要完成的工作,上例中通知就是加減乘除。
目標:被通知的對象。
代理:代理類。
連接點:程序執行的某個特定位置。
切點:每個類擁有多個連接點,AOP 通過切點定位到特定的連接點。
Java 社區中最流行的 AOP 框架。
基于注解方式,要在 IOC 容器中添加注解支持,只需要在配置文件中添加<aop:aspectj-autoproxy/>,自動為匹配到類生成代理對象
要在 Spring 中聲明切面,只需要將其聲明為 Bean,當在 IOC 容器中聲明切面類后,IOC 就會為那些與切面類匹配的類生成代理對象,切面是一個帶有@Aspect 注解的 Java 類。
通知是標注有某種注解的 Java 方法。
Aspect 支持五種類型的通知注解:
@Before
/**
* 前置通知,方法執行前執行
*/
@Before("execution(* com.kernel.spring.aop.impl.ArithmeticCalculatorImpl.*(..) )")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method:" + methodName + " begins with " + args);
}
@After
/**
* 后置通知,方法執行后執行
*/
@After("execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method:" + methodName + " end");
}
@AfterReturning
/**
* 返回值通知,方法執行成功后執行,可以訪問到方法的返回值
*/
@AfterReturning(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method:" + methodName + " end with " + result);
}
@AfterThrowing
/**
* 異常通知,方法執行發生異常后執行,可以訪問到異常對象,同時可以在出現特定異常后執行響應的邏輯
*/
@AfterThrowing(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs exception:" + ex);
}
@around
/**
* 環繞通知需要攜帶 proceedingJoinPoint 類型的參數
* 環繞通知相當于動態代理的全過程
* 環繞通知必須有返回值,返回值即為方法執行后的返回值
*/
@Around(value = "execution(* com.kernel.spring.aop.impl.ArithmeticCalculator.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
// 前置通知
System.out.println("The method:" + methodName + " begins with " + Arrays.asList(proceedingJoinPoint.getArgs()));
result = proceedingJoinPoint.proceed();
// 返回值通知
System.out.println("The method:" + methodName + " end with " + result);
} catch (Throwable e) {
// 異常通知
System.out.println("The method occurs exception:" + e);
}
// 后置通知
System.out.println("The method:" + methodName + " end");
return result;
}
優先級:可以通過 @order 注解設置切面類的優先級
基于 XML 方式
<bean id="arithmeticCalculator" class="com.kernel.spring.aop.impl.ArithmeticCalculatorImpl"/>
<bean id="loggerAscpect" class="com.kernel.spring.aop.impl.LoggerAscpect"/>
<!--配置AOP-->
<aop:config>
<!--配置切入點表達式-->
<aop:pointcut id="pointcut"
expression="execution(* com.kernel.spring.aop.impl.ArithmeticCalculatorImpl.*(..) ))"/>
<!--配置切面及通知-->
<aop:aspect ref="loggerAscpect" order="1">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after method="afterMethod" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" returning="result" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
為了使 JDBC 更加易用,Spring 在 JDBC API 上建立了一個抽象層,缺點是畢竟是 Spring 提供了一個小工具,不支持級聯操作。
使用 SQL 語句更新數據庫
String sql = "UPDATE empoyees SET EMAIL = ? WHERE id = ?";
int update = jdbcTemplate.update(sql, "Tom@163.com", 1);
批量更新
String sql = "INSERT INTO empoyees(LAST_NAME, EMAIL,DEPT_ID) VALUES (?, ?, ?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"AA","aa@163.com", 1});
batchArgs.add(new Object[]{"BB","bb@163.com", 2});
batchArgs.add(new Object[]{"CC","cc@163.com", 3});
batchArgs.add(new Object[]{"DD","dd@163.com", 2});
batchArgs.add(new Object[]{"EE","ee@163.com", 1});
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
查詢單個記錄
String sql = "SELECT ID, LAST_NAME lastName, EMAIL FROM empoyees where id = ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
查詢多個記錄
String sql = "SELECT ID, LAST_NAME lastName, EMAIL FROM empoyees";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
List<Employee> maps = jdbcTemplate.query(sql, rowMapper);
事務是企業級開發必不可少的技術,用來保證數據的一致性和完整性,事務是一系列的操作,要么都完成,要么都不完成。
原子性:事務中包含的邏輯不可再分。
一致性:事務執行前后,數據的完整性保持一致。
隔離性:事務在執行期間,不能受到其他事務的影響。
持久性:事務執行成功,應該持久化到磁盤上。
當事務方法被另一個事務調用時,必須指定事務的傳播方式。
使用 @Transactional 注解中的 Propagation 指定事務的傳播行為。
REQUIRED:如果有事務在運行,當前方法就在這個事務中運行,否則創建新事務。
REQUIRES_NEW:當前方法必須創建新事務,并在自己的事務中運行,原事務掛起。
臟讀是指讀到了別的事務回滾前的臟數據。
不可重復讀取是指同一個事務在整個事務過程中對同一筆數據進行讀取,每次讀取結果都不同。
幻讀是指同樣一筆查詢在整個事務過程中多次執行后,查詢所得的結果集是不一樣的。
Read Uncommitted 讀未提交 ,引發臟讀問題
Read Committed 讀已提交,解決臟讀,引發不可重復讀問題(Oracle默認隔離級別)。
Repeatable Read 重復讀,解決不可重復讀,未解決幻讀(MySQL默認隔離級別)
Serializable,可串行化 解決所有問題
隔離級別分類
按性能從高到低可劃分為:讀未提交>讀已提交>重復讀>可串行化
按攔截程序從高到低可劃分為:可串行化>重復讀>讀已提交>讀未提交
注解方式
<!-- 配置事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 啟用事務注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
// 使用Propagation指定事務的傳播行為
// 使用Isolation指定事務的隔離級別
// 使用noRollbackFor指定事務不回滾的異常
// 使用readOnly指定事務是否只讀
// 使用timeout指定強制回滾操作之前事務占用的時間
@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, noRollbackFor = BookStockException.class)
@Override
public void purchase(String username, String isbn) throws Exception {
int bookPriceByIsbn = bookShopDao.findBookPriceByIsbn(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(username, bookPriceByIsbn);
}
}
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:tx="http://www.springframework.org/schema/tx" 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/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 導入資源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置C3P0數據源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="initialPoolSize" value="${jdbc.initalPoolSize}"/>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="bookShopDao" class="com.kernel.spring.tx.xml.BookShopDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="bookShopService" class="com.kernel.spring.tx.xml.BookShopServiceImpl">
<property name="bookShopDao" ref="bookShopDao"/>
</bean>
<bean id="cashier" class="com.kernel.spring.tx.xml.CashierImpl">
<property name="bookShopService" ref="bookShopService"/>
</bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事務屬性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="purchase" propagation="REQUIRES_NEW"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事務切入點-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kernel.spring.tx.xml.BookShopService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
在 web.xml 中配置 Spring 配置文件的名稱和路徑,并且配置啟動 IOC 容器的 ServletContextListener。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置Spring配置文件的名稱和路徑-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--啟動IOC容器的ServletContextListener-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
獲取 IOC 容器:
在 Web 應用中,我們用到的 IOC 容器是 WebApplicationContext,它繼承自 ApplicationContext,它的初始化方式和 ApplicationContext 有所不同,因為 WebApplicationContext 需要 ServletContext 實例,所以我們必須在 web.xml 配置 Servlet 或者 Web 容器監聽器。
Spring 提供了用于啟動 WebApplicationContext 的 Servlet 和 Web 容器監聽器:
org.springframework.web.context.ContextLoaderServlet
org.springframework.web.context.ContextLoaderListener
那么 ContextLoaderListener 做了什么呢?
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
繼續追蹤 WebApplicationContext,ContextLoader 將 WebApplicationContext 放到了 Servlet中,Servlert 是一個 MAP 容器,WebApplicationContext 的 KEY 是 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。
Spring提供了一個WebApplicationContextUtils類,可以方便的取出WebApplicationContext,只要把ServletContext傳入就可以了。
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。