【SSM框架】初识Spring

初识Spring

Spring家族

Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若千个项目,每个项目用于完成特定的功能

  • ✅Spring Framework(底层框架)
  • Spring Boot(提高开发速度)
  • Spring Cloud(微服务)

Spring发展史

  • Spring1:纯配置文件
  • Spring2:注解开发(语法糖)
  • Spring3:不写配置
  • Spring4:修改API
  • Spring5:支持jdk8

Spring系统架构

Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基

系统架构图:上层依赖于下层

  • Data Access:数据访问
  • Data Integration:数据集成
  • Web:Web开发
  • AOP:面向切面编程
  • Aspects:AOP思想实现
  • Core Container:核心容器
  • Test:单元测试和集成测试

核心容器

IoC/DI

概念介绍

IoC(控制翻转):耦合度偏高 => 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象

Spring技术对IoC思想进行了实现:

  • Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的、外部
  • IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean

DI(依赖注入):在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

目的:充分解耦

  • 使用IoC容器管理bean(Ioc)
  • 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
使用

1、导入spring的坐标spring-context,对应版本是5.2.10.RELEASE

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

2、创建配置文件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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置bean-->
    <!--
        id属性标示给bean起名字
        class属性表示给bean定义类型
	-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <!--配置server与dao的关系-->
        <!--
			property标签表示配置当前bean的属性
            name属性表示配置哪一个具体的属性
            ref属性表示参照哪一个bean
		-->
        <property name="bookDao" ref="bookDao"/>
    </bean>

</beans>

3、修改ServiceImpl方法

package com.itheima.service.impl;

import com.itheima.dao.BookDao;
import com.itheima.service.BookService;

public class BookServiceImpl implements BookService {
    // 删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    // 提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

Bean

Bean的基础配置

1、name起别名

  • 可以使用逗号、空格、分号分隔
<bean id="bookDao" name="bookEbi" class="com.itheima.dao.impl.BookDaoImpl"/>

2、Bean的作用范围

  • 单例 or 多例:获取的Bean是否为同一个对象
<!-- 单例(默认) -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="singleton"/>
<!-- 多例 -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>

适合交给容器进行管理的bean:

  • 表现层对象
  • 业务层对象
  • 数据层对象
  • 工具对象

不适合交给容器进行管理的bean:

  • 封装实体的域对象
Bean实例化

1、构造方法

Spring实例化Bean的方法:无参构造

<bean id="bookDao" name="bookEbi" class="com.itheima.dao.impl.BookDaoImpl"/>

2、静态工厂

//静态工厂创建对象
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("factory setup....");
        return new OrderDaoImpl();
    }
}
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>

3、实例工厂

public class AppForInstanceUser {
    public static void main(String[] args) {
        // 创建实例工厂对象
        UserDaoFactory userDaoFactory = new UserDaoFactory();
        // 通过实例工厂对象创建对象
        UserDao userDao = userDaoFactory.getUserDao();
        userDao.save();
    }
}
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

4、FactoryBean

// FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    // 代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }
}
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
Bean的生命周期
  1. 初始化容器
    1. 创建对象(内存分配)
    2. 执行构造方法
    3. 执行属性注入(set操作)
    4. 执行bean初始化方法
  2. 使用bean
    1. 执行业务操作
  3. 关闭/销毁容器
    1. 执行bean销毁方法
配置文件

配置initdestroy方法

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
public class AppForLifeCycle {
    public static void main( String[] args ) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 注册关闭钩子函数:在虚拟机退出之前回调此函数,关闭容器
        // ctx.registerShutdownHook();
        
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();

        // 关闭容器
        ctx.close();
    }
}
实现接口
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <property name="bookDao" ref="bookDao"/>
</bean>
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        System.out.println("setBookDao ...");
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

    public void destroy() throws Exception {
        System.out.println("service destroy ...");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("service init ...");
    }
}

依赖注入

setter注入

1、简单类型

在bean中定义引用类型属性并提供可访问的set方法

配置中使用property标签value属性注入简单类型对象

<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <!--property标签:设置注入属性-->
    <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
    <!--value属性:设置注入简单类型数据值-->
    <property name="connectionNum" value="100"/>
    <property name="databaseName" value="mysql"/>
</bean>

2、引用类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

配置中使用property标签ref属性注入引用类型对象

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <property name="bookDao" ref="bookDao"/>
</bean>
构造器注入

标准书写:根据构造方法参数名称注入

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <constructor-arg name="connectionNum" value="10"/>
    <constructor-arg name="databaseName" value="mysql"/>
</bean>

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>

