文章目录
- Spring框架
- 服务器端框架简介
- Spring概述
- Spring核心
- Spring工厂类概述
- Spring的详细配置
- Spring的分模块开发的配置
- CRM的综合案例
- Spring的注解开发
- Junit集成main方法 注解方式除去重复代码
- Spring的AOP的XML开发
- Spring的AOP的注解开发
- Spring的JDBC的模板的使用
- Spring的事务管理
- 事务
- Spring的事务管理的API
- Spring事务中的传播行为
- PROPAGATION_REQUIRED:默认值,如果A中有事务,使用A中的事务。如果A没有,创建一个新的事务,将操作包含进来。
- PROPAGATION_SUPPORTS:支持事务,如果A中有事务,使用A中的事务。如果A没有,就不使用事务。
- PROPAGATION_MANDATORY:如果A中有事务,使用A中的事务 。如果A没有,抛出异常
- PROPAGATION_REQUIRES_NEW:默认值,如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身的操作。
- PROPAGATION_NOT_SUPPORTED:不支持事务,如果A中有事务,将A的事务挂起。不使用事务管理。
- PROPAGATION_NEVER:如果A中有事务,抛出异常。
- PROPAGATION_NESTED:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,就执行完成。如果有异常,可以回滚到最初始的位置,也可以回滚到保存点。
- 搭建Spring的事务管理的环境
- Spring的事务管理
Spring框架
服务器端框架简介
经典三层框架有这些
-
web层
- Structs
- Spring MVC
-
service层(业务层)
- Spring
-
dao层(持久层)
- Hibernate
- Mybatis
- jdbcTemplate–>Spring data
Spring概述
Spring是service层框架,很重要,讲多少用多少
- 是开源框架
- 轻量级框架
- 这是和EJB(JavaBean)对比的
- 依赖资源少+消耗资源少=轻量级框架
- 是一站式框架
- Web层:SpringMVC
- Service层:Spring的bean管理,Spring声明式事务
- Dao层(Data Access Object):Spring的jdbc模板,Spring的ORM模块
Spring优点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jn5yNirD-1589804546842)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569230492056.png)]
Spring的体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UpM6RXIg-1589804546845)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569230339444.png)]
- Core Container(1.核心容器)
- Beans(1.1 管理bean)
- Core(1.2 核心)
- Context 上下文(1.3 配置文件)
- Expression Language (1.4 SpringEL表达式)
- AOP(2.1 切面编程)
- Aspects(2.2 AOP框架)
- Data Access/Integration(3)
- JDBC (3.1 jdbc template 数据库开发)
- ORM (3.2 整合Hibernate)
- Transactions (3.3 事务管理)
- Web(4 MVC/Remoting)
- Web(4.1 web开发)
- Struts(4.2 整合Struts)
- Test(整合Junit)
Spring核心
控制反转IoC
需要弄一下 log4j.properties的配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lnCAlCnH-1589804546847)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569330959479.png)]
- 导入Jar包4+1(4个核心beans,core,context,expression + 1个依赖 commons-logging)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Cokmdbk-1589804546849)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569232616073.png)]
- xxx.jar(需要的)
- xxx-javadoc.jar(帮助文档)
- xxx-sources.jar(源码)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tQRKlxf-1589804546852)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569232929356.png)]
- 目标类
-
提供UserService接口和实现类
-
获得UserService实现类的实例
-
定义接口
package com.itheima.a_ioc;
public interface UserService {
public void addUser();
}
- 定义实现类
package com.itheima.a_ioc;
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println(a_ico add user);
}
}
- 配置文件
XML需要点选design来看代码,别用Open with Text Editer,那样就没有高亮了。
- 位置:任意,开发中一般在classpath下(src)
- 名称:任意,开发中常用
applicationContext.xml
- 内容:添加
schema约束
- 配置service
<?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 definitions here -->
<!-- 配置service
<bean>配置需要创建的对象
id:用于之后从Spring绒球获得实例时使用的
class:需要创建实例的全限定类名
-->
<bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl"></bean>
</beans>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wUqDTbKw-1589804546854)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569238062721.png)]
- 测试
依赖注入(属性设置)DI
属性注入的前四种方式
构造方法的 普通类型 属性注入用
<constructor-arg name="属性名" value="属性值">
构造方法的 对象类型 属性注入用
<constructor-arg name="属性名" value="属性值" ref="引用的对象">
set方法的 普通类型 属性注入用
<property name="属性名" value="属性值">
set方法的 对象类型 属性注入用
<constructor-arg name="属性名" ref="引用的对象">
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7BJwHLkD-1589804546856)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569389775684.png)]
前提是必须有IOC的环境
-
is a :是一个,继承
-
has a :有一个,成员变量,依赖
class B{ private A a; //B类依赖A类 }
-
依赖:一个对象需要使用另一个对象
-
注入:通过setter方法进行另一个对象实例设置
class B{
private A a;// B类依赖A类
}
这两个图非常重要,方便理解IoC和DI,解耦与耦合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbfjgGni-1589804546857)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569246052188.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5QNEmM3-1589804546860)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569246503730.png)]
-
目标类
-
配置文件
-
测试
属性注入的P名称空间的方式(了解)
要使用p名称空间,需要修改头部的xmlns
beans
变p
,然后前缀改成xmlns:
不用p空间就要把
xmlns:
那一行删去,不然会有问题
xmlns="http://www.springframework.org/schema/beans"
<!--要使用p名称空间,需要修改头部的xmlns `beans`变`p`,然后前缀改成`xmlns:`-->
xmlns:="http://www.springframework.org/schema/p"
- p名称空间的 普通属性 的注入
<bean id="car2" class="com.binyu.spring.demo4.Car2" p:name="奇瑞qq" p:price="30000"></bean>
- p名称空间的 对象属性 的注入
<bean id="employee" class="com.binyu.spring.demo4.Employee" p:name="王东" p:car2-ref="car2"></bean>
属性注入的SpEL的方式
#{SpEL}
使用前,要确认xmlns:
已经清除
<!-- SpEL的属性注入 -->
<bean id="car2" class="com.binyu.spring.demo4.Car2">
<property name="name" value="#{'三蹦子'}"></property>
<property name="price" value="#{3000}"></property>
</bean>
<bean id="employee" class="com.binyu.spring.demo4.Employee">
<!-- 直接注入属性 -->
<property name="name" value="#{'赵洪'}"></property>
<!-- 直接注入对象 -->
<property name="car2" value="#{car2}"></property>
</bean>
<!-- SpEL的属性注入 -->
<bean id="carInfo" class="com.binyu.spring.demo4.CarInfo"></bean>
<bean id="car2" class="com.binyu.spring.demo4.Car2">
<!-- 直接注入对象的属性 -->
<property name="name" value="#{carInfo.name}"></property>
<!-- 直接注入对象的方法 -->
<property name="price" value="#{carInfo.calculatorPrice()}"></property>
</bean>
复杂数据类型的SpEL注入
- 目标类
package com.binyu.spring.demo5;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 集合属性的注入
* @author Dong Binyu
*
*/
public class CollectionBean {
private String[] arrs;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
public void setArrs(String[] arrs) {
this.arrs=arrs;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String,String> map) {
this.map = map;
}
@Override
public String toString() {
return "CollectionBean [arrs=" + Arrays.toString(arrs) + ", list=" + list + ", set=" + set + ", map=" + map
+ "]";
}
}
- 配置文件
<bean id="collection"
class="com.binyu.spring.demo5.CollectionBean">
<property name="arrs">
<!-- 注入数组类型和List配置一样 -->
<list>
<!-- 普通类型 数组 -->
<value>董滨雨</value>
<value>李铭霞</value>
<value>在一起</value>
<value>真好看</value>
</list>
</property>
<property name="list">
<!-- 注入数组类型和List配置一样 -->
<list>
<!-- List集合 -->
<value>大学里</value>
<value>谈恋爱</value>
<value>在一起</value>
<value>真好看</value>
</list>
</property>
<property name="set">
<!-- 注入数组类型和List配置一样 -->
<set>
<!-- Set集合 -->
<value>set1</value>
<value>set2</value>
<value>set3</value>
<value>set4</value>
</set>
</property>
<property name="map">
<!-- Map集合 -->
<map>
<entry key="aaa" value="111"></entry>
<entry key="bbb" value="222"></entry>
<entry key="ccc" value="333"></entry>
</map>
</property>
</bean>
- 测试类
package com.binyu.spring.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);
}
}
Spring工厂类概述
Spring的工厂类的结构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBRNZuKl-1589804546861)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml11952\wps1.jpg)]
BeanFactory:老版本的工厂类
- 在调用getBean的时候,才回生成类的实例
ApplicationContext:新版本的工厂类
- 加载配置文件的时候,就会将Spring管理的类实例化
- 功能更强大
- 有两个实现类
- ClassPathXmlApplicationContext:加载类路径(src)下的配置文件
- FileSystemXmlApplicationContext:加载文件系统(磁盘)下的配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnCjY3xw-1589804546863)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569372714504.png)]
Spring的详细配置
XML的schema提示配置
一个xml能被一个DTD约束,但是可以被多个schema约束的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A2dN1eVv-1589804546865)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569311744329.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMp1fJrQ-1589804546866)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569312103651.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v26LIbHs-1589804546867)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569312239368.png)]
Bean(可重用组件)的配置
Bean标签id和name的配置
id
:使用了约束中的唯一约束。里面不能出现特殊字符name
:没有使用唯一约束(理论上可以重复,但是实际开发中不允许)。里面可以出现特殊字符- Spring和Stucts1整合的时候,才会遇到
class
:实例的类的全路径
Bean的生命周期配置【了解】
init-method
:Bean被初始化执行的方法destroy-method
:Bean被销毁时执行的方法(Bean是单例创建,工厂关闭)
Bean的作用范围的配置【重点】
-
scope
:Bean的作用范围singleton
:默认的,spring会采用单例模式创建这个对象(当容器创建时活着,当容器销毁时死亡)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Te4vqzvj-1589804546870)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569389214451.png)]
prototype
:多例模式【Structs2和Spring整合的时候要用到】(当使用对象时活着,当对象长时间不用才会回收)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25T8flH2-1589804546872)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569389126142.png)]
request
:应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中session
:应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中globalsession
:应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相当于session
单例模式:不管有多少次程序调用,只会new一次。每次用的只是一个实例
多例模式:用一次就new一次
porlet环境:子域名免登录,比如百度中的百度云,百度百科
Spring的分模块开发的配置
加载多个配置文件方式1
配置两个xml
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
加载多个配置文件方式2
在一个xml中引用另一个xml
<import resource="applicationContext2.xml"/>
加载多个配置文件方式3
@Import("ApplicationContext2.xml")
@Import(ApplicationContext2.class)
CRM的综合案例
用了struts2框架,没太懂!!
JSP 它使用JSP标签在HTML网页中插入Java代码
-
搭建开发环境
-
创建数据库和表
-
创建一个web项目,引入jar包
-
引入struts2开发的jar包
-
引入struts2的配置文件
- web.xml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjcBD69p-1589804546873)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569587482493.png)]
- struts.xml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5BKW9n95-1589804546875)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569587536420.png)]
-
-
引入页面
-
编写action
-
配置action
-
修改页面提交到action
-
编写action的save方法
-
引入spring的环境
- 引入jar包
- 引入配置文件
-
将Service交给Spring
-
在Action中调用Service
-
编写DAO并完成配置
-
在Service中使用DAO
-
Spring的注解开发
注解开发:不需要配置文件
-
创建web项目,引入6+1(aop的jar包)
-
创建
applicationContext.xml
,并引入context约束(之前引入的是bean约束) -
实现接口和实现类
-
开启Spring的组件扫描
<!-- 使用IOC注解开发,配置组件扫描 -->
<context:component-scan base-package="com.binyu.demo1"/>
- 在类上添加注解
- 在属性上添加注解
@Component("userDao")// 相当于<bean id="userDAO" class="com....">
public class UserDAOImpl implements UserDAO {
@Value("注解牛逼!")
String name;
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserDAO"+name);
}
}
@Component
:组件
其实就是控制反转,将类交给Spring管理
- 修饰一个类,将这个类交给Spring管理
- 这个注解有三个衍生注解(功能类似),修饰类
@Controller
:web层@Service
:service层@Repository
:DAO层
属性注入的注解
其实就是依赖注入
- 普通属性
@Value
:设置普通属性的值- 如果有set方法,写在set方法上
- 如果没有set方法,写在属性上
- 对象类型属性
@Autowired
:设置对象类型的属性的值,- 但是按照类型进行注入的,所以不一定要名称能对的上
- 如果要让名称对的上,
@Qualifier
和@Autowired
一起使用
@Resource
:完成对象类型的属性注入,按照名称完成属性注入,@Resource(name="xxx")
Bean的其他的注解
-
生命周期相关的注解
这两个注解目前使用不了,原因不明
@PostConstruct
:初始化方法@PreDestroy
:销毁方法
-
Bean作用范围的注解
@Scope
:作用范围singleton
:默认单例prototype
:多例request
session
globalsession
IOC和XML注解开发的比较
- XML配置
- 任意场景都能使用
- 结构清晰,维护方便
- 注解配置
- 有些场景不能使用,比如这个类不是自己提供的。
- 开发速度快,轻松又简单~
- 注解牛逼!
XML和注解整合开发
- 使用XML来管理Bean
- 使用注解完成属性注入
去掉配置文件的几个注解(了解)
@Configuration
:指定当前类是一个配置类
@ComponentScan(base-package="")
:通过注解指定spring在创建容器时要扫描的包,等同于<context:component-scan base-package="com.itheima"></context:component-scan>
@Bean
:用于把当前方法的返回值作为bean对象存入spring的容器中。name和value是它的两个属性,value不指定时默认是方法名。
@PropertiesSource(classpath:jdbcConfig.properties)
:用于加载配置文件
Junit集成main方法 注解方式除去重复代码
导入spring-test 的jar包
@Runwith(SpringJunit4ClassRunner.class)
@ContextConfiguration(classpath:applicationcontext.xml)
Spring的AOP的XML开发
AOP:面向切面编程,是OOP开发的拓展和延伸,解决OOP开发遇到的问题
底层实现是用的 JDK动态代理和Cglib动态代理的自动切换
方便了 方法增强和拓展
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuN5ga6C-1589804546877)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569742736079.png)]
AOP可以用来:日志记录、审计、声明式事务、安全性和缓存等
动态代理的部分我又没听懂~
Spring的AOP的开发(AspectJ的XML的方式)
AspectJ是一个AOP的框架,Spring后来的版本引入AspectJ作为AOP的开发
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIkt8HTC-1589804546880)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1569744167440.png)]
- 引入jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysQXj8Yb-1589804546881)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570418961080.png)]
- 引入配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flWwKu4U-1589804546883)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570419442987.png)]
- 编写目标类并完成配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-adCQI4pi-1589804546885)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570420173194.png)]
Spring整合单元测试的jar包是
spring-test-4.2.4.RELEASE.jar
- 编写测试类
package com.binyu.demo1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
@Autowired
@Qualifier("productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
}
@Test
public void demo2() {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
ProductDao productDao=(ProductDao) applicationContext.getBean("productDao");
productDao.save();
productDao.update();
productDao.find();
productDao.delete();
}
}
- 编写切面类
package com.binyu.demo1;
/**
* 切面类
* @author Dong Binyu
*
*/
public class MyAspectXML {
public void checkPri() {
System.out.println("这是权限校验方法。。。");
}
}
- 将切面类交给Spring管理
<bean id="myAspectXML" class="com.binyu.demo1.MyAspectXML"></bean>
- 通过AOP的配置完成对目标类产生代理
<aop:config>
<!-- 表达式配置哪些类的那些方法需要增强 -->
<aop:pointcut
expression="execution(* com.binyu.demo1.ProductDaoImpl.save(..))"
id="pointcut1" />
<!-- 配置切面 -->
<aop:aspect ref="myAspectXML">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
Spring中的通知类型
- 前置通知:在目标方法执行之前进行操作
- 后置通知:在目标方法执行之后进行操作
- 环绕通知:在目标方法执行之前和之后进行操作
- 异常抛出通知:出现异常时候,进行的操作
- 最终通知:无论代码是否有异常,都进行操作
- 引介通知(不用)
Spring中execution切入点表达式
的写法
Spring的AOP的注解开发
- 引入jar包(基本的6个+AOP4个+整合JUit单元测试1个)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCggt8V1-1589804546886)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570426284560.png)]
- 引入配置文件
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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
-
编写目标类并配置
-
编写切面类并配置
-
使用注解的AOP对象目标类进行增强
- 在配置文件中打开注解的AOP开发
- 在切面类上使用注解
<!-- 在配置文件中开启注解的AOP的开发 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.binyu.demo1;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 切面类:注解的切面类
*
* @author Dong Binyu
*
*/
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.binyu.demo1.OrderDao.save(..))")
public void before() {
System.out.println("前置增强-------");
}
}
- 编写测试类
package com.binyu.demo1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
@Autowired
@Qualifier(value = "orderDao")
private OrderDao orderDao;
@Test
public void demo1() {
orderDao.save();
orderDao.update();
orderDao.find();
orderDao.delete();
}
}
Spring的AOP中的通知类型
@Before
前置通知:在目标方法执行之前进行操作
@Before(value="execution(* com.binyu.demo1.OrderDao.save(..))")
public void before() {
System.out.println("前置增强-------");
}
@AfterReturning
后置通知:在目标方法执行之后进行操作
@AfterReturning(value="execution(* com.binyu.demo1.OrderDao.delete(..))",returning = "result")
// 用returning拿到切面目标类方法的返回值
public void afterReturning(Object result) {
System.out.println("后置增强----------"+result);
}
@Around
环绕通知:在目标方法执行之前和之后进行操作
// 环绕通知
@Around(value="execution(* com.binyu.demo1.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知-------");
Object obj=joinPoint.proceed();
System.out.println("环绕后通知-------");
return obj;
}
@AfterThrowing
异常抛出通知:出现异常时候,进行的操作
// 异常抛出通知
@AfterThrowing(value="execution(* com.binyu.demo1.OrderDao.find(..))",throwing = "e")
public void afterThrowing(Throwable e) {
System.out.println("异常抛出通知。。。"+e.getMessage());
}
注意:如果find方法没有异常,则不会调用该切面类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUoZ7hkn-1589804546888)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570428840300.png)]
@After
最终通知:无论代码是否有异常,都进行操作
// 最终通知
@After(value="execution(* com.binyu.demo1.OrderDao.find(..))")
public void after() {
System.out.println("这是最终通知----");
}
Spring的AOP注解的切入点的配置@Pointcut
package com.binyu.demo1;
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;
/**
* 切面类:注解的切面类
*
* @author Dong Binyu
*
*/
@Aspect
public class MyAspectAnno {
// 前置通知
// @Before(value="execution(* com.binyu.demo1.OrderDao.save(..))")
@Before(value="MyAspectAnno.pointcut2()")
public void before() {
System.out.println("前置增强-------");
}
@AfterReturning(value="MyAspectAnno.pointcut4()",returning = "result")
// 用returning拿到切面目标类方法的返回值
public void afterReturning(Object result) {
System.out.println("后置增强----------"+result);
}
// 切入点注解
@Pointcut(value="execution(* com.binyu.demo1.OrderDao.find(..))")
private void pointcut1() {};
@Pointcut(value="execution(* com.binyu.demo1.OrderDao.save(..))")
private void pointcut2() {};
@Pointcut(value="execution(* com.binyu.demo1.OrderDao.update(..))")
private void pointcut3() {};
@Pointcut(value="execution(* com.binyu.demo1.OrderDao.delete(..))")
private void pointcut4() {};
}
如果目标类实现了接口,Spring底层使用JDK动态代理
如果目标类没有实现接口,Spring底层使用cglib动态代理
Spring的JDBC的模板的使用
Spring是EE开发的一站式框架,有EE开发的每层的解决方案,Spring对持久层也提供了解决方:ORM模块和JDBC模板
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83KerrCH-1589804546889)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570432758263.png)]
JDBC模板使用的入门
- 创建项目,引入jar包(基本6个包+数据库驱动1个包+Spring的JDBC模板和它依赖的2个包+单元测试整合1个包)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hj819ilk-1589804546891)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570433298075.png)]
- 创建数据库和表
create database spring_day03;
use spring_day03;
create table account(
id int primary key auto_increment,
name varchar(20),
money double);
- 使用Spring的JDBC模板来保存数据
package com.binyu.jdbc.demo1;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class JdbcDemo1 {
@Test
public void demo1() {
// jdbc模板的使用类似于Dbutils
// 创建连接池
DriverManagerDataSource dataSource=new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_day03");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 创建jdbc模板
JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
jdbcTemplate.update("insert into account values(null,?,?)","陈冠希",100000d);
}
}
- 在数据库中查看更新的数据
mysql> select * from account;
+----+-----------+--------+
| id | name | money |
+----+-----------+--------+
| 1 | 陈冠希 | 100000 |
+----+-----------+--------+
1 row in set (0.00 sec)
将连接池和模板交给Spring管理
- 引入Spring的配置文件
- 配置文件中配置连接池和jdbc模板
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Spring内置的连接池============== -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 属性注入 -->
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 配置Spring的JDBC的模板=============== -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
- 编写测试类
package com.binyu.jdbc.demo1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcDemo2 {
@Autowired
@Qualifier(value="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void demo2() {
jdbcTemplate.update("insert into account values(null,?,?)","王宝强",10000d);
}
}
因为是Spring4的缘故,此时会报错,需要引入aop的jar包!!!
Spring3不报错。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S89eOZAo-1589804546894)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570435837305.png)]
- 查看数据库
运行了三次,所以出现了这种情况
mysql> select * from account;
+----+-----------+--------+
| id | name | money |
+----+-----------+--------+
| 1 | 陈冠希 | 100000 |
| 2 | 王宝强 | 10000 |
| 3 | 王宝强 | 10000 |
| 4 | 王宝强 | 10000 |
+----+-----------+--------+
4 rows in set (0.00 sec)
使用开源数据库连接池
DBCP连接池的属性和Spring内置连接池属性相同
C3P0连接池的属性和前面两个连接池不同
DBCP的使用
- 引入两个jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z1pvxI3N-1589804546895)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570436597937.png)]
- 配置DBCP连接池
<!-- 配置DBCP连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 属性注入 和Spring内置的连接池属性一样 -->
<property name="driverClassName"
value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring_day03"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
-
编写测试类
略
C3P0的使用
- 引入一个jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nm6JQK3e-1589804546896)(C:\Users\dby\AppData\Roaming\Typora\typora-user-images\1570438347016.png)]
- 配置C3P0连接池
<!-- 配置C3P0连接池=================== -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 注意两个连接池的属性不一样 -->
<property name="driverClass"
value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_day03"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
- 编写测试类
- 查看数据库
mysql> select * from account;
+----+-----------+--------+
| id | name | money |
+----+-----------+--------+
| 1 | 陈冠希 | 100000 |
| 2 | 王宝强 | 10000 |
| 3 | 王宝强 | 10000 |
| 4 | 王宝强 | 10000 |
| 5 | 李如花 | 10000 |
| 6 | 邓紫棋 | 10000 |
+----+-----------+--------+
6 rows in set (0.01 sec)
抽取配置到属性文件
- 定义一个属性文件
jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_day03
jdbc.username=root
jdbc.password=root
- 在Spring的配置文件中引入属性文件
<!-- 引入属性文件==================== -->
<!-- 第一种:通过bean标签引入(很少用,很复杂) -->
<!-- 第二种:常用 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass"
value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
- 测试类
- 查看数据库
使用JDBC模板完成增删改查操作
-
添加操作
上面已经做了
-
修改操作
// 修改操作
@Test
public void demo2() {
jdbcTemplate.update("update account set name=?,money=? where id=6","董滨雨",2000.2d);
}
- 删除操作
// 删除操作
@Test
public void demo3() {
jdbcTemplate.update("delete from account where id=?",6);
}
mysql> select * from account;
+----+-----------+--------+
| id | name | money |
+----+-----------+--------+
| 1 | 陈冠希 | 100000 |
| 2 | 王宝强 | 10000 |
| 3 | 王宝强 | 10000 |
| 4 | 王宝强 | 10000 |
| 5 | 李如花 | 10000 |
| 7 | 邓紫棋 | 10000 |
+----+-----------+--------+
6 rows in set (0.00 sec)
- 查询操作
// 查询操作
@Test
public void demo4() {
String name=jdbcTemplate.queryForObject("select name from account where id=?", String.class,5);
System.out.println(name);
}
@Test
public void demo5() {
Long count=jdbcTemplate.queryForObject("select count(*) from account ", Long.class);
System.out.println(count);
}
- 查询结果保存到对象
- 定义一个Account类
- 将查询到的结果封装到对象中
package com.binyu.domain;
public class Account {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
}
}
// 查询然后封装到一个Account对象中
@Test
public void demo6() {
Account account=jdbcTemplate.queryForObject("select * from account where id=?",new MyRowMapper(),5);
System.out.println(account);
}
class MyRowMapper implements RowMapper<Account>{
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account=new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
return account;
}
}
- 查询多条记录,返回List
// 查询然后返回一个Account集合
@Test
public void demo7() {
List<Account> list = jdbcTemplate.query("select * from account", new MyRowMapper());
for (Account account : list) {
System.out.println(account);
}
}
控制台的输出:
Account [id=1, name=陈冠希, money=100000.0]
Account [id=2, name=王宝强, money=10000.0]
Account [id=3, name=王宝强, money=10000.0]
Account [id=4, name=王宝强, money=10000.0]
Account [id=5, name=李如花, money=10000.0]
Account [id=7, name=邓紫棋, money=10000.0]
Spring的事务管理
事务
什么是事务?
逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败
事务的特性
- 原子性:事务不可分割
- 一致性:事务执行前后数据完整行保持一致
- 隔离性:一个事务的执行不应该受到其他事务的干扰
- 持久性:一旦事务结束,数据就会持久化到数据库
如果不考虑隔离性引发安全性问题
- 读问题
- 脏读:一个事务读到另一个事务未提交的数据
- 不可重复读:一个事务读到另一个事务已经提交的update的数据,导致一个书屋中多次查询结果不一致
- 虚读,幻读:一个事务读到另一个事务已经提高的insert的数据,导致一个事务中多次查询结果不一致
- 写问题
- 丢失更新
解决读问题
- 设置事务的隔离级别
Read uncommitted
:未提交读,任何读问题解决不了Read committed
:已提交读,解决脏读,但是不可重复读和虚读有可能发生。(Oracle使用)Repeatable read
:重复读,解决脏读和不可重复度,但是虚读有可能发生。(MySql使用)Serializable
:解决所有读问题,但是不允许事务并发。安全但是效率很低。
Spring的事务管理的API
-
PlatformTransactionManager
平台事务管理器- 平台事务管理器:接口,是Spring用于管理事务的真正的对象
- DataSourceTransactionManager:底层使用JDBC管理事务
- HibernateTransactionManager:底层使用Hibernate管理事务
- 平台事务管理器:接口,是Spring用于管理事务的真正的对象
-
TransactionDefinition
事务定义信息- 事务定义:用于定义事务的相关的信息,隔离级别,超时信息,传播行为,是否只读
-
TransactionStatus
事务状态- 事务状态:用于记录在事务管理的过程中,事务的状态的对象
三个API的关系如下:
Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理的过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中
Spring事务中的传播行为
主要用来解决 Service业务层方法相互调用的问题
-
Spring提供了七种事务的传播行为,一般用
PROPAGATION_REQUIRED
-
保证多个操作在同一个事务中
-
保证多个操作不在同一个事务中
-
嵌套式事务
-
搭建Spring的事务管理的环境
详见 com.binyu.tx.demo1
JdbcDaoSupport类
- 创建Service的接口和实现类
- 创建Dao的接口和实现类
- 配置Service和Dao:交给Spring管理
<!-- 配置Service -->
<bean id="accountService" class="com.binyu.tx.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.binyu.tx.demo1.AccountDaoImpl"></bean>
-
在Dao中编写扣钱和加钱方法
- 配置连接池和JDBC模板
<!-- 配置连接池和JDBC模板 --> <!-- 引入属性文件==================== --> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 配置Spring的JDBC的模板=============== --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
在Dao中注入JDBC模板
-
方法一
public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
-
方法二
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money,from); } @Override public void inMoney(String to, Double money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money ,to); } }
-
如果没有事务管理,一旦业务层出现问题,数据库就会被误操作
Spring的事务管理
编程式事务
- 配置平台事务管理器
- 配置事务管理的模板类
- 在业务层注入事务管理的模板
- 配置文件注入事务管理的模板
- 编写事务管理的代码
<!-- 配置Service -->
<bean id="accountService"
class="com.binyu.tx.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao">
</property>
<!-- 注入事务管理的模板 -->
<property name="transactionTemplate" ref="transactionTemplate">
</property>
</bean>
<!--配置平台事务管理器,拿到连接池 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</property>
</bean>
<!--配置事务管理的模板,简化事务管理的代码-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property
</bean>
package com.binyu.tx.demo1;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
/**
* 转账的业务层的实现类
* @author Dong Binyu
*
*/
public class AccountServiceImpl implements AccountService {
// 注入Dao
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
// 注入事务管理的模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
/**
* from:转出账号
* to:转入账号
* money:转帐金额
*/
@Override
public void transfer(final String from, final String to,final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
// 这两个操作需要事务管理
accountDao.outMoney(from, money);
// int i=1/0;
accountDao.inMoney(to,money);
}
});
}
}
声明式事务管理—AOP
XML式的声明式事务管理
l 第一步:引入aop的开发包
l 第二步:恢复转账环境
l 第三步:配置事务管理器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IziLSbbH-1589804546898)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps1.jpg)]
l 第四步:配置增强
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmQke1Tc-1589804546900)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps2.jpg)]
l 第五步:AOP的配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lx5VZdJJ-1589804546901)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps3.jpg)]
l 测试
注解式的声明式事务管理@Transactional
l 第一步:引入aop的开发包
l 第二步:恢复转账环境
l 第三步:配置事务管理器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DGYXMOTX-1589804546902)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps4.jpg)]
l 第四步:开启注解事务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlQ2mfZV-1589804546903)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps5.jpg)]
l 第五步:在业务层添加注解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVwSc4wy-1589804546905)(file:///C:\Users\dby\AppData\Local\Temp\ksohtml137472\wps6.jpg)]