概览
Spring5框架包含许多特性,负责管理项目中的所有对象,并被很好地组织在下图所示的模块中
核心容器:由spring-beans、spring-core、spring-context、spring-expression(SpEL)4个模块组成,如下:
1. spring-bean和spring-core模块是spring框架核心模块,包含了IOC和DI,beanFactory接口是spring框架中的核心接口,是工厂模式的具体体现
2. spring-context模块架构于核心模块之上,扩展了beanFactory,并为之添加了bean生命周期控制、框架事件体系、资源透明化等体系,此外还提供了企业级的支持,
例如邮件发送、远程访问、任务调度等,ApplicationContext是spring-context模块的核心接口,该接口的继承图参加下文;
3. spring-expression模块是统一表达式语言(EL)的扩展模块,最出色的功能是函数调用和简单字符串的模板函数;
AOP和设备支持:由spring-aop、spring-aspects和spring-instrument3个模块组成
1. spring-aop是另一个核心模块,是AOP主要实现模块,在spring中以JVM的动态代理为基础设计出一系列的AOP横切实现,例如:前置通知等,同时Pointcut接口来匹配
切入点,然后使用切入点来设计横切面;
2. spring-aspects模块集成自AspectJ框架,主要是为SpringAOP提供多种AOP实现方法;
3. spring-instrument模块是基于JAVASE中的"java.lang.instrument"进行设计的,是AOP的一个支援模块。作用:在JVM启用时,生成一个代理类,程序员通过代理类
在运行时修改类的字节,从而改变一个类的功能,实现AOP的功能;
数据访问及集成:由spring-jdbc、spring-tx、spring-orm、spring-jms和spring-oxm5个模块组成
1. spring-jdbc模块是Spring提供的JDBC抽象框架的主要实现模块,用于简化JDBC编程,主要实现类是(Simple)JdbcTemplate、NamedParameterJdbcTemplate
2. spring-tx模块是SpringJDBC事务控制实现模块,对事务做了很好的封装,通过它的AOP配置,可以灵活的配置在任何一层;
3. spring-orm模块是ORM框架支持模块,主要集成Hibernate,JPA、JDO用于资源管理、数据访问对象(DAO)的实现和事务策略;
4. spring-jms模块(JavaMessagingService)能够发送和接受信息,自SpringFramework4.1以后,还提供了对spring-messaging模块的支持;
5. spring-oxm模块主要提供一个抽象层以支持OXM(将java对象映射成XML数据,或者将XML数据映射成java对象);
Web:由spring-web、spring-webmvc、spring-websocket和spring-webflux4个模块组成
1. spring-web模块为spring提供了最基础Web支持,主要建立于核心容器之上,通过Servlet或者Listeners来初始化IOC容器,也包含一些与Web相关的支持;
2. spring-webmvc模块众所周知是一个的Web-Servlet模块,实现了SpringMVC(model-view-Controller)的Web应用;
3. spring-websocket模块主要是与Web前端的全双工通讯的协议(个人理解);
4. spring-webflux是一个新的非堵塞函数式ReactiveWeb框架,可以用来建立异步的,非阻塞,事件驱动的服务,扩展性非常好;
报文发送:spring-messaging模块、测试模块:Testspring-test模块
1. spring-messaging是从spring4开始新加入的一个模块,主要职责是为spring框架集成一些基础的报文传送应用
2. spring-test模块主要为测试提供支持的
【参考资料】:Spring各个jar包的详解,以及依赖
Maven搭建Spring项目
1.pom.xml添加依赖
2.在/src/main/webapp下添加文件夹WEB-INF,并在该文件夹中添加web.xml,不添加会报错
3.在/src/main/resources文件夹下创建applicationContext.xml文件,名称可以任意,然后在applicationContext.xml中添加<beans>约束
4.将对象交给spring容器管理,此处采用配置文件的形式,当然也可以采用注解的形式
Spring中的概念
以下方式都是通过配置文件的方式配置
IOC
IOC:反转控制,之前由开发人员创建、维护对象,包括依赖关系也是由开发人员注入,使用Spring之后,对象的创建以及依赖关系由spring完成
DI
DI:依赖注入,实现IOC需要DI支持
注入类型:
1. 值类型注入:8大基本数据类型
2. 引用类型注入:将依赖对象注入
注入方式:
1. set方法注入
set方法注入对象,如下有2个对象User和Car,每个对象有自己的属性,此外User对象中引用了Car对象,即要在注入User对象同时注入Car对象
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<bean name="car" class="com.oxygen.bean.Car"><!-- 容器注入Car对象 --> <property name="color" value="red"></property> <property name="name" value="TOYOTA"></property> </bean> <bean name="user" class=" com.oxygen.bean.User " scope="singleton" init-method="init" destroy-method="destroy"> <property name="name" value="zhangsan"></property> <property name="age" value="18"></property> <property name="car" ref="car"></property><!-- 依赖对象注入 --> </bean>
2. 构造函数注入
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.bean; /** * @author Oxygen * @date 2018年10月9日 */ public class User { private String name; private Integer age; private Car car; public User() { super(); } public User(String name, Integer age, Car car) { // 构造函数1 this.name = name; this.age = age; this.car = car; } public User(Integer name, Integer age, Car car) {// 构造函数2 this.name = name + ""; this.age = age; this.car = car; } public User(Integer age, String name, Car car) {// 构造函数3 this.name = name; this.age = age; this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public void init() { // user对象初始化方法,需要在bean中配置 System.out.println("init..."); } public void destroy() {// user对象销毁方法,需要在bean中配置 System.out.println("destroy..."); } @Override public String toString() { return "User [name=" + name + ", age=" + age + ", car=" + car + "]"; } }
User类提供3个带参数的构造函数,其中构造函数1和构造函数2只有name类型不同,参数列表一样,构造函数1和构造函数3类型一样,但是参数列表不一样,那么此时在
applicationContext.xml中该怎么指定这3个构造函数呢?如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<!-- 通过构造函数1注入,通过index属性来指明构造函数中参数的次序,type属性来指明参数类型 --> <bean name="user1" class=" com.oxygen.bean.User " scope="singleton" init-method="init" destroy-method="destroy"> <constructor-arg name="name" value="zhangsan" index="0" type="java.lang.String"></constructor-arg> <constructor-arg name="age" value="18" index="1"></constructor-arg> <constructor-arg name="car" ref="car" index="2"></constructor-arg> </bean> <!-- 通过构造函数3注入 --> <bean name="user2" class=" com.oxygen.bean.User " scope="singleton" init-method="init" destroy-method="destroy"> <constructor-arg name="name" value="zhangsan" index="1" ></constructor-arg> <constructor-arg name="age" value="18" index="0"></constructor-arg> <constructor-arg name="car" ref="car" index="2"></constructor-arg> </bean>
3. p名称空间注入和spel注入(了解)略
AOP★
AOP(Aspect Oriented Programming),即面向切面编程,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可
重用模块,命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模
块之间的耦合度,并有利于未来的可操作性和可维护性,如下:
AOP的功能完全集成到了Spring事务管理、日志和其他各种特性的上下文中
AOP编程的常用场景有:
Authentication权限认证、Logging日志、TransctionsManager事务、LazyLoading懒加载、
ContextProcess上下文处理、ErrorHandler错误跟踪(异常捕获机制)、Cache缓存
生成代理对象的两种方式
1. 动态代理:被代理对象必须要实现接口才能产生代理对象,如果没有接口,则不能使用动态代理技术(spring优先使用动态代理)
2. CJLIB代理:第三方代理技术,可对任何类生成代理,代理原理是:对目标对象进行继承代理,如果目标对象被final修饰,该类则无法被cjlib代理
AOP相关的名词解释
1. Joinpoit:连接点,目标对象中的方法称为连接点
2. Pointcut:切入点,目标对象中被增强的方法称为切入点
3. Advice: 通知,增强的代码部分
4. Target: 目标对象,被代理的对象
5. Weaving:织入,将通知应用到切入点的过程称之为织入,也就是增强目标方法的过程
6. Proxy: 代理,将通知织入到目标对象之后,形成代理对象
7. 切面:切入点+通知称之为切面
结合代码解释如下:
Spring中AOP的配置
AOP中的通知有5类:前置通知、后置通知(2)、环绕通知、异常通知
一.通过applicationContext.xml配置通知
1. 首先要自定义一个通知类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.advice; import org.aspectj.lang.ProceedingJoinPoint; /** * 自定义通知类 * @author Oxygen * @date 2018年10月11日 */ public class MyAdvice { //自定义前置通知 public void before() { System.out.println("这是前置通知"); } //自定义后置通知:异常出现后不执行 public void after() { System.out.println("这是后置通知1"); } // 后置通知:在异常出现时也执行 public void afterException() { System.out.println("这是后置通知2"); } //异常通知 public void Exc() { System.out.println("出现异常啦"); } //环绕通知:在切入点方法前后都执行 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("before环绕通知"); Object proceed = joinPoint.proceed(); //调用目标方法 System.out.println("after环绕通知"); return proceed; }}
2. 将通知织入目标对象
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<!-- AOP配置 --> <!-- 配置目标对象 --> <bean name="target" class="com.oxygen.proxy.Target"></bean> <!-- 配置通知对象 --> <bean name="myAdvice" class="com.oxygen.advice.MyAdvice"></bean> <!-- 将通知织入目标对象 --> <aop:config> <!-- 配置切点 --> <!-- 切点表达式expression解释: 语法结构:execution(方法修饰符 方法返回值 方法所属类 匹配方法名 (方法中的形参表) 方法申明抛出的异常 ) 支持两种通配符:*代表一个任意类型的参数;..代表零个或多个任意类型的参数 最原始表达式:execution(public void com.oxygen.proxy.Target.method1()) 上式中,public默认可以省略,void为被增强方法返回值的类型,com.oxygen.proxy.Target表示方法所属的类,method1()表示被增强的方法 通用表达式: execution(* com.oxygen.proxy.*Target.*(..)) 上式表示:对com.oxygen.proxy下,所有以Target结尾的目标类中的所有连接点方法进行增强,连接点的参数个数也不做限制 --> <aop:pointcut expression="execution(* com.oxygen.proxy.Target.*(..))" id="pc" /> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="pc"/><!-- 前置通知:before为自定义类中的方法名 --> <aop:after-returning method="after" pointcut-ref="pc"/><!-- 后置通知:发生异常不执行 --> <aop:after method="afterException" pointcut-ref="pc"/><!-- 后置通知:发生异常也执行 --> <aop:around method="around" pointcut-ref="pc"/><!-- 环绕通知 --> <aop:after-throwing method="Exc" pointcut-ref="pc"/><!-- 异常通知 --> </aop:aspect> </aop:config>
二.通过注解配置通知
1. 首先在applicationContext.xml中添加如下
<!-- 使用注解完成织入 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2. 在目标类的方法上添加相关的注解,如下
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.advice; 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.stereotype.Component; /** * 自定义通知类 * @author Oxygen * @date 2018年10月11日 */ @Aspect @Component public class MyAdvice { @Pointcut("execution(* com.oxygen.proxy.Target.*(..))") public void pc() {} //这样的话,所有切入点方法都不用再指定那么长的切点表达式了 //自定义前置通知 @Before("MyAdvice.pc()") public void before() { System.out.println("这是前置通知"); } //自定义后置通知:异常出现后不执行 @AfterReturning("MyAdvice.pc()") public void after() { System.out.println("这是后置通知1"); } // 后置通知:在异常出现时也执行 @After("MyAdvice.pc()") public void afterException() { System.out.println("这是后置通知2"); } //异常通知 @AfterThrowing("MyAdvice.pc()") public void Exc() { System.out.println("出现异常啦"); } //环绕通知:在切入点方法前后都执行 @Around("MyAdvice.pc()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("before环绕通知"); Object proceed = joinPoint.proceed(); //调用目标方法 System.out.println("after环绕通知"); return proceed; } }
ApplicationContext & BeanFactory
结论: web开发中,使用applicationContext。但是在资源匮乏的环境可以使用BeanFactory
spring创建对象的三种方式
1.通过空参构造方法创建对象
<bean name="user" class="com.oxygen.bean.User"></bean><!-- 创建方式1:通过空参构造方法创建对象 -->
2. 通过静态工厂类返回对象(了解),然后在applicationContext.xml中直接配置静态工厂类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.factory; /** * @author Oxygen * @date 2018年10月9日 */ import com.oxygen.bean.User; public class UserFactory { public static User createUser() { return new User(); } }
<bean name="user2" class="com.oxygen.factory.UserFactory" factory-method="createUser"></bean><!--创建方式2:静态工厂创建-->
3. 通过实例工厂类返回对象(了解),然后在applicationContext.xml中直接配置静态工厂类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.factory; /** * @author Oxygen * @date 2018年10月9日 */ import com.oxygen.bean.User; public class UserFactory { public User createUser() { //不是静态方法,因此在创建User对象时,首先要创建UserFactory对象,在applicationContext.xml中配置如下 return new User(); } }
<bean name="userFactory" class="com.oxygen.factory.UserFactory"></bean><!-- 首先创建UserFactory对象 --> <bean name="user3" factory-bean="userFactory" factory-method="createUser"></bean><!-- 创建User对象 -->
Bean元素详解
User对象代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.bean; /** * @author Oxygen * @date 2018年10月9日 */ public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void init() { // user对象初始化方法,需要在bean中配置 System.out.println("init..."); } public void destroy() {// user对象销毁方法,需要在bean中配置 System.out.println("destroy..."); } }
该User对象在applicationContext.xml中配置如下
<bean name="user" class=" com.oxygen.bean.User " scope="singleton" init-method="init" destroy-method= "destroy"></bean>
name属性:被管理对象的名字.可根据该名称获得对象.可重复.可使用特殊字符
id属性: 与name属性功能一样. 只不过名称不可重复.不能使用特殊字符
scope属性有4种:
1. singleton 默认值 : 被标识为单例的对象在spring容器中只会存在一个实例.
2. prototype 多例原型:标识当前对象多例对象,每次在获得才会创建.每次创建都是新的对象.整合struts2时,ActionBean必须配置为多例的.
3. request: web环境下.对象与request生命周期一致.(基本不用)
4. session: web环境下,对象与session生命周期一致. (基本不用)
5. application的作用范围表示从服务器一开始执行任务,到服务器关闭为止,会一直在服务器的内存中存在(不用)
生命周期属性:
init-method:初始化方法. spring会在对象创建之后立即调用
destroy-method:销毁方法. spring容器在关闭并销毁所有容器中的对象之前调用
【参考资料】:spring中bean的生命周期
模块化配置
<import resource="/路径**/applicationContext.xml"/><!-- 引入其他applicationContext.xml下的配置 -->
Spring注解
要想使用注解代替配置文件,需要在applicationContext.xml中配置如下:
<!-- 自动扫描包下类的注解 --> <context:component-scan base-package="com.oxygen.bean"></context:component-scan>
然后包下所有类的注解都可以被spring扫描到,可以通过@Component、@Controller、@Service、@Repository等注解注入,以Student类为例,如下
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.bean; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** * @author Oxygen * @date 2018年10月9日 */ @Component//等价于@Component("user"),等价于<bean name="student" class="com.oxygen.bean.Student"></bean> @Scope(scopeName="prototype")//多例 public class Student { @Value("tom")//该方式通过反射Field赋值,破坏了封装性,此外也可以在setName()方法上赋值,结果是一样的 private String name; @Value(value="18") //也可以是@Value("18"),注解只有一个值而且是value属性时,value属性可以省略 private Integer age; @Autowired //注解注入引用类型对象,会按照类型进行匹配,但是该注解有一个问题是,如果有多个Car类,则无法选择 //此时要配合@Qualifier("car2")使用来指定注入的类 //此外有一个更为简单的注解可以直接指定注入的类@Resource(name="car2") private Car car; @PostConstruct//在Student对象被创建后调用,相当于init-method public void init() { System.out.println("init..."); } @PreDestroy //Student对象销毁之前调用,相当于destroy-method public void destroy() { System.out.println("destroy..."); } }
Spring与Junit整合
1. 普通的单元测试如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@Test public void fun1() { // 1. 创建容器对象 // 2. 从容器中获取已经在配置文件中注入的user对象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) context.getBean("user"); print(1, user); } public static void print(Integer i, Object object) { System.out.println(String.format("%d,%s", i, object.toString())); }
2. Spring整合Junit单元测试如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@RunWith(SpringJUnit4ClassRunner.class) // 该注释会帮助我们创建容器 @ContextConfiguration("classpath:applicationContext.xml") // 读取配置文件 public class TestDemo{ @Resource(name = "user") //将user对象注入到属性u中 private User u; @Test public void fun1() { System.out.println(u); } }
Spring与JDBC整合
Spring提供了一个可以操作数据库的对象JDBCTemplateJDBC模板对象,该对象封装了JDBC技术,该对象与DBUtils中的QueryRunner相似
JDBC模板演示:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.oxygen.jdbctemplate; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.List; 6 7 import org.springframework.jdbc.core.JdbcTemplate; 8 import org.springframework.jdbc.core.RowMapper; 9 10 import com.oxygen.bean.User; 11 12 /** 13 * @author Oxygen 14 * @date 2018年10月11日 15 */ 16 public class UserDaoImpl implements UserDao { 17 18 private JdbcTemplate jdbcTemplate; 19 20 public JdbcTemplate getJdbcTemplate() { 21 return jdbcTemplate; 22 } 23 24 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 25 this.jdbcTemplate = jdbcTemplate; 26 } 27 28 @Override 29 public void save(User user) { //增 30 String sql = "insert into t_user values(null,?)"; 31 jdbcTemplate.update(sql, user.getName()); 32 } 33 34 @Override 35 public void delete(Integer id) { //删 36 String sql = "delete from t_user where id = ?"; 37 jdbcTemplate.update(sql, id); 38 } 39 40 @Override 41 public void update(User user) { //改 42 String sql = "update t_user set name = ? where id = ?"; 43 jdbcTemplate.update(sql, user.getName(), user.getId()); 44 } 45 46 @Override 47 public User getById(Integer id) { //查 48 String sql = "select * from t_user where id = ?"; 49 User user = jdbcTemplate.queryForObject(sql, new RowMapper<User>() { 50 @Override 51 public User mapRow(ResultSet rs, int arg1) throws SQLException { 52 User user = new User(); 53 user.setId(rs.getInt("id")); 54 user.setName(rs.getString("name")); 55 return user; 56 } 57 }, id); 58 return user; 59 } 60 61 @Override 62 public int getTotalCount() { // 63 String sql = "select count(*) from t_user"; 64 Integer count = jdbcTemplate.queryForObject(sql, Integer.class); 65 return count; 66 } 67 68 @Override 69 public List<User> getAll() { 70 String sql = "select * from t_user"; 71 List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() { 72 @Override 73 public User mapRow(ResultSet rs, int arg1) throws SQLException { 74 User user = new User(); 75 user.setId(rs.getInt("id")); 76 user.setName(rs.getString("name")); 77 return user; 78 } 79 }); 80 return list; 81 } 82 83 }
将Userdao交给Spring容器来管理,通过上述的代码可知UserDaoImpl依赖JdbcTemplate jdbcTemplate的,而JdbcTemplate依赖Datasource,因此在
application-Context.xm中配置如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<!-- 告知spring读取数据库配置文件 --> <context:property-placeholder location="classpath:db.properties" /><!-- classpath*:properties/*.properties --> <!-- 1. 将UserDao放入spring容器 --> <bean name="userDao" class="com.oxygen.jdbctemplate.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!-- 2. 将JDBCTemplate放入Spring容器 --> <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 3. 将连接池放入Spring容器 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="${jdbc.url}" /><!-- value从配置文件读取 --> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="driverClassName" value="${jdbc.driver}" /> <property name="maxActive" value="10" /> <property name="minIdle" value="5" /> </bean>
显然上述配置方式过于繁琐,如果UserDaoImpl extends JDBCSupport的话,只要在XML文件中注入DataSource引用即可。
Spring事务管理★
【参考资料】:事务的并发和隔离级别
Spring封装事务管理代码
因为在不同平台,操作事务的代码各不相同.spring提供了一个事务操作对象 PlatformTransactionManager 接口,针对不同的数据库有不同的实现类,如下:
- DataSourceTransactionManager(核心事务管理)
- HibernateTransitionmanager
- HibernateTransitionmanager
Spring管理事务属性介绍:1. 事务的隔离级别;2. 是否只读; 3. 事务的传播行为
事务的7种传播行为 | |
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(Spring默认) |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
Spring管理事务方式
1. 编码式配置aop事务(略)
2. 通过XML配置aop事务
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<!-- 1.事务核心管理器,封装了所有事务操作. 依赖于连接池 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource" ></property> </bean> <!-- 2. 配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager" > <tx:attributes> <!-- 以方法为单位,指定事务属性;isolation:隔离级别;propagation:传播行为;read-only:是否只读--> <tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> <tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" /> <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <!-- 3. 配置织入 --> <aop:config > <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* cn.oxygen.service.*ServiceImpl.*(..))" id="txPc"/> <!-- 配置切面 : 通知+切点;advice-ref:通知的名称;pointcut-ref:切点的名称--> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" /> </aop:config> <!-- 4.将连接池 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property> <property name="driverClass" value="${jdbc.driverClass}" ></property> <property name="user" value="${jdbc.user}" ></property> <property name="password" value="${jdbc.password}" ></property> </bean>
3. 通过注解配置aop事务
<!-- 开启使用注解管理aop事务 --> <tx:annotation-driven/>
注解配置过程如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.oxygen.jdbctemplate; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = true) public class AccountServiceImpl implements AccountService { private AccountDao ad; @Override @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = false) public void transfer(final Integer from, final Integer to, final Double money) { //减钱 ad.decreaseMoney(from, money); int i = 1 / 0; //加钱 ad.increaseMoney(to, money); } }