Spring
1.Spring介绍
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题。他将面向接口编程思想运用于整个系统应用。
1.1 Spring特点
- 方便解耦,简化开发
Spring提供IOC容器,我们可以将对象的依赖关系交由Spring进行控制管理,避免硬编码所造成的程序过度耦合。有了Spring,不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 - AOP编程支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。 - 声明式事务支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。 - 方便程序测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。 - 方便集合各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。 - 降低Java EE API的使用难度
1.2 Spring组织架构
1.3 Spring的核心模块
spring-core:依赖注入IOC与DI的最基本实现
spring-beans:Bean工厂与bean的装配
spring-context:spring的context上下文即IoC容器
spring-context-support
spring-expression:spring表达式语言
2 Spring中的IOC
IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。
DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它所依赖的属性。
2.1 实现过程
- 添加jar包
<!-- Spring的核心工具包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、 任务调度、远程访问、缓存以及多种视图层框架的支持--> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring context的扩展支持,用于MVC方面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring表达式语言 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
- 创建配置文件
<?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">
</beans>
- 在配置文件中创建对象
<bean id="对象名" class="类的完整路径">
<property name="属性名" ref="对象的id值"></property>
</bean>
- 加载配置文件获取对象
ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
//通过getBean()方法创建对象,默认都是使用无参构造方法创建对象
UserService uservice = (UserService) app.getBean("uservice");
总结:使用IOC容器对对象进行管理的好处:
- 不需要再使用new关键字来创建对象,减少代码的耦合。
- 在不改变代码的情况下,通过配置文件来更换不同的实现类对象。
2.2 bean标签的属性介绍
- class – 指定bean对应类的全路径(不能写接口的完整路径,毕竟接口不能被实例化)
- name – name是bean对象的一个标识
- scope – 执行bean对象创建的模式和生命周期,scope=“singleton” (单例) 和scope=“prototype”(多例) ;在Spring中默认采用单例模式
- id – id是bean对象的唯一标识,不能添加特别符号
- lazy-init – 是否延时加载,默认值为false。true延时加载对象,是指在对象被调用时才会加载,(通过getBean()来获取对象时,对象才会被加载);false,指不延迟,无论对象是否被使用,都会立即创建对象。
- init-method – 对象初始化方法
- destory-method – 对象销毁方法
2.3 对象创建方法
- 无参构造
通过getBean()方法创建对象,默认都是使用无参构造方法创建对象 - 有参构造
//给属性赋值时,对象类型用ref,非对象类型使用value
<bean id="u5" class="com.wjb.bean.User">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="19"/>
</bean>
<bean id="u6" class="com.wjb.bean.User">
<constructor-arg name="uname" value="王五"/>
<constructor-arg name="age" value="18"/>
</bean>
- 静态方法创建对象
//静态方法
public static Person createPerson(){
System.out.println("静态工厂模式创建对象");
return new Person();
}
<bean id="u3" class="com.wjb.bean.User" factory-method="createPerson"></bean>
- 非静态工厂方法
//非静态工厂创建对象
public Person createPerson2(){
System.out.println("非静态工厂创建对象");
return new Person();
}
<bean id="u4" class="com.wjb.bean.User"></bean>
<bean id="u5" class="com.wjb.bean.User" factory-bean="u4" factory-method="createPerson2"></bean>
2.4 SpringBean的生命周期
Bean生命周期的整个执行过程描述:
- 根据配置情况调用Bean构造方法或工厂方法实例化bean对象
- 利用依赖注入完成Bean中的所有属性值的配置注入
- Spring调用Bean的setBeanName()方法传入当前Bean的id值(使bean的id值和当前对象进行关联)
- Spring调用setBeanFactory()方法将当前bean对象传入当前工厂实例进行管理。
- Spring 调用 setApplicationContext() 方法将当前工厂传入当前 ApplicationContext 实例进行管理
- 调用BeanPostProcessor的预初始化方法,postProcessBeforeInitialzation() 对 Bean 进行加工操作(前置增强)
- 调用 afterPropertiesSet() 方法。初始化bean的时候执行,可以针对某个具体的bean进行配置。
- 调用定制的初始化方法,bean标签中的init-method()
- 调用BeanPostProcessor的后初始化方法(后置增强)
- 如果在bean标签中指定了该bean的作用范围为 scope=“singleton”,则将该bean放入 Spring IOC的缓存池中,将触发 Spring 对该bean的生命周期管理;如果在bean标签中指定了该 bean的作用范围为scope=“prototype”,则将该bean交给调用者,调用者管理该bean的生命周期,Spring 不再管理该bean。
- 如果bean实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
3. DI 依赖注入
主要有两种方法:一种是调取属性的set方法赋值;另一种使用构造方法赋值
对象类型使用ref,非对象类型使用value
3.1 set注入值
当容器给属性赋值时,默认调用set方法进行赋值
3.1.1 基本属性类型注入值
<property name="属性名" value="基本属性值" />
3.1.2 引用属性类型注入值
<property name="属性名" ref="引用对象" />
3.2 构造注入
3.2.1 可以通过name属性,按照参数名赋值
<bean name="person" class="com.wjb.bean.Person">
<constructor-arg name="name" value="zhangsan"/>
<constructor-arg name="car" ref="car"/>
</bean>
3.2.2 可以通过index属性,按照参数索引注入
<bean name="person" class="com.wjb.bean.Person">
<constructor-arg index="0" value="zhangsan"/>
<constructor-arg index="1" ref="car"/>
</bean>
3.2.3 使用type注入
3.3 spel spring 表达式
<bean name="car" class="com.wjb.bean.Car" >
<property name="name" value="迈巴赫" />
<property name="color" value="白色"/>
</bean>
<!--利用spel引入car的属性 -->
<bean name="person1" class="com.wjb.bean.Person" p:car-ref="car">
<property name="car" value="#{car.name}"/>
<property name="name" value="zhangsan"/>
</bean>
p命名空间注入值(需要添加命名空间p)
使用p:属性名 完成注入,走set方法
- 基本类型值: p:属性名=“值”
- 引用类型值: P:属性名-ref=“bean名称”
实现步骤:在配置文件中 添加命名空间p
<bean id="u1" class="com.wjb.bean.User" p:age="30" p:name="李四" p:student- ref="stu1"></bean>
3.5 复杂类型注入
<!-- 数组变量注入 -->
<property name="arrs">
<list>
<value>数组1</value>
<!--引入其他类型-->
<ref bean="car"/>
</list>
</property>
<!-- 集合变量赋值-->
<property name="list">
<list>
<value>集合1</value>
<!--集合变量内部包含集合-->
<list>
<value>集合中的集合1</value>
<value>集合中的集合2</value>
<value>集合中的集合3</value>
</list>
<ref bean="car" />
</list>
</property>
<!--set赋值-->
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
<ref bean="u1"></ref>
</set>
</property>
<!--map赋值 -->
<property name="map">
<map>
<entry key="car" value-ref="car" />
<entry key="name" value="保时捷" />
<entry key="age" value="11"/>
</map>
</property>
<!-- properties赋值 -->
<property name="properties">
<props>
<prop key="name">pro1</prop>
<prop key="age">111</prop>
</props>
</property>
3.6 自动注入(由程序自动给属性赋值)
autowire
- no 不自动装配(默认值)
- byName 属性名=id名 ,调取set方法赋值
- byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值
- constructor 构造方法的参数类型和id对象的类型相同,当没有找到时,报错。调取构造方法赋值
4.注解实现IOC
- 配置文件中添加约束
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- 配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包
<!--扫描包设置-->
<context:component-scan base-package="com.xzk.spring.bean" </context:component- scan>
- 注解
3.1 添加在类名上面
@Component(“对象名”)
@Service(“person”) // service层
@Controller(“person”) // controller层
@Repository(“person”) // dao层
@Scope(scopeName=“singleton”) //单例对象
@Scope(scopeName=“prototype”) //多例对象、
3.2 添加在属性上
@Value(“属性值”) private String name; 给属性添加默认值
@Autowired 指的是自动注入,默认使用byType(但是如果一个接口类型,有两个实现类,则报错)可以借助@Qualifier(“bean name”)
@Resource 相当于@Resource(name=“对象名”) == @Autowired + @Qualifier(“name”)
3.3 添加在方法上
@PostConstruct //等价于init-method属性
@PreDestroy //等价于destroy-method属性
5.AOP介绍
AOP(Aspect Oriented Programming)即面向切面编程。即在不改变源程序的基础上为代码添加新的功能。主要应用在权限认证、日志、事务。
AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开。
6.AOP的实现机制
- JDK 的动态代理:针对实现了接口的类产生代理。实现InvocationHandler接口
- CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术生成当前类的子类对象,实现MethodInterceptor接口
6.1 JDK动态代理实现
- 创建接口和对应的实现类
public interface UserDao {
public void test();
}
public class UserDaoImpl implements UserDao {
@Override
public void test() {
System.out.println("测试JDK动态代理");
}
}
- 创建代理类实现invocationHandler接口
public class Dai implements InvocationHandler {
private UserDao userDao;
public Dai(UserDao userDao) {
this.userDao = userDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("日志开始--");
//调用真实方法
Object invoke = method.invoke(userDao, args);
System.out.println("日志结束--");
return invoke;
}
}
- 测试
public class Test1 {
public static void main(String[] args) {
//创建真实对象
UserDao userDao = new UserDaoImpl();
Dai dai = new Dai(userDao);
//创建代理对象
UserDao o = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), dai);
//通过代理对象执行方法
o.test();
}
}
6.2 CGlib实现代理
CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑
实现步骤:
- 添加依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
- 创建普通类
public class User {
public void test(){
System.out.println("测试Cglib动态代理");
}
}
- 创建CGlib代理器
public class CgLib implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("日志开始--cglib");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("日志结束--cglib");
return o1;
}
}
- 测试
public class Test2 {
public static void main(String[] args) {
//创建真实对象
User user = new User();
//创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(user.getClass());
enhancer.setCallback(new Cglib());
User o = (User) enhancer.create();
o.test();
}
}
6.3 两种代理方式的区别:
- JDK动态代理是针对实现了接口的类产生代理, CGlib的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术生成当前类的子类对象。
- CGlib动态代理不能处理被final关键字修饰的方法。
- jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;
7.spring中使用aop
- 添加所需要的jar包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId> <version>5.0.8.RELEASE</version>
</dependency>
- 创建增强类(本质就是一个普通类)
- 前置增强:目标方法运行之前调用 aop:before
- 后置增强:(如果出现异常不会调用) 在目标方法运行之后调用 aop:after-returning
- 环绕增强:(如果出现异常依然会调用) 在目标方法之前和之后都调用 aop:around;需要使用ProceedingJoinPoint 作为参数(通过ProceedingJoinPoint 来获取切入点信息 利用 joinPoint.proceed()来调取目标方法)
- 异常增强:程序出现异常时执行(要求:程序代码中不要处理异常) aop:after-throwing
- 最终增强:(无论是否出现异常都会调用):在目标方法运行之后调用 aop:after
- 需要添加aop命名空间
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
- 设置配置文件
<!--创建增强类对象-->
<bean id="myaop" class="com.wjb.util.MyAop"></bean>
<aop:config>
<!--设置切点,将方法设置为切点-->
<aop:pointcut id="mypc" expression="execution(public void com.wjb.service.impl.UserServiceImpl.test())"/>
<aop:aspect ref="myaop">
<!--设置前置增强-->
<aop:before method="before" pointcut-ref="mypc"></aop:before>
<!--后置增强-->
<aop:after-returning method="after" pointcut-ref="mypc"></aop:after-returning>
<!--环绕增强-->
<aop:around method="around" pointcut-ref="mypc"></aop:around>
<!--异常增强-->
<aop:after-throwing method="ex" pointcut-ref="mypc"></aop:after-throwing>
<!--最终增强-->
<aop:after method="aftera" pointcut-ref="mypc"></aop:after>
</aop:aspect>
</aop:config>
8.切入点方法的定义
表达式匹配规则举例:
public * addUser(com.pb.entity.User):“*”表示匹配所有类型的返回值
public void *(com.pb.entity.User):“*”表示匹配所有方法名。
public void addUser (..):“..”表示匹配所有参数个数和类型。
* com.pb.service.*.*(..):匹配com.pb.service 包下所有类的所有方法。
* com.pb.service..*(..):匹配com.pb.service 包及子包下所有类的所有方法
9.如何获取切入点信息
通过JoinPoint对象获取信息
- 切入点对象:joinPoint.getTarget().getClass().getSimpleName()
- 切入点方法:joinPoint.getSignature()
- 切入点参数:joinPoint.getArgs()[0]
10.特殊的前置增强–>Advisor前置增强实现步骤
- 创建增强类,要求该类实现MethodBeforeAdvice接口
- 修改applicationContext.xml文件
(1)创建增强类对象
(2)定义增强和切入点的关系
<bean id="beforeAop" class="com.wjb.util.BeforeAop"></bean>
<aop:config>
<!--设置切点,将方法设置为切点-->
<aop:pointcut id="mypc" expression="execution(public void com.wjb.service..*(..))"/>
<aop:advisor advice-ref="beforeAop" pointcut-ref="mypc"> </aop:advisor>
</aop:config>
11.使用了AspectJ依赖注解开发
- 添加AspectJ和Aop所需要的jar包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId> <version>5.0.8.RELEASE</version>
</dependency>
- 需要在配置文件中添加spring注解扫描包以及启动AspectJ的注解方式
<!--Spring:注解扫描包-->
<context:component-scan base-package="com.wjb"></context:component-scan>
<!--启动aspectJ的注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 创建增强类(增强类也需要实例化,所以需要@Component来修饰,通过@Aspect来标注这是一个增强类)
- 定义切点方法,使用@Pointcut 指明切点方法
5.@Before(@Pointcut修饰的方法名称) 表示前置增强
@AfterReturning(@Pointcut修饰的方法名称) 表示后置增强
@Around(@Pointcut修饰的方法名称) 表示环绕增强
@AfterThrowing(@Pointcut修饰的方法名称) 表示异常增强
@After(@Pointcut修饰的方法名称) 表示最终增强
@Component //创建对象
@Aspect //指明是增强类
public class MyAop {
//指明切点
@Pointcut("execution(* com.wjb.service.*.*(..))")
public void abc(){}
@Before("abc()")
public void before(){
System.out.println("前置增强");
}
@AfterReturning("abc()")
public void aftera(){
System.out.println("后置增强");
}
@Around("abc()")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("环绕开始");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕结束");
}
@AfterThrowing("abc()")
public void myex(){
System.out.println("异常增强");
}
@After("abc()")
public void after(){
System.out.println("最终增强");
}
}
11.1 通过注解实现的增强顺序
分为两种情况:
- 有异常的情况:环绕开始-前置增强-(代码出现异常)-环绕结束-最终增强-异常增强
- 没有异常的情况:环绕开始-前置增强-环绕结束-最终增强-后置增强
12.Spring-JDBC 数据访问
12.1 使用spring-jdbc操作数据库
Spring中操作数据库主要是通过jdbcTemplate对象来实现
实例
添加相应的jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
</dependencies>
public class Test {
public static void main(String[] args) throws Exception {
//创建c3p0连接池
ComboPooledDataSource db = new ComboPooledDataSource();
db.setDriverClass("com.mysql.jdbc.Driver");
db.setJdbcUrl("jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8");
db.setUser("root");
db.setPassword("");
//创建JdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(db);
int update = jdbcTemplate.update("insert into student (id,stuname,sex) values (null,'王五','男')");
System.out.println("添加成功! i="+update);
}
}
12.2. Spring管理JdbcTemplate
让实现类继承JdbcDaoSupport类,通过getJdbcTemplate()方法得到JdbcTemplate对象,需要通过配置文件往实现类注入数据源。
//通过properties文件来获取连接数据库的信息
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="db" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driver}"></property>
<property name="jdbcUrl" value="${url}"></property>
<property name="user" value="${uname}"></property>
<property name="password" value="${password}"></property>
</bean>
//将数据源作为属性添加在实现类中
<bean id="studentDao" class="com.wjb.dao.impl.StudentDaoImpl">
<property name="dataSource" ref="db"></property>
</bean>
public class StudentDaoImpl extends JdbcDaoSupport implements StudentDao {
@Override
public int insertStudent(Student student) {
String sql = "insert into student (id,stuname,sex) value (?,?,?)";
int update = getJdbcTemplate().update(sql, student.getId(),student.getStuName(), student.getSex());
return update;
}
}
12.3 crud
JdbcTemplate常用方法:
JdbcTemplate.update:主要用于增删改
jdbcTemplate.queryForObject:用于查询单行数据,参数类型有很多需要根据实际需求
jdbcTemplate.query:用于查询全部数据
查询得到的数据被RowWapper存放在Result中,得到Result解析成实体类对象即可
13.Spring事务管理
13.1什么是事务?
通过sql将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性(准确性)。
事务通常是以begin transaction开始,以commit或rollback结束。
事务执行的流程:开启事务->执行insert,update,delete->commit/rollback
13.2 事务的特性ACID
- 原子性(atomicity):事务是数据库的逻辑工作单元,而且必须是原子工作单位,对于其数据修改,要么全部执行,要么全部都不执行。
- 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
- 隔离性(isolation):一个事务的执行不能被其他事务所影响。
- 持久性(durability):一个事务一旦提交,事务的操作便永久性的保存在数据库中。即使此时再执行回滚操作也不能撤消所做的更改
13.3 事务的嵌套->传播行为propagation
事务的传播机制
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
规定了事务方法和事务方法发生嵌套调用时事务如何进行传播
Spring定义了七种传播行为:
事务传播行为失效的情况
spring事务是基于代理来实现的,所以:
(1)private、final、static 方法无法被代理,所以添加事务无效
(2)当绕过代理对象, 直接调用添加事务管理的方法时, 事务管理将无法生效。比如直接new出的对
象。
(3)在同一个类下,有2个方法,A、B,A没有事务,B有事务,但是A调用B时,方法B被标记的事务无效。究其原因,因为此类的调用对象为代理对象,代理方法A调用真正的被代理方法A后,在被代理方法A中才会去调用方法B,此时this对象为被代理的对象,所以是不会通知到代理对象,也就变成了第二种情况,绕过了代理对象。所以无效。
13.4 事务的隔离级别
MySQL数据库共定义了四种隔离级别:
- Serializable(串行化):可避免脏读、不可重复读,幻读情况的发生。
- Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
- Read committed(读已提交):可避免脏读情况发生。
- Read uncommitted(读未提交):最低级别,可能出现脏读、不可重复读、幻读的情况。
级别越高,数据越安全,但性能越低。
13.5 事务的实现
Spring XML配置声明事务
13.5.1. TransactionManager
在不同平台,操作事务的代码各不相同,因此spring提供了一个 TransactionManager 接口:
- DateSourceTransactionManager 用于 JDBC 的事务管理
- HibernateTransactionManager 用于 Hibernate 的事务管理
13.5.2 Spring事务管理实现
·1. 添加所需要的命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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
- 添加事务相关配置
<!--声明式事务-->
<!--创建事务管理器对象-->
<bean id="mytx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="db"></property>
</bean>
<!--指定哪些方法需要进行事务管理-->
<tx:advice id="ad" transaction-manager="mytx">
<tx:attributes>
<!--method:需要事务管理的方法(增删改) propagation:传播机制-->
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--利用aop思想指定哪层需要事务管理,事务添加在service层最合理-->
<aop:config>
<aop:pointcut id="mypc" expression="execution(* com.wjb.dao..*(..))"/>
<aop:advisor advice-ref="ad" pointcut-ref="mypc"></aop:advisor>
</aop:config>
14. 使用注解方式添加事务
14.1 使用注解方式实现事务添加
- 添加tx命名空间,在配置文件中创建事务管理器对象
<!--声明式事务-->
<!--创建事务管理器对象-->
<bean id="mytx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="db"></property>
</bean>
- 启动事务注解
<!--启动事务注解-->
<tx:annotation-driven transaction-manager="mytx"></tx:annotation-driven>
//在默认情况, <tx:annotation-driven /> 中transaction-manager属性会自动使用名为 "transactionManager" 的事务管理器.
- 使用@Transactional对需要进行事务管理的类或方法进行标注,(如果标注类,那么类中的所有方法,都进行事务管理)
@Transactional
public int insertStudent(Student student) {
String sql = "insert into student (id,stuname,sex) value (?,?,?)";
int update = getJdbcTemplate().update(sql, student.getId(),student.getStuName(), student.getSex());
//添加异常操作
// System.out.println(7/0);
return update;
}
14.2 在何处标注@Transactional注解?
@Transactional注解可以直接用于接口定义和接口方法,类定义和类的public方法上,但Spring建议在业务实现类上使用@Transactional注解,当然也可以添加到业务接口上,但是这样会留下一些容易被忽视的隐患,因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务类实现继承.
总结:@Transactional注解使用在实现类上或实现类的方法上最可