解决参数类型重复问题,使用位置解决参数匹配:根据构造方法参数位置注入

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <constructor-arg index="0" value="mysql"/>
    <constructor-arg index="1" value="100"/>
</bean>

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
  1. 实现方式不同:构造函数注入是通过构造函数将依赖对象注入进来,而setter注入是通过setter方法将依赖对象注入进来。
  2. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致nu11对象出现
  3. 可选依赖使用setter注入进行,灵活性强
  4. spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  5. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  6. 实际开发过程中还要根据实际情况分析 ,如果受控对象没有提供setter方法就必须使用构造器注入
  7. 自己开发的模块推荐使用setter注入
自动装配

1、提供setter方法

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

2、配置xml

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<!-- 按类型(找实现的接口) -->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>

<!-- 按名称 -->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耩合 ,不推荐使用
  4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入

数组

<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
        <!-- <ref bean="beanId"/> -->
    </array>
</property>

list集合

<property name="list">
    <list>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>chuanzhihui</value>
    </list>
</property>

set集合

<property name="set">
    <set>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>boxuegu</value>
    </set>
</property>

map集合

<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>

Properties

<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

加载Properties文件

1、开启context命名空间

<?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
            ">
</beans>

2、使用context命名空间,加载指定properties文件

<context: property-placeholder location="jdbc.properties"/>

3、使用${}读取加载的厲性值

<property name="username" value="${jdbc.username}"/>

不加载系统属性:

<context: property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>

加载properties文件标准格式:

<context: property-placeholder location="classpath:*.properties"/>

从类路径或jar包中搜索并加载properties文件

<context: property-placeholder location="classpath*:*.properties"/>

容器

创建容器的方法

方式一:类路径加载配置文件

Applicationcontext ctx = new ClassPathxmlApplicationcontext ("applicationcontext. xml");

方式二:文件路径加载配置文件

Applicationcontext ctx = new FilesystemxmlApplicationcontext("D:\\applicationcontext.xml")

加载多个配置文件

ApplicationContext ctx = new ClassPathXmlApplicationContext ("bean1.xml", "bean2.xml");
容器类层次结构

BeanFactory初始化

类路径加载配置文件

Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean (BookDao.class);
bookDao.save();

BeanFactory创建完毕后,所有的bean均为延迟力载

总结

容器相关
  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
  • ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
  • ApplicationContext接口常用初始化类
    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext
bean相关

依赖注入相关

注解开发

注解开发

使用@Component定义bean

@Component("bookDao")
public class BookDaoImpl implements BookDao {
    // xxx
}

@component
public class BookServiceImpl implements BookService{
    // xxx
}

核心配置文件中通过组件扫描加载bean

<context:component-scan base-package="com.itheima"/>

Spring提供@Component注解的三个衍生注解

  • @controller:用于表现层bean定义
  • @service:用于业务层bean定义
  • @Repository:用于数据层bean定义

纯注解开发

使用Java类代替Spring核心配置文件

@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
  • @configuration注解用于设定当前类为配置类
  • @componentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

bean管理

bean作用范围

使用@Scope定义bean作用范围:单例、多例

@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao {
    // xxx
}
bean的生命周期

init:@PostConstruct

destroy:@PreDestroy

@Repository
@scope("singleton")
public class BookDaoImpl implements BookDao{
    public BookDaoImpl() {
		System.out.println("book dao constructor ...");
	}
    
    @PostConstruct
    public void init() {
        System.out.println("book init ...");
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("book destory ...");
    }
}

依赖注入

引用类型

@Autowired:注入引用类型,自动装配模式,默认按类型装配

@Qualifier:自动装配bean时按bean名称装配(选填)

  • 自动装配基于反射设计创建对象并暴力反射对应厲性为私有属性初始化数据 ,因此无需提供setter方法
  • 自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDao")
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}
简单属性

@Value:注入简单类型(无需提供set方法)

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    @Value("wmh")
    // 来自prop文件
    // @Value("${name}")
    private String name;

    public void save() {
        System.out.println("book dao save ..." + name);
    }
}
配置引入prop

@PropertySource:加载properties配置文件

不支持使用通配符*

@Configuration
@ComponentScan("com.itheima")
@PropertySource({"jdbc.properties"})
public class SpringConfig {
}

第三方bean

Bean管理
导入式
public class JdbcConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

使用@Import注解手动加入配置类到核心配置

@Configuration
@Import({JdbcConfig.class})
public class SpringConfig {
    
}
扫描式
@Configuration
public class JdbcConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

使用@ComponentScan注解扫描配置类所在的包,加载对于的配置类信息

