这是我学习Spring的笔记,方便我以后复习用的,希望对你也有帮助|ू・ω・` ),若发现笔记中知识点有纰漏,望指出。
文章目录
Spring的简单概述
Spring是一个开源框架
Spring为简化企业级应用开发而生,使用Spring开源使简单的JavaBean实现以前只有EJB才能实现的功能
Spring是JavaSE/EE的一站式框架
方便解耦,简化开发:
Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理。
AOP编程的支持:
Spring提供面向切面编程,可以方便的实现对程序权限拦截、运行监控等功能。
可能有人对耦合不熟悉:(Java开发的原则:高内聚低耦合)
耦合:指模块与模块之间的连接程度。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。
声明式事务的支持:
只需要通过配置就可以完成对事务的管理,而无需手动编程。
其优点:
方便程序的测试:
Spring对Junit4支持,可以通过注解的测试Spring程序。
方便集成各种优秀框架:
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis等)的直接支持。
降低JavaEE API的使用难度:
Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),提供了封装,使这些API应用难度大大降低。
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的入门实例
本人使用idea,可以创建Maven,然后创建一个web模板:
点击创建进入后,
点击后,等待一段时间,创建完成
我会在resources下创建一个:
其名为applicationContext.xml。
下面便进行Spring的入门实例代码:
Spring核心开发包:
pom.xml:(这是一个导入基础包的操作)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
UserService接口:(方便我使用)
package com.imooc.ioc.demo1;
public interface UserService {
public void sayHello();
}
UserServiceImp1.java:
package com.imooc.ioc.demo1;
public class UserServiceImp1 implements UserService {
//添加属性
private String name;
public void sayHello() {
System.out.println("Hello Spring" + name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SpringDemo1.java:(试验代码)
package com.imooc.ioc.demo1;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo1 {
@Test
/**
* 传统方式开发
*/
public void demo1(){
//UserService userService = new UserServiceImp1();
UserServiceImp1 userService = new UserServiceImp1();
//设置属性
userService.setName("哈哈");
userService.sayHello();
}
@Test
/**
* Spring的方式实现
*/
public void demo2(){
//创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类
UserService userService = (UserService)applicationContext.getBean("userService");
userService.sayHello();
}
}
applicationContext.xml
<!-- UserService的创建权交给了Spring -->
<bean id="userService" class="com.imooc.ioc.demo1.UserServiceImp1">
<!--设置属性-->
<property name="name" value="哈哈"/>
</bean>
其demo1和demo2运行结果都为:
SpringDemo1.java中也可以添加如代码来读取磁盘系统中的配置文件:
@Test
/**
* 读取磁盘系统中的配置文件
*/
public void demo3(){
//创建Spring的工厂类
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("F:\\applicationContext.xml");
//通过工厂获得类
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
}
二、Spring IOC&DI
1、 IOC(控制反转),将对象间的依赖关系交给Spring容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式,实现解耦合。可以通过注解@Autowired和@Resource来注入对象,被注入的对象必须被下边的四个注解之一标注:
@Controller
@Service
@Repository
@Component
2、在Spring配置文件中配置<context:annotation-config/>元素开启注解。
DI(依赖注入),IOC的另一种表述方式:即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源(对象等)。
即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。
依赖:指bean对象创建依赖于容器。Bean对象的依赖资源
注入:指bean对象依赖的资源由容器来设置和装配
其注入方式有四种:
接口注入(Interface Injection) Spring早期版本使用
setter注入(setter Injection) 通过setter方法注入属性
构造注入(Constructor Injection) 通过构造方法注入属性
名称空间注入 Spring中的一种实现注入的方式,依赖于setter注入或者构造注入,依赖于setter注入的实现时P名称空间注入,依赖于构造注入的实现是C名称空间注入
IOC: Spring 反向控制应用程序需要的资源。
DI: 应用程序依赖Spring为其提供资源。
XML方式
1、三种实例化Bean的方式
1、使用类构造器实例化(默认无参数)
2、使用静态工厂方法实例化(简单工厂模式)
3、使用实例工厂实例化(工厂方法模式)
1、默认无参数
Bean1.java:
package com.imooc.ioc.demo2;
import org.springframework.context.annotation.Bean;
/**
* Bean的实例化的三种方式,采用无参数的构造方法的方式
*/
public class Bean1 {
public Bean1(){
System.out.println("Bean1被实例化了......");
}
}
applicationContext.xml配置:
<!--第一种:无参构造器的方式-->
<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
2、简单工厂模式
Bean2.java:
package com.imooc.ioc.demo2;
/**
* Bean的实例化方式:静态工厂实例化方式
*/
public class Bean2 {
}
Bean2Factory.java:
package com.imooc.ioc.demo2;
/**
* Bean的静态工厂
*/
public class Bean2Factory {
public static Bean2 createBean2(){
System.out.println("Bean2Factory已经执行......");
return new Bean2();
}
}
applicationContext.xml配置:
<!--第二种:静态工厂的方式-->
<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
3、工厂方法模式
Bean3.java:
package com.imooc.ioc.demo2;
/**
* Bean的实例化三种方式:实例化工厂
*/
public class Bean3 {
}
Bean3Factory.java:
package com.imooc.ioc.demo2;
/**
* Bean3的实例工厂
*/
public class Bean3Factory {
public Bean3 createBean3(){
System.out.println("Bean3Factory执行了......");
return new Bean3();
}
}
applicationContext.xml配置:
<!--第三种:实例工厂的方式-->
<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
总的运行:
SpringDemo2:
package com.imooc.ioc.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的实例化的三种方式
*/
public class SpringDemo2 {
@Test
public void demo1(){
//创建工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean1 bean1 = (Bean1)applicationContext.getBean("bean1");
}
@Test
public void demo2(){
//创建工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean2 bean2 = (Bean2)applicationContext.getBean("bean2");
}
@Test
public void demo3(){
//创建工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean3 bean3 = (Bean3)applicationContext.getBean("bean3");
}
}
其结果为:
2、Beam的配置和作用域
id和name:
一般情况下,装配一个Bean时,通过指定一个id属性作为Bean的名称;
id属性在IOC容器中必须是唯一的;
如果Bean的名称中含有特殊字符,就需要使用name属性。
class:
class用于设置一个类的完全路径名称,主要作用是IOC容器生成类的实例。
作用域(scope属性):
singleton:在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在(单例)
prototype:每次调用getBean()时都会返回一个新的实例(多例)
request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session:同一个HTTP Session共享一个Bean,不同的HTTPSession使用不同的Bean。该作用域仅适用于WebApplicationContext环境
3、Spring容器中Bean的生命周期方法
Spring初始化bean或销毁bean时,有时需要做一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<bean id="xxx" class="...XXX" init-method="init" destory-method="destroy"/>
init: 当bean被载入到容器的时候调用
destory-method: 当bean从容器中删除的时候调用(scope=singleton有效)
注意: web容器中会自动调用,但是main函数或测试用例需要手动调用
Bean的生命周期具体可以分为11步:
1、instantiate bean对象实例化
2、populate properties封装属性
3、如果Bean实现BeanNameAware执行setBeanName
4、如果Bean实现BeanFactoryAware或者ApplicationContextAware设置工厂setBeanFactory或者上下文对象setApplicationContext
5(重点)、 如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforeInitialization
6、如果Bean实现InitializingBean执行afterPropertiesSet
7、调用指定初始化方法init
8(重点)、 如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterInitialization
9、执行业务处理
10、如果Bean实现DisposableBean执行destroy
11、调用指定销毁方法customerDestroy
代码演示:
Man.java:
package com.imooc.ioc.Demo3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Man implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public Man() {
System.out.println("第一步:初始化...");
}
public void setName(String name) {
System.out.println("第二步:设置属性");
this.name = name;
}
public void setBeanName(String s) {
System.out.println("第三步:设置Bean的名称" + name);
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
System.out.println("第四步:了解工厂的信息");
}
public void afterPropertiesSet() {
System.out.println("第六步:属性设置后");
}
public void setup() {
System.out.println("第七步:MAN被初始化了...");
}
public void run() {
System.out.println("第九步:执行业务方法");
}
public void destroy() {
System.out.println("第十步:执行Spring的销毁方法");
}
public void teardown() {
System.out.println("第十一步:MAN被销毁了...");
}
}
MyBeanPostProcessor.java:(第五步和第八步)
package com.imooc.ioc.Demo3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:初始化前方法...");
return bean;
}
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("第八步:初始化后方法...");
return bean;
}
}
applicationContext.xml:
<bean id="man" class="com.imooc.ioc.Demo3.Man" init-method="setup" destroy-method="teardown">
<property name="name" value="嘻嘻"/>
</bean>
<bean class="com.imooc.ioc.Demo3.MyBeanPostProcessor"/>
Spring.java:
package com.imooc.ioc.Demo3;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo3 {
@Test
public void demo2(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Man man = (Man) applicationContext.getBean("man");
man.run();
applicationContext.close();
}
}
beanpostprocessor的作用:
可以在增删改查的方法中的保存时添加权限效验:
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
if ("userDao".equals(beanName)) {
Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("权限效验===========");
return method.invoke(bean, args);
}
return method.invoke(bean, args);
}
});
return proxy;
} else {
return bean;
}
}
}
4、Spring的属性注入
对于类成员变量,注入方式有三种:
构造函数注入:
通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用;构造器注入在元素里声明的属性。
编写生成类的构造方法后,
<bean id="xxx" class="xxx.xxx.xxx">
<constructor-arg name="xxxx" value="xx"/>
</bean>
属性setter方法注入:
使用set方法注入,在Spring配置文件中,通过设置注入的属性,
用get、set方法生成后,
<bean id="xxx" class="xxx.xxx.xxx">
<property name="xx" value="xxx"/>
<property name="qita" ref="qita"/><!-- ref 可以引入其他bean的id-->
</bean>
<bean id=" qita" class="xxx.xxx.xxx">
<property name="xxx" value="xxxx"/>
</bean>
p名称空间的属性注入
使用p名称空间:
为了简化XML文件配置,Spring从2.5开始引入一个新的p名称空间
p:<属性名> = “xxx” 引入常量值
p:<属性名>-ref = "xxx"引用其它Bean对象
xmlns="http://www.springframework.org/schema/beans"<!-- 把原先的在xmlns后加 :p,并且在最后把beans改为p-->
xmlns:p="http://www.springframework.org/schema/p"
<bean id="xxx" class="xxx.xxx.xxx" p:name="xx" p:qita-ref="qita">
<bean id="qita" class="xxx.xxx.xxx" p:name="xxx">
产生的效果与set方法注入一样。
SpEL属性注入:
SpEL:spring expression language,spring表达式语法,对依赖注入进行简化
语法:#{表达式}
<bean id=" " value="#{表达式}">
SpEL表达式语言:
语法:#{}
#{‘hello’}:使用字符串
#{topicId3}:使用另一个bean
#{topicId4.content.toUpperCase()}:使用指定名属性,并使用方法
#{T(java.lang.Math).PI}:使用静态字段或方法
<bean id="xxx" class="xxx.xxx.xxx">
<property name="xxx" value="#{...}"/>
</bean>
复制类型的属性注入
数组类型的属性注入
List集合类型的属性注入
Set集合类型的属性注入
Map集合类型的属性注入
Properties类型的属性注入
代码实例操作:
CollectionBean.java:
package com.imooc.ioc.demo5;
import java.util.*;
public class CollectionBean {
private String[] arrs; //数组类型
private List<String> list; //List集合类型
private Set<String> set; //Set集合类型
private Map<String,Integer> map; //Map集合类型
private Properties properties; //属性类型
public String[] getArrs() {
return arrs;
}
public void setArrs(String[] arrs) {
this.arrs = arrs;
}
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, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean{" +
"arrs=" + Arrays.toString(arrs) +
", list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
applicationContext.xml:
<!--集合类型的属性注入-->
<bean id="collectionBean" class="com.imooc.ioc.demo5.CollectionBean">
<!--数组类型-->
<property name="arrs">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<!-- List集合的属性注入-->
<property name="list">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
</list>
</property>
<!-- Set集合的属性注入-->
<property name="set">
<set>
<value>ddd</value>
<value>eee</value>
<value>fff</value>
</set>
</property>
<!-- Map集合的属性注入-->
<property name="map">
<map>
<entry key="aaa" value="111"/>
<entry key="bbb" value="222"/>
<entry key="ccc" value="333"/>
</map>
</property>
<!-- Properties-->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">1234</prop>
</props>
</property>
</bean>
SpringDemo5 .java:
package com.imooc.ioc.demo5;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo5 {
@Test
public void demo1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean collectionBean = (CollectionBean)applicationContext.getBean("collectionBean");
System.out.println(collectionBean);
}
}
其运行结果为:
注解方式
Spring2.5引入使用注解去定义Bean
@Component描述Spring框架中Bean
除了@Component外,Spring提供了3个功能基本和@Component等效的注解
@Repository用于对DAO实现类进行标注
@Service用于对Service实现类进行标注
@Controller用于对Controller实现类进行标注
这三个注解是为了让标注本身的用途清晰,Spring在后续版本会对其增强
applicationContext.xml:
替换:
<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">
并配置:
<context:component-scan base-package="包名"/>
就不需要向传统方式需要去XML中配置,而现在只需要在该类中@Component(“xxx”) ---->xxx相当于id
注解方式的属性注入:
使用@Autowired进行自动注入
@Autowired默认按照类型进行注入,如果存在两个相同Bean类型相同,则按照名称注入
@Autowired注入是可以针对成员变量或者set方法
通过@Autowired的required属性,设置一定要找到匹配的Bean
使用@Qualifier指定注入Bean的名称,使用Qualifier指定Bean名称后,注解Bean必须指定相同名称
Spring提供对JSR-250中定义@Resource标准注解的支持,@Resource和@Autowired注解功能相似 (@Autowired+@Qualifier = = @Resource)
在上面我们写到Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<bean id="xx" class="xxx.xxx.xxx" init-method="init" destory-method="destroy"/>
当bean被载入到容器的时候调用init,注解方式:@PostConstruct ,初始化
当bean从容器中删除的时候调用destroy(scope=singleton有效),注解方式:@PreDestroy ,销毁
Bean的作用范围:
使用注解配置的Bean和配置的一样,默认作用范围都是singleton,@Scope注解用于指定Bean的作用范围
XML配置与注解配置混合使用
从上面的了解大致可得:
XML方式的优势:结构清晰,易于阅读
注解方式的优势:开发便捷,属性注入方便
XML与注解的整合开发:
1、引入context命名空间
2、在配置文件中添加context:annotation-config标签
在实例中,可以如下操作:
在xml配置:
<context:annotation-config/>
<bean id="xxx" class="xxx.xxx.xxx"/>
而在类中则用注解赋予:@Resource(name=“内容”)
三、Spring AOP
1、AOP的相关术语和实现的底层原理
何为AOP:
AOP Aspect Oriented Programing 面向切面编程
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
Spring AOP 使用纯Java实现,不需要专门的编程过程和类加载器,在运行期通过代理方式目标类织入增强代码
AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
AOP相关术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入。
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面):是切入点和通知(引介)的结合。
public class UserDaoImp1 implements UserDao{
public void save(User user) {
}
public void update(User user) {
}
public List find() {
}
public void delete(User user) {
}
}
Joinpoint(连接点):指的是可以被拦截到的点。
*增删改查这些方法都可以被增强,这些方法称为是连接点。
Pointcut(切入点):指的是真正被拦截到的点。
*只想对save方法进行增强(做权限效验),save方法称为是切入点。
Advice(通知):拦截后要做的事情。
*对save方法进行权限效验,权限效验的方法称为是通知。
Target(目标):被增强的对象
Weaving(织入):将Advice应用到Target的过程,将权限效验应用到UserDaoImp1的save
方法的这个过程。
Proxy(代理):被应用了增强后,产生一个代理对象。
Aspect(切面):就是切面点和通知的组合。
其实AOP的相关术语总的来说就是:
一个类中里面的方法可以被增强的方法,我们称为连接点(Joinpoint),但在实际开发中可能只需增强某一个或某几个,而真正被增强的方法,我们称为切入点(Pointcut),切入后如果做删除后的日志记录,那么日志记录就是通知(Advice),而被增强的对象我们称为目标(Target),然后我们把通知运用到目标的过程称为织入(Weaving),织入增强以后会产生一个代理类(Proxy),大概这个过程就是这样。
JDK的动态代理:(了解一下代码就好)
使用CGLIB生成代理:(了解一下就好)
对于不使用接口的业务类,无法使用JDK动态代理
CGLIB采用非常底层字节码技术,可以为一个类创建子类,解决无接口代理问题。
代理知识总结:
Spring在运行期,生成动态代理对象,不需要特殊的编译器
Spring AOP的底层技术推广JDK动态代理或CGLIB动态代理技术为目标Bean执行横向织入:
1、若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2、若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
程序中应优先对接口创建代理,便于程序解耦维护
标记为final的方法,不能被代理,因为无法进行覆盖:(因为无法重写父类或接口的方法)
1、JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
2、CGLIB是针对目标类生产子类,因此类或方法不能使final的Spring只支持方法连接点,不提供属性连接。
Spring AOP增强类型:
AOP联盟为通知Advicd定义了org.aopalliance.aop.Interface.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
—前置通知org.springframework.aop.MethodBeforeAdvice
----------在目标方法执行前实施增强
—后置通知org.springframework.aop.AfterReturningAdvice
----------在目标方法执行后实施增强
—环绕通知org.aopalliance.intercept.MethodInterceptor
----------在目标方法执行前后实施增强
—异常抛出通知org.springframework.aop.ThrowsAdvice
----------在方法抛出异常后实施增强
—(不要求掌握)引介通知org.springframework.aop.IntroductionInterceptor
----------在目标类中添加一些新的方法和属性
Spring AOP切面类型:
Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
IntroductionAdvisor:代表引介切面,针对引介通知而使用切面(不要求掌握)
下面便演示Advice切面案例:
先导入要用的包:
pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
创建一个接口,StudentDao.java:
package com.imooc.aop.demo2;
public interface StudentDao {
public void find();
public void save();
public void update();
public void delete();
}
StudenDaoImp1.java:
package com.imooc.aop.demo2;
public class StudenDaoImp1 implements StudentDao{
@Override
public void find() {
System.out.println("查询。。。");
}
@Override
public void save() {
System.out.println("保存。。。");
}
@Override
public void update() {
System.out.println("修改。。。");
}
@Override
public void delete() {
System.out.println("删除。。。");
}
}
applicationcontext.xml:
<!--配置目标类==========-->
<bean id="studentDao" class="com.imooc.aop.demo2.StudenDaoImp1"/>
上面便是AOP传统的准备工作:
下面将以前置增强为例:
MyBeforeAdvice.java:
package com.imooc.aop.demo2;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置增强===============");
}
}
Spring提供了一个类:
ProxyFactoryBean常用可配置属性
—target:代理的目标对象
—proxyInterfaces:代理要实现的接口
如果多个接口可以使用以下格式赋值
<list>
<value></value>
......
</list>
需要在applicationcontext.xml:添加
<!--Spring的AOP 产生代理对象==========-->
<bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置目标类-->
<property name="target" ref="studentDao"/>
<!--实现接口-->
<property name="proxyInterfaces" value="com.imooc.aop.demo2.StudentDao"/>
<!--采用拦截的名称-->
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
SpringDemo2.java:
package com.imooc.aop.demo2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo2 {
@Resource(name = "studentDaoProxy")
private StudentDao studentDao;
@Test
public void demo1() {
studentDao.find();
studentDao.save();
studentDao.update();
studentDao.delete();
}
}
其运行结果为:
在我们配置applicationContext.xml中,我们用到了proxyInterfaces(实现的接口)、interceptorNames(拦截的名称)其中除了它们还有一些:
—proxyTargetClass:是否对类代理而不是接口,设置为true时,使用CGLib代理
—interceptorNames:需要织入目标的Advice
—singleton:返回代理是否为单实例,默认为单例
—optimize:当设置为true时,强制使用CGLib
PointcutAdvisor切点切面
使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带有切点的切面
常用PointcutAdvisor实现类
—DefaultPointcutAdvisor最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面
—JdkRegexpMethodPointcut构造正则表达式切点(主要我们讲这个)
其在一个spring config中,创建一个applicationContext.xml(名字不做要求)配置如下:
<!--配置目标类-->
<bean id="aaa" class="xxx.xxx.AAA"/>
<!--配置通知-->
<bean id="bbb" class="xxx.xxx.MyAroundAdvice"/>
<!--一般的切面是使用通知作为切面的,因为要对目标类的某个方法进行增强就需要配置一个带有切入点的切面-->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--pattern中配置正则表达式: .任意字符 *任意次数 -->
<property name="pattern" value=".*"/> <!--若要对某个方法进行增强: value=".*xxx.*,.*yyy.*"
<property name="advice" ref="bbb"/>
</bean>
<!--配置产生代理-->
<bean id="cccProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="aaa"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value=""/>
</bean>
前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大,所以为了解决这一问题:
自动创建代理:
解决方案:自动创建代理:
—BeanNameAutoProxyCreator根据Bean名称创建代理
—DefaultAdvisorAutoProxyCreator根据Adcisor本身包含信息创建代理
—AnnotationAwareAsprctJAutoProxyCreator基于Bean中AspectJ注解进行自动代理
下面是一些案例:
BeanNameAutoProxyCreator举例
对所有以DAO结尾Bean所有方法使用代理:
上面的案例是对所有的方法进行代理,但如果要对某个进行代理,则需要用到下面的配置:
DefaultAdvisorAutoProxyCreator举例
配置环绕代理案例:
2、基于AspectJ的注解AOP开发
AspectJ简介:
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
使用AspectJ需要导入Spring AOP和AspectJ相关jar包
—spring-aop-4.2.4.RELEASE.jar
—com.springsource.org.aopalliance-1.0.0.jar
—spring-aspects-4.2.4.RELEASE.jar
—com.springssource.org.aspectj.weaver-1.6.8.RELEASE.jar
注解开发,环境的准备:
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
<!--开启AspectJ的注解开发,自动代理-->
<aop:aspectj-autoproxy/>
</beans>
pom.xml:
<!--spring的基本开发包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!--AOP-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!--方便测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!--AspectJ-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
@AspectJ提供不同的通知类型
@Before前置通知,相当于BeforeAdvice
@AfterReturning后置通知,相当于AfterReturningAdvice
@Around环绕通知,相当于MethodInterceptor
@AfterThrowing异常抛出通知,相当于ThrowAdvice
@After最终final通知,不管是否异常,该通知都会执行
@DeclareParents引介通知,相当于IntroductionInterceptor(不要求掌握)
在通知中通过value属性定义切点
通过execution函数,可以定义切点的方法切入
语法:
---execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>) <!--访问修饰符可有可无-->
例如
---匹配所以类public方法execution(public * *(..))
---匹配指定包下所有类方法execution(* com.imooc.dao.*(..)) 不包含子包
---execution(* com.imooc.dao..*(..)) ..*表示包、子孙包下所有类
---匹配指定类所有方法 execution(* com.imooc.service.UserService.*(..))
---匹配实现特定接口所有类方法
execution(* com.imooc.dao.GenericDAO+.*(..))
---匹配所有save开头的方法 execution(* save*(..))
@Before前置通知
可以在方法中传入JoinPoint对象,用来获得切点信息
//要增强的代码:
@Before("execution(* xxx.xxx.xxx.类名.方法(..))")
public void before(Joinpoint joinpoint) {
System.out.println("===前置通知===" + joinpoint);
}
@AfterReturing后置通知
通过returning属性可以定义方法返回值,作为参数
@AfterReturning(value="execution(* xxx.xxx.xxx.类名.方法(..))", returning="returing")
public void afterReturing(Object returing) {
System.out.println("===后置通知===" + returing);
}
@Around环绕通知
around方法的返回值就是目标代理方法执行返回值
参数为ProceedingJoinPoint可以调用拦截目标方法执行
@Around("execution(* xxx.xxx.xxx.类名.方法(..))")
public Object around(proceedingJoinPoint joinPoint) throws Throwable {
System.out.println("===环绕前通知===");
Object obj = joinPoint.proceed(); //调用目标方法
System.out.println("===环绕后通知===");
return obj;
}
重点: 如果不调用ProceedingJoinPoint的proceed方法,那么目标方法就被拦截了
@AfterThrowing异常抛出通知
通过设置throwing属性,可以设置发生异常对象参数
@AfterThrowing(value="execution(* xxx.xxx.xxx.类名.方法(..))", throwing="e")
public void afterThrowing(Throwable e) {
System.out.println("===异常抛出通知===" + e.getMessage());
}
@After最终通知
无论是否出现异常,最终通知总是会被执行的
@After(value="execution(* xxx.xxx.xxx.类名.方法(..))")
public void after() {
System.out.println("===最终通知===");
}
相信大家看了上面的注解方法大致上有一定了解注解的使用,应该也发现其出现的一个弊端,就是如果我的一个方法需要多种通知时,那就要重复写以上的方法,那有没有可以解决的方法呢?下面的就可以解决此类问题:
通过@Pointcut为切点命名
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointucut进行定义
切点方法:private void 无参数方法,方法名为切点名
当通知多个切点时,可以使用||进行连接
@Pointcut(value="execution(* xxx.xxx.xxx.类名.方法(..))")
private void myPointcut1(){} //因为是注解所以需要一个方法,我们便创建一个空方法
而使用只需要把 xxx.xxx.xxx.类名.方法(…)替换为myPointcut1(),便可达成目的。
3、基于AspectJ的XML方式的AOP开发
下面是一些实例,看看其设置方法:
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
<!--XML的配置方式完成AOP的开发-->
<!--配置目标类-->
<bean id="customerDao" class="com.imooc.aspectJ.demo2.CustomerDaoImpl"/>
<!--配置切面类-->
<bean id="myAspectXml" class="com.imooc.aspectJ.demo2.MyAspectXml"/>
<!--aop的相关配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.save(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.update(..))"/>
<aop:pointcut id="pointcut3" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.delete(..))"/>
<aop:pointcut id="pointcut4" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.findOne(..))"/>
<aop:pointcut id="pointcut5" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.findAll(..))"/>
<aop:aspect ref="myAspectXml">
<!--配置前置通知-->
<aop:before method="before" pointcut-ref="pointcut1"/>
<!--配置后置通知-->
<aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="result"/>
<!--配置环绕通知-->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!--配置异常抛出通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
<!--配置最终通知-->
<aop:after method="after" pointcut-ref="pointcut5"/>
</aop:aspect>
</aop:config>
</beans>
CustomerDao.java:(接口)
package com.imooc.aspectJ.demo2;
public interface CustomerDao {
public void save();
public String update();
public void delete();
public void findOne();
public void findAll();
}
CustomerDaoImpl.java:
package com.imooc.aspectJ.demo2;
public class CustomerDaoImpl implements CustomerDao {
public void save() {
System.out.println("保存。。。");
}
public String update() {
System.out.println("修改。。。");
return "update";
}
public void delete() {
System.out.println("删除。。。");
}
public void findOne() {
System.out.println("查询一个。。。");
int a = 1/0; //用于测试异常抛出
}
public void findAll() {
System.out.println("查询多个。。。");
}
}
MyAspectXml.java:
package com.imooc.aspectJ.demo2;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectXml {
//前置通知
public void before() {
System.out.println("XML方式的前置通知======");
}
//后置通知
public void afterReturing(Object result) {
System.out.println("XML方式的后置通知======" + result);
}
//环绕通知
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("XML方式的环绕前通知======");
Object obj = joinPoint.proceed(); //调用目标方法
System.out.println("XML方式的环绕后通知======");
return obj;
}
//异常抛出通知
public void afterThrowing(Throwable e) {
System.out.println("XML方式的异常抛出通知======" + e.getMessage());
}
//最终通知
public void after() {
System.out.println("XML方式的最终通知======");
}
}
SpringDemo2.java:
package com.imooc.aspectJ.demo2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class SpringDemo2 {
@Resource(name = "customerDao")
private CustomerDao customerDao;
@Test
public void demo1() {
customerDao.save();
customerDao.update();
customerDao.delete();
customerDao.delete();
customerDao.findOne();
customerDao.findAll();
}
}
其最后结果为:
四、Spring 事务管理
1、事务的概述
什么是事务:
—事务是正确执行一系列的操作(或动作),使得数据库从一种状态转换成另一种状态,且保证操作全部成功,或者全部失败。
事务原则是什么:
—事务必须服从ISO/IEC所制定的ACID原则。
—ACID原则的具体内涵如下:
原子性(Atomicity):
—即不可分割性,事务要么全部被执行,要么就全部不被执行。
一致性(Consistency):
—事务的执行使得数据库从一种正确状态转换成另一种正确状态。
隔离性(Isolation):
—在事务正确提交之前,它可能的结果不应显示给任何其他事务。
持久性(Durability):
—事务正确提交后,其结果将永久保存在数据库中。
Java事务
Java事务的产生:
—程序操作数据库的需要。以Java编写的程序或系统,实现ACID的操作。
Java事务实现:
—通过JDBC相应方法间接来实现对数据库的增、删、改、查,把事务转移到Java程序代码中进行控制;
—确保事务一要么全部执行成功,要么撤销不执行。
总结: Java事务机制和原理就是操作确保数据库操作的ACID特性。
Java事务实现模式
Java事务的实现:
—通过Java代码来实现对数据库的事务性。
Java事务类型:
—JDBC事务:用Connection对象控制的手动模式和自动模式;
—JTA(Java Transaction API)事务:与实现无关的,与协议无关的API;
—容器事务:应用服务器提供的,且大多是基于JTA完成(通常基于JNDI的,相当复杂的API实现)。
三种事务的差异:
JDBC事务:控制的局限性在一个数据库连接内,但是其使用简单。
JTA事务:功能强大,可跨越多个数据库或多DAO,使用比较复杂。
容器事务:主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
2、Spring事务核心接口
事务接口框架:
Spring事务管理器:
JDBC事务管理器(DataSourceTransactionManager)
—本事务管理器是通过调用java.sql.Connection来管理事务。
—Spring配置示例:---------------------------------------------------------
Hibernate事务管理器(HibernateTransactionManager)
—本管理器将事务管理的职责委托给org.hibernate.Transaction对象来管理事务,而后者是从Hibernate Session中获取到的。
—Spring配置方式:-------------------------------------------------------
JPA事务管理器(JpaTransactionManager)
—通过一个JPA实体管理工厂
(javax.persistence.EntityManagerFactory接口的任意实现)将与由工厂所产生的JPA EntityManager合作来构建事务。
JTA事务管理器(Jta TransactionManager)
—本管理器将将事务管理的责任委托给
javax.transaction.UserTransaction和javax.transaction.TransactionManager对象进行事务处理。
Spring事务属性定义
事务属性范围:
传播行为、是否只读?、事务超时、回滚规则、隔离规则
事务属性定义:
public interface TransactionDefinition {
//返回事务的传播行为
int getPropagationBehavior();
//返回事务的隔离级别,事务管理器根据它来控制
//另外一个事务可以看到本事务内的哪些数据
int getIsolationLevenl();
//返回事务必须在多少秒内完成
int getTimeout();
//事务是否只读,事务管理器能够根据这个
//这个返回值进行优化,确保事务是只读的
boolean isReadOnly();
}
事务传播行为
—当事务方法被另一个事务方法调用时,必须指定事务如何传播;
—Spring的7种传播行为:
事务隔离级别
—隔离级别定义了一个事务可能受其他并发事务影响的程度。
—隔离级别分为:
事务中注意的问题
事务是否只读:
—利用数据库事务的“只读”属性,进行特定优化处理。
注意:
—事务的是否“只读”属性,不同的数据库厂商支持不同。
—通常而言:只读属性的应用要参考厂商的具体支持说明,比如,
Oracle的“readOnly”不起作用,不影响其增删改查;
Mysql的“readOnly”为ture,只能查,增删改则出异常。
事务超时:
—事务超时就是事务的一个定时器,在特定时间内务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
设计事务时注意点:
—为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。
事务回滚:
—默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚。
自定义回滚策略:
—声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚;
—声明事务遇到特定的异常不回滚,即使这些异常时运行期异常。
其配置例如下:
<!--声明式事务配置开始-->
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="insert" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="delelte" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(*cn.sxt.service.impl.*.*(..))" id="pointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<!--声明事务配置结束-->
Spring事务状态
事务状态:
—通过事务管理器获取TransactionStatus实例;
—控制事务在回滚或提交的时候需要应用对应的事务状态;
—Spring事务接口:
//Spring事务状态接口:
//通过调用PlatformTransactionManager的getTransaction()
//获取事务状态实例
public interface TransactionStatus {
boolean isNewTransaction();//是否是新的事务
boolean hasSavepoint();//是否有恢复点
void setRollbackOnly();//设置为只回滚
boolean isRollbackOnly();//是否为只回滚
boolean isCompleted;//是否已完成
}
3、编程式事务管理
编程式事务实现方式:
模板事务(TransactionTrmplate)的方式:
—此为Spring官方团队推荐的编程式事务管理方式;
—主要工具为JdbcTemplate类。
平台事务管理器(PlatformTransactionManager)方式:
—类似应用JTA UserTransacyion API方式,但异常处理更简洁;
—辅助类为:TransactionDefinition和TransactionStatus。
编程式事务实现案例
模板事务(TransactionTemplate)案例:
—步骤:获取模板对象;选择事务结果类型;业务数据操作处理。
平台事务管理器(PlatformTransactionManager)案例:
—步骤:获取事务管理器;获取事务属性对象;
获取事务状态对象;创建JDBC模板对象;
业务数据操作处理。
编程事务总结
需要有效的数据源,具体数据源根据实际情况创建
创建编程事务管理对象:
—事务模板(TransactionTemplate)
—事务管理器(PlateformTransactionManager)
业务逻辑处理:
—基于JdbcTemplate完成业务处理。
4、声明式事务管理
声明式事务管理的配置类型:
—5种类型:独立代理;共享代理;
拦截器;tx拦截器;全注释。
声明式事务管理配置实现方式:
—5种类型的配置实现参考(配置代码案例)
—声明式事务管理完整案例:书籍管理操作。
5、事务管理最佳实践
编程事务管理和声明事务管理区别
编程式事务允许用户在代码中精确定义事务的边界;
声明式事务有助于用户将操作与事务规则进行解耦
–基于AOP交由Spring容器实现;
—实现关注点聚焦在业务逻辑上。
概括而言:
—编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;
而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
两种事务的选择
小型应用、事务操作少:
—建议编程式事务管理实现:TransactionTemplate;
—简单、显示操作、直观明显、可以设置事务名称。
大型应用,事务操作量多:
—业务复杂度高、关联性紧密,建议声明式事务管理实现;
—关注点聚焦到业务层面,实现业务和事务的解耦。
通用事务问题的解决方案
事务管理器类型:
—基于不同的数据源选择对应的事务管理器;
—选择正确的PlatformTransactionManager实现类;
—全局事务的选择:JtaTransactionManager。
笔记就先记录到这里,ψ(*`ー´)ψ