目录
2. spring数据访问模块的事务管理模块(AOP讲解后进行详细介绍)
Spring 是什么?
spring 是一个轻量级的,非侵入式的,IOC和AOP的一站式框架。为简化企业级应用开发而生。
轻量级: 核心功能:体积小(一般在1M以下或者几百kb)、占用资源小,运行效率高
非侵入式:
我们的业务代码中的类,不会继承实现spring框架中的类和接口。 即在编写一些业务类的时候不需要继承Spring特定的类,通过配置完成依赖注入后就可以使用,这样spring就没有入侵到我们的业务代码中。
IOC:
Inversion of Control 控制反转 以前程序中使用对象是程序员是自己用,使用了spring 以后,我们不需要new 对象,由spring框架来管理生成对象。
AOP:(增强)
面向切面编程。
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP的思想是:可以帮我们动态代理的调用,而不需要修改源代码
一站式:
它集项目整体管理,数据访问,WEB,测试等为一身的一站式框架。
Spring Hello World 搭键
1. Maven 导入 spring 核心基础 jar
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2.编写spring配置文件(spring.xml), 配置哪些类需要spring来管理
在bean标签中,配置需要spring框架管理的类, 把spring 框架生成的对象称为一个bean 对象,此对象与自己new 对象是有区别的 spring 在创建的时候,可以根据配置为生成的对象增强功能。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.User"> </bean>
</beans>
3. 编写一个 User 实体类
public class User {
private String name;
private int no;
public User(){
System.out.println("User无参");
}
public User(String name, int no) {
this.name = name;
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", no=" + no +
'}';
}
}
4. 编写测试类Test1,测试 spring
使用ClassPathXmlApplicationContext来读取配置文件
package com.test;
import com.model.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1 {
public static void main(String[] args) {
以前使用类创建对象是这样的,在哪使用在哪new
User user = new User();
使用spring 框架来管理生成对象,
使用spring框架中的一个实现类,读取spring的配置文件,
生成并管理这些对象
在程序中需要对象时,直接从spring框架中获取即可。
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
//获取
User user1 = app.getBean("user", User.class);
System.out.println(user1);
}
}
5.获得spring创建生成的对象
运行结果为User类的地址,则搭建成功。
IOC(控制反转)
IOC 是一种编程(或架构)思想。把什么的控制权,反转给谁。就是将原来程序中自己new对象的权利,反转给spring框架。
正控:若要使用某个对象,需要自己去负责对象的创建。
反控:若要使用某个对象,只需要从spring容器中获取需要使用的对象,不关心对象创建的过程,把创建对象的控制权反转给spring框架。
目的:降低耦合度。
实现:在spring.xml文件中配置需要spring管理的类。
- 解析xml来读取配置;
- 使用bean工厂创建对象(反射机制);
- 在spring中维护一个容器(Map),将创建的对象管理起来;
- 在哪里需要,在哪里进行注入。
底层实现原理: 解析xml/扫描注解标签 + 工厂模式 + 反射机制
控制反转:将在程序中创建对象的权利反转给spring框架,由spring统一管理。
优点:解耦(降低耦合性),功能增强
spring管理bean方式
1. 在xml中配置
xml配置方式实现
id = "对象名" class = "类的地址" name = "也可以定义对象名" scope = "指导spring框架如何生成对象"(singleton,prototype) singleton:默认的 单例的 在一个应用程序中,为一个类只创建一个对象。对象在spring容器启动时创建。(多次创建都为同一个对象地址) prototype: 原型的 每一次获取时,会创建一个新的对象 request: 一次请求中创建一个 session: 一次会话中创建一个 application: 一个应用程序中创建一个
依赖注入
在IoC的同时,会发生一个动作,称为依赖注入。就是spring 创建对象的同时,为对象中的属性注入值。
注入值的方式(两种):
1. 通过属性的set方法注入
2. 通过构造方法注入(有弊端不建议使用)
先写有参的构造方法
<bean id="user" name="user2" class="User" scope="singleton">
<!-- 使用set方法-->
<property name="no" value="100"></property>
<property name="name" value="ji,"></property>
<!-- 构造方法-->
<constructor-arg name="no" value="101"></constructor-arg>
<constructor-arg name="name" value="jim"></constructor-arg>
</bean>
<!-- spring创建UserDao对象-->
<bean id="userDao" class="com.dao.UserDao">
</bean>
<!-- spring在创建UserService的同时,为其UserDao属性注入值-->
<bean id="userService" class="com.service.UserService">
<property name="userDao" ref="userDao"></property>
<!-- 注入-->
</bean>
2.使用注解方式(最终使用的方式)
添加配置
开启注解扫描 ,会扫描指定包下的类。
<!--开启注解扫描 会扫描指定包下的类-->
<context:component-scan base-package="com"> </context:component-scan>
@Scope(value = "singleton") 指定类是原型的或单例的
model层
@Component(value = "user")
@Scope(value = “prototype”) 原型
@Scope(value = “singleton”) 单例
@Component(value = "user")
@Scope(value = "singleton")
public class User {
private String name;
private int no;
//方法略
}
dao层 表示数据访问层
@Repository(value = "userDao")
UserDao == <bean id = "userDao" class="com.dao.UserDao">
//@Repository(value = "userDao")
@Repository
public class UserDao {
public void save(){
System.out.println("保存用户");
}
}
servic层 调用dao层
@Service(value = "userService")
可以使用注解标签为属性注入值
@Autowired 属于Spring框架提供的注入的注解标签,一般写在属性上面或者属性的set方法上面。
可以通过类型注入(byType):不需要对象名,可以根据属性的类型,在spring容器中查找。
@Autowired
UserDao userDao;
可以通过对象名注入(byName):通过@Qualifier指定需要注入的对象名。(注:必须同@Autowired 一起使用)
@Autowired
@Qualifier(value = "userDao")
UserDao userDao;
默认情况下注入的属性值不能为空。
UserDao userDao; //是UserService 中的一个属性
@Service(value = "userService")
public class UserService {
@Autowired
UserDao userDao; //是UserService 中的一个属性
public void save(String name){
userDao.save();
}
}
@Resource是jdk中提供的注解标签
@Resource 注解既可以按名称匹配 Bean,也可以按类型匹配 Bean。默认按照 ByName 自动注入
可以按照类型注入
@Resource
UserDao userDao;
可以按照名称注入
@Resource(name = "userDao")
UserDao userDao;
注解与XML对比
注解优点:方便直观,高效。
注解缺点:修改时需要重新编译代码的。(*.java --> 编译为 .class文件)
xml优点:配置和代码是分离的,在xml中进行修改,无需编译代码,只需要重启服务器即可将新的配置进行加载。
xml缺点:编写麻烦、效率低,大型项目过于复杂。
Spring JDBC
1.spring使用IOC管理其他的bean
spring是如何使用IOC管理其他的bean, 提供JdbcTemplate(了解即可) 增 删 改 查 , mybatis
1.1 spring管理数据库连接对象
1)导入jar包
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!-- 阿里数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
2)创建db.xml文件和config.properties属性文件,并在spring.xml文件中进行管理。
创建config.properties属性文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/school?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
uname=root
password=root
创建db.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"
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">
<!--让spring 管理阿里数据源对象 druid-德鲁伊-->
<context:property-placeholder location="config.properties"/>
<!-- 让spring 管理阿里数据源对象 druid-德鲁伊 实现了数据库连接池功能销 减少了频繁创建销毁数据库连接对象的开销-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${uname}"></property>
<property name="password" value="${password}"></property>
<property name="initialSize" value="10"></property>
<property name="minIdle" value="5"></property>
<property name="maxActive" value="20"></property>
</bean>
</beans>
3) 在spring文件中添加
<import resource="db.xml"></import>
1.2 生成提供JdbcTemplate
springjdbc模块封装了 JdbcTemplate类,用于执行sql。
- execute:无返回值,可执行 ddl,增删改语句
- update:执行新增、修改、删除语句;
- queryForXXX:执行查询相关语句;
在db.xml中添加
<!--springjdbc模块封装了 JdbcTemplate类,用于执行sql-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
@Repository(value = "userDao")
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void save(){
// jdbcTemplate.update("insert into user(account,password,gender)value(?,?,?)","jim","111","男");
/*User user = jdbcTemplate.queryForObject("select * from user where id = ?",
new RowMapper<User>(){
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setNo(resultSet.getInt("id"));
user.setName(resultSet.getString("account"));
return user;
}
}
,1);
System.out.println(user);*/
List<User> users = jdbcTemplate.query("select * from user where gender=?",
(resultSet,i)->{
User user = new User();
user.setNo(resultSet.getInt("id"));
user.setName(resultSet.getString("account"));
return user;
}
,"男");
System.out.println(users);
}
}
2. spring数据访问模块的事务管理模块(AOP讲解后进行详细介绍)
事务?
事务属于数据库功能,sqlsession.commit();提交事务
事务可以看做是由数据库若干操作组成的一个单元(一个整体),在业务的代码执行时,有时一个业务操作需要向数据库发送多条sql,多条sql属于同一个业务的。
要求是一个整体,要么都执行成功,要么都不成功。
事物特性:
原子性、隔离性、持久性、一致性(最终目的)。
数据库事务管理就是在你将这若干操作执行完成后,需要将事物提交,数据库对数据进行操作,最终达到持久性,数据库事务重要性,增、删、改操作必须在事务管理(没有玩问题提交,有问题不能提交)中进行。
举例:
转账:A向转账转账500;
A账户减500,B账户加500。
网购:下订单:平台
支付: 平台
@Repository
public class ZdDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void save(){
}
public void jia(){
jdbcTemplate.update("update zd set money = money + 500 where id = 2");
}
public void jian(){
jdbcTemplate.update("update zd set money = money - 500 where id = 1");
}
}
@Service(value = "zdService")
public class ZdService {
@Autowired
ZdDao zdDao;
public void save(){
zdDao.save();
}
public void zhuanzhang(){
zdDao.jian();
System.out.println(10/0);
zdDao.jia();
}
}
spring事务管理
spring代理我们的程序提交事务。
配置:
(1)配置事务管理器:配置 spring 事务管理类, 并注入数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
(2)注解方式:开启注解事务管理
<!-- 开启注解事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
1. 编程式事务
需要在我们的代码中显示的提交事务的 2. 声明式事务
有两种实现方式:
(1) 在xml中配置
需要大量的配置,为需要在事务管理中的方法增强功能。
注入private TransactionTemplate transactionTemplate;
(2) 注解方式实现
在类上加入@Transactional
注解标签可以作用在类和方法上,
作用在类上,表示此类中所有的方法都会添加事务增强功能
作用在某个方法上,表示此方法可以添加事务增强功能。
@Service(value = "zdService")
@Transactional
public class ZdService {
//代码略
}
声明式事务失效的场景
- 同一个类中方法调用,导致声明式事务失效(在自己的类中调用,就是普通方法的调用, 不会走代理的环绕通知)
- 将@Transactional 应用在非public 修饰的方法上
- 将@Transactional 用在的类非spring容器管理的bean
- @Transactional 注解事务传播行为设置错误异常被 catch 捕获导致@Transactional 失效。例:@Transactional(rollbackFor = RuntimeException ) 默认出现运行时异常时不提交
- 可以修改rollbackFor = Exception.class 对所有异常都不提交
- 数据库引擎不支持事务
怎么管理?
提供DataSourceTransactionManager实现事务提交功能。
管理的实现原理:AOP使用代理的方式进行管理,动态代理、静态代理、jdk代理(反射实现)、cglib代理。
注:CGLIB是一个功能强大,高性能的代码生成包。(Code Generation Library)它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
AOP(面向切面编程)
把业务之外的代码,例如事务提交,获得sqlsession,获得接口代理,事务提交,关闭sqlsession等管理起来。使用预编译的方式和动态代理的方式,使用程序可以统一维护的一种技术。
作用:可以将业务逻辑之间进行隔离,降低耦合度,使得开发维护方便。
OOP与AOP的区别:
OOP: 是程序各层之间的整体设计。AOP:针对业务逻辑处理过程中某个具体的步骤进行处理。
AOP到底为程序带来了哪些好处?
可以将一些公共的,重复出现的非业务代码进行提取,然后使用动态代理的方式,为我们的业务代码横切添加额外提取的功能,而且不需要显示的在业务代码中调用,可以通过一个代理对象,来帮助我们调用这些抽取出来的方法。
通过配置,告诉代理,在执行哪个方法的时候,帮助我们动态调用某个额外的方法。
代理:中介、4s、手机店等。
好处:减少重复,专注业务。
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑.
使用案例:
- 事务处理:开启事务,关闭事务,出现异常后回滚事务
- 权限判断:在执行方法前,判断是否具有权限
- 日志:在执行前进行日志处理
AOP的基本概念
- 连接点(Joinpoint):类中可以被增强的方法。
- 切入点:类中实际被增强的方法。
- 通知:在连接点上要做的事情
- 切面:把通知添加到连接点的整个过程
- 目标:要增强的方法所在的类
- 代理:(淡化 框架的内部实现好了的)配置完成后,为目标对象创建一个代理对象。
首先提取通知要增强的功能,发现切入点,进行配置。
spring 中的事务管理
aop底层是java设计模式中的动态代理模式。
AOP不是spring框架特有的技术,spring 框架应用这一技术思想进行封装。
配置步骤
在pom.xml中配置,并下载jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
public class MyUtil {
/*
通知: 为连接点添加的功能
*/
public void saveLog(){
System.out.println("保存日志");
}
public void commit(){
System.out.println("提交事务");
}
public void exception(Throwable e){
System.out.println("统一的异常提示:"+e.getMessage());
}
/*
环绕通知
*/
public void aound(ProceedingJoinPoint joinPoint){
System.out.println("方法执行之前保存日志");
try {
joinPoint.proceed();//将切入点动态传递过来了, proceed()调用切点
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("出现异常"+throwable.getMessage());
}
System.out.println("方法执行之后提交事务");
}
}
@Service(value = "userService")
public class UserService {
/*
连接点: 可以被增强的方法
切入点: 实际被增强的方法
*/
public void save(String name){
System.out.println(10/0);
System.out.println("有参保存用户");
}
public void save(){
System.out.println("无参保存用户");
}
public void update(){
System.out.println("修改用户");
}
public void delete(){
System.out.println("删除用户");
}
}
aop的xml配置方式
<bean id="aopdemo" class="com.util.MyUtil"></bean>
配置通知和切入点
配置切入点 和 通知 最终框架内部生成代理对象
<aop:config>
配置切入点
<aop:pointcut expression="execution(* com.service.UserService.*(..))" id="save" />
配置通知和切入点
<aop:aspect ref="aopdemo">
<aop:before method="saveLog" pointcut-ref="save"></aop:before>
<aop:after method="commit" pointcut-ref="save"></aop:after>
<aop:after-throwing method="exception" pointcut-ref="save" throwing="e"></aop:after-throwing>
</aop:aspect>
</aop:config>
环绕通知
<aop:config>
<aop:pointcut expression="execution(* com.service.UserService.*(..))" id="save" />
<aop:aspect ref="aopdemo">
<aop:around method="aound" pointcut-ref="save"></aop:around>
</aop:aspect>
</aop:config>
AOP基于注解的方式实现
不需要在xml中进行配置
@Component
@Aspect
public class AopDemo {
@Before("execution(* com.service.UserService.*(..))")
public void saveLog(){
System.out.println("保存日志");
}
@After("execution(* com.service.UserService.*(..)) ")
public void commit(){
System.out.println("提交事务");
}
@AfterThrowing(value = " execution(* com.service.UserService.*(..))", throwing = "e" )
public void exceptionAdvice(Throwable e ){
System.out.println("统一的异常提示:" + e.getMessage());
}
/*
环绕通知
*/
@Around("execution(* com.service.UserService.*(..)) ")
public void aound(ProceedingJoinPoint joinPoint){
System.out.println("方法执行之前保存日志");
try {
joinPoint.proceed();//将切入点动态传递过来了, proceed()调用切点
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("出现异常"+throwable.getMessage());
}
System.out.println("方法执行之后提交事务");
}
}
Spring 事务传播行为
什么是Spring事务传播行为?
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NE W | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPOR TED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。 |
PROPAGATION_REQUIRED
A方法传播行为REQUIRED,调用B方法,B方法传播行为也是REQUIRED的情况下,那么事务B就加到事务A去执行。A方法无事务,调用B方法,B方法传播行为是REQUIRED的情况下,那么B事务自己会添加一个事务。
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
}
事务B,即被调用方法的事务
@Transactional(propagation = Propagation.REQUIRED)
public void savelog(){
commonDao.save();
int a = 10/0;
}
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
A方法传播行为REQUIRED,调用B方法,B方法是传播行为是SUPPORTS的情况下,那么事务B就加入到事务A里面去执行。当A方法无事务,调用B方法,B方法传播行为是SUPPORTS的情况下,那么方法就以非事务方法执行。
简单来说,方法B不是独立的事务,跟随方法A的事务。
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
}
事务B,即被调用方法的事务
@Transactional(propagation = Propagation.SUPPORTS)
public void savelog(){
commonDao.save();
int a = 10/0;
}
PROPAGATION_REQUIRES_NEW
总是新建一个事务,如果当前存在事务,把当前事务挂起,直到新建的事务结束。
A方法传播行为REQUIRED,调用B方法,B方法传播行为是REQUIRES_NEW的情况下,那么B方法会开启一个独立的新的事务执行,B不会受到A的影响。A方法无事务,调用B方法,B方法传播行为是REQUIRES_NEW的情况下,那么B方法会开启一个独立的新的事务执行。
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
int a = 10/0;
}
事务B,即被调用方法的事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void savelog(){
commonDao.save();
}
Spring 集成 MyBatis
Spring 集成 Mybatis 其核心是将 SqlSessionFactory 交由 Spring 管理,并由 Spring 管理对 dao 接口的代理实现
1. 导入jar包
<!--spring-mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
2. 在spring_mybatis中配置sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="mybatis-config.xml"></property>
<property name="mapperLocations" value="com/*Mapper.xml">
</property>
</bean>
3. 指定生成接口代理
<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ff.ssm.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">
</property>
</bean>
4. 在 service 中注入 Dao 代理接口,此接口有 Spring 代理实现
@Service(value = "UserService")
public class UserService {
@Autowired
UserMapper userMapper;
@Transactional
public void saveUser(){
userMapper.saveUser();
}
}