@Configuration
@ComponentScan({"com.itheima.config"})
public class SpringConfig {
    
}
依赖注入

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

 public class JdbcConfig {
    @Value("jdbc:mysql://localhost:3306/spring")
    public String url;
    @Value("root")
    public String username;
    @Value("root")
    public String password;
    @Value("com.mysql.jdbc.Driver")
    public String diver;

    @Bean
    public DataSource getDs() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setDriverClassName(diver);
        return ds;
    }

    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource ds) {
        return new JdbcTemplate(ds);
    }
 }

AOP

AOP概述

AOP:面向切面编程,一种编程范式,指导开发者如何组织程序结构(在不惊动原始设计的基础上为其进行功能增强)

OOP:面向对象编程

Spring理念:无入侵式/无侵入式

AOP使用

1、导入坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

2、定义Dao接口和实现类

@Repository
public class BookDaoImpl implements BookDao {
    public void update(){
        System.out.println("book dao update ...");
    }
}

3、定义通知类,制作通知。绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置

@Component
// 设置当前类为切面类类
@Aspect
public class MyAdvice {
    // 设置切入点,要求配置在方法上方
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    // 设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

切入点定义依托一个不具有实际意义的方法进行 ,即无参数,无返回值,方法体无实际逻辑

4、开启Spring对AOP注解驱动支持

@Configuration
@ComponentScan("com.itheima")
// 开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    1. 匹配失败,创建对象
    2. 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    1. 获取bean,调用方法并执行,完成操作
    2. 获取的bean是代理对象时 ,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
  • 目标对象(Target):原始功能去掉共性功能对应的类产生的对象 ,这种对象是无法直接完成最终工作的
  • 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

切入点表达式

切入点:要进行增强的方法

切入点表达式:要进行增强的方法的描述方式

描述方式

1、接口

execution(void com.itheima.dao.BookDao.update())

2、实现类

execution(void com.itheima.dao.impl.BookDaoImpl.update())
切入点表达式格式
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
  • 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
  • 访问修饰符:public, private等,可以省略
  • 返回值
  • 包名
  • 类/接口名
  • 方法名
  • 参数
  • 异常名:方法定义中抛出指定异常,可以省略

通配符:

  • *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
  • ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
  • +:专用于匹配子类类型
书写技巧

所有代码按照标准规范开发,否则以下技巧全部失效

描述切入点通常描述接口,而不描述实现类

访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)

返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述

包名书写尽量不使用..匹配,效率过低 ,常用*做单个包描述匹配,或精准匹配

接口名/类名书写名称与模块相关的采用*匹配 ,例如UserService 书写成*Service,绑定业务层接口名

方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy*selectAll书写成selectAll

参数规则较为复杂,根据业务方法灵活调整

通常不使用异常作为匹配规则

通知类型

前置通知、后置通知

// @Before:前置通知,在原始方法运行之前执行
@Before("pt()")
public void before() {
    System.out.println("before advice ...");
}

// @After:后置通知,在原始方法运行之后执行
@After("pt()")
public void after() {
    System.out.println("after advice ...");
}

环绕通知

// @Around:环绕通知,在原始方法运行的前后执行

// 无返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("around before advice ...");
    // 表示对原始操作的调用
    Object ret = pjp.proceed();
    System.out.println("around after advice ...");
    return ret;
}

// 有返回值
@Around("pt2()")
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("around before advice ...");
    //表示对原始操作的调用
    Integer ret = (Integer) pjp.proceed();
    System.out.println("around after advice ...");
    return ret;
}

环绕通知可以通过ProceedingJoinPoint获取签名对象

// 设置环绕通知,在原始操作的运行前后记录执行时间
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
    // 获取执行的签名对象
    Signature signature = pjp.getSignature();
    String className = signature.getDeclaringTypeName();
    String methodName = signature.getName();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        pjp.proceed();
    }
    long end = System.currentTimeMillis();
    System.out.println("万次执行:" + className + "."+methodName+"---->" + (end-start) + "ms");
}

AOP通知获取数据

获取切入点方法的参数

  • JoinPoint:适用于前置、后置、返回后、抛出异常后通知
  • ProceedJointPoint:适用于环绕通知
@Before("pt()")
public void before(JoinPoint jp) {
    // 获取参数
    Object[] args = jp.getArgs();
    System.out.println(Arrays.toString(args));
    System.out.println("before advice ..." );
}

@Around("pt()")
public Object around(ProceedingJoinPoint pjp) {
    Object[] args = pjp.getArgs();
    // 修改参数
    args[0] = 666;
    // 获取返回值
    ret = pjp.proceed(args);
    return ret;
}

获取切入点方法返回值

  • 返回后通知
  • 环绕通知
// 设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(JoinPoint jp, String ret) {
    System.out.println("afterReturning advice ..." + ret);
}

public Object around(ProceedingJoinPoint pjp) {
    Object[] args = pjp.getArgs();
    System.out.println(Arrays.toString(args));
    args[0] = 666;
    Object ret = null;
    // 异常处理
    try {
        ret = pjp.proceed(args);
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return ret;
}

获取切入点方法运行异常信息

  • 抛出异常后通知
  • 环绕通知
// 设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
    System.out.println("afterThrowing advice ..." + t);
}

事务

事务使用

事务作用:在数据层保障一系列的数据库操作同成功同失败

Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合

注解式事务可以添加到业务方法上表示当前方法开启事务 ,也可以添加到接口上表示当前接口所有方法开启事务

1、在业务层接口上添加Spring事务管理

public interface AccountService {
    // 配置当前接口方法具有事务
    @Transactional
    public void transfer(String out, String in, Double money);
}

2、设置事务管理器

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    // 配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

3、开启注解式事物驱动

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement	// 开启注解式事务驱动
public class SpringConfig {
}

事务角色

事务相关配置

事务传播行为:事务协调员对事务管理员所携带事务的处理态度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/318551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C# 图解教程 第5版 —— 第24章 预处理指令

文章目录 24.1 什么是预处理指令24.2 基本规则24.3 符号指令&#xff08;#define、#undef &#xff09;24.4 条件编译&#xff08;#if、#else、#elif、#endif&#xff09;24.5 条件编译结构24.6 诊断指令&#xff08;#warning、#error&#xff09;24.7 行号指令&#xff08;#li…

最新域名群站开源系统:打造强大网站矩阵,引领SEO优化新潮流!

搭建步骤 第一步&#xff1a;安装PHP和MYSQL服务器环境 对于想要深入了解网站建设的人来说&#xff0c;自己动手安装PHP和MYSQL服务器环境是必不可少的步骤。这将使你能够更好地理解网站的运行机制&#xff0c;同时为后续的网站开发和优化打下坚实基础。 第二步&#xff1a;…

迅腾文化用网络集成化生态系统助力品牌之路的坚实后盾

商业竞争激烈&#xff0c;品牌不仅是企业的标志和形象&#xff0c;更是其核心价值和竞争力的体现。然而&#xff0c;企业在品牌推广过程中面临着诸多如缺乏有效的渠道管理、品牌形象模糊以及竞争激烈的市场环境等。这些阻碍着企业的品牌发展和市场占有率的提升。本文将通过企业…

[GN] nodejs16.13.0版本完美解决node-sass和sass-loader版本冲突问题

项目场景&#xff1a; npm install 运行vue项目时候 问题描述 项目场景&#xff1a;sass-loader &#xff0c;node-sass出错 ! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: smoore-mes-web1.4.0 npm ERR! Found: webpack3.12.0 npm ER…

【计算机组成原理】高速缓冲存储器 Cache 的写策略(Writing Policy)

写策略 Writing Policy 缓存的写策略指的是确定何时将数据写入缓存或主存的策略。 写命中 Write Hit 全写法 Write Through 在全写法策略中&#xff0c;每次发生写操作时都会将数据同时写入缓存和主存。这样可以保证数据的一致性&#xff0c;但会增加主存的写入操作&#xf…

ubuntu连接xshell怎么连接

在网上找了好多办法都不行 例如 太久没打开Ubuntu可能输入命令查不到IP地址&#xff0c;解决办法也比较简单&#xff0c;首先第一步 确定自己能不能进入管理员root权限&#xff08;输入命令su&#xff09;&#xff0c;如果没有的话得重新配置&#xff0c;如下图 这是因为当前Ub…

黑马程序员JavaWeb开发|案例:tlias智能学习辅助系统(1)准备工作、部门管理

一、准备工作 1.明确需求 根据产品经理绘制的页面原型&#xff0c;对部门和员工进行相应的增删改查操作。 2.环境搭建 将使用相同配置的不同项目作为Module放入同一Project&#xff0c;以提高相同配置的复用性。 准备数据库表&#xff08;dept, emp&#xff09; 资料中包含…

[Linux 进程(三)] 进程优先级,进程间切换,main函数参数,环境变量

文章目录 1、进程优先级1.1 Linux下查看进程优先级1.2 Linux 进程优先级的修改PRI and NItop命令配合操作更改优先级 1.3 竞争 独立 并行 并发 2、进程间切换3、Linux2.6内核进程调度队列3.1 活跃进程3.2 过期进程 4 main函数参数 — 命令行参数4.1 利用main函数的参数实现一个…

golang实现rpc方法二:使用jsonrpc库【跨平台】

首先在golang实现rpc方法一net/rpc库中实现了RPC方法&#xff0c;但是那个方法不是跨平台的&#xff0c;没法在其他语言中调用这个实现的RPC方法&#xff0c;接下来我们可以通过jsonroc库实现跨语言的RPC方法。俩种实现方式的代码其实也是差不多的&#xff0c;大差不差&#xf…

Spark---累加器

1.累加器实现原理 累加器用来把 Executor 端变量信息聚合到 Driver 端。在 Driver 程序中定义的变量&#xff0c;在Executor 端的每个 Task 都会得到这个变量的一份新的副本&#xff0c;每个 task 更新这些副本的值后&#xff0c;传回 Driver 端进行 merge。 //建立与Spark框架…

wpf使用Popup封装数据筛选框

(关注博主后,在“粉丝专栏”,可免费阅读此文) 类似于DevExpress控件的功能 这是DevExpress的winform筛选样式,如下: 这是DevExpress的wpf筛选样式,如下: 这是Excel的筛选样式,如下: 先看效果 本案例使用wpf原生控件封装,功能基本上都满足,只是颜色样式没有写…

扫描电镜技术在材料科学中的应用及发展趋势

在材料科学领域&#xff0c;扫描电镜技术扮演着极为重要的角色&#xff0c;广泛应用于多种材料形态结构、界面状况、损伤机制和材料性能预测的研究。本文将深入探讨扫描电镜技术的结构、主要性能、工作原理、试样制备技术以及在不同领域的应用。 第一部分&#xff1a;扫描电镜…

【现代密码学】笔记4--消息认证码与抗碰撞哈希函数《introduction to modern cryphtography》

【现代密码学】笔记4--消息认证码与抗碰撞哈希函数《introduction to modern cryphtography》 写在最前面4 消息认证码与抗碰撞哈希函数MAC概念回顾&#xff08;是的&#xff0c;我忘记这些缩写是什么了。。&#xff09;MAC的定义适应性CMA&#xff08;Chosen Message Attack&a…

Android json功能解析

1. 简介 JAVAScript Object Notation是一种轻量级的数据交换格式具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案&#xff08;有点类似于正则表达式 &#xff0c;获得了当今大部分语言的支持&#xff09;。  JSON采用兼容性很高的文本格式&#xf…

Python | 四、链表

为什么需要链表 在Python中&#xff0c;引入链表这一结构没有像C等语言那样有很多好处&#xff0c;因为Python里的列表和字符串结构已经十分灵活且大小可变&#xff0c;仍保留的好处如下&#xff1a; 列表、字符串等结构是连续存储的&#xff0c;因此如果有一块较小的内存区域…

QuEra 10,000个物理量子位和100个逻辑量子位的量子计算机2026

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

mongoose6.0版以上操作mongodb数据库的基本使用

1、介绍 Mongoose 是一个对象文档模型库&#xff0c;官网 http://www.mongoosejs.net/ 2、作用 方便使用代码操作 mongodb 数据库 3、使用流程 3.1、链接数据库 //1. 安装 mongoose---> npm install mongoose --save//2. 导入 mongoose const mongoose require(&quo…

网络安全B模块(笔记详解)- 网络渗透测试

LAND网络渗透测试 1.进入虚拟机操作系统:BT5中的/root目录,完善该目录下的land.py文件,填写该文件当中空缺的Flag1字符串,将该字符串作为Flag值(形式:Flag1字符串)提交;(land.py脚本功能见该任务第6题) 输入flag sendp(packet) Flag:sendp(packet) 2.进入虚拟机操作…

鸿蒙Harmony-PersistentStorage--持久化存储UI状态储详解

用简单的心境&#xff0c;对待复杂的人生&#xff0c;方能看淡得失&#xff0c;从容入世&#xff0c;潇洒自如&#xff0c;心变得简单了&#xff0c;世界也就简单了 目录 一&#xff0c;定义 二&#xff0c;限制条件 三&#xff0c;使用 一&#xff0c;定义 LocalStorage和App…

Open3D AABB包围盒计算与使用(19)

Open3D AABB包围盒计算与使用(19) 一、算法速览二、算法实现1.代码2.结果少年听雨歌楼上。红烛昏罗帐。壮年听雨客舟中。江阔云低、断雁叫西风。 而今听雨僧庐下。鬓已星星也。悲欢离合总无情。一任阶前、点滴到天明。 一、算法速览 AABB包围盒就是将点云用一个各条边沿着坐…