Spring框架

文章目录

  • Spring 框架
  • 1 概述
  • 2 IOC
  • 3 Bean管理
    • 3.1 XML配置方式
    • 3.2 Java注解方式
    • 3.3 Java代码方式
    • 3.4 对象之间调用示例
    • 3.5 自动装配
  • 4 单例和多例
    • 4.1 单例(Singleton)
    • 4.2 多例(Prototype)
  • 5 生命周期
  • 6 注解管理Bean
    • 6.1 @Component、@Service、@Repository和@Controller
    • 6.2 @Autowired
  • 7 AOP
    • 7.1 几个要点
    • 7.2 通知类型
    • 7.3 配置AOP
      • 7.3.1 使用XML配置AOP的示例
      • 7.3.2 使用注解配置AOP的示例
  • 8 JdbcTemplate
    • 8.1 配置数据源和 JdbcTemplate
    • 8.2 使用 JdbcTemplate 进行数据库操作
  • 9 事务管理
    • 9.1 事务的传播行为
    • 9.2 事务的隔离级别
    • 9.3 案例:银行转账
      • 9.3.1 基于注解的声明式事务管理
      • 9.3.2 基于XML配置的声明式事务管理

Spring 框架

1 概述

Spring 框架是一个开源的 Java 平台应用程序框架,主要用于简化企业级 Java 应用的开发。它为Java开发者提供了一种全面的、一致的编程和配置模型,用于构建现代化、可维护、可测试的企业级应用。Spring 框架包含了多个模块,可以根据需要选择使用:

  1. 核心容器(Core Container): 提供了Spring框架的基本功能,包括IOC(控制反转)和DI(依赖注入)。

    • Beans: 提供了创建、配置和管理Java对象的机制。
    • Core: 提供了核心功能,包括IOC容器和事件系统。
    • Context: 建立在Beans模块之上,提供了访问定义和配置的对象的方式。
  2. 数据访问/集成(Data Access/Integration): 提供了与数据库和其他数据源集成的功能。

    • JDBC: 提供了简化JDBC编码的方法。
    • ORM(对象关系映射): 例如Hibernate、JPA等,提供了简化数据库操作的方式。
    • JMS(Java消息服务): 提供了用于发送和接收消息的功能。
    • 事务管理: 提供了编程和声明式事务管理的支持。
  3. Web(Web): 提供了创建Web应用程序的方法。

    • Web模块: 提供了与基于Servlet的JSP应用程序集成的功能。
    • Web MVC: 提供了一个模型-视图-控制器(MVC)架构,用于构建灵活和强大的Web应用程序。
  4. AOP(面向切面编程): 提供了与切面编程和联盟代理的功能。

    • AOP: 提供了与面向切面编程相关的功能,例如声明性方法拦截。
    • Aspects: 提供了与AspectJ集成的功能。
  5. 安全性(Security): 提供了对应用程序进行认证和授权的功能。

    • Security: 提供了与安全性相关的配置和功能。
  6. 测试(Test): 提供了对Spring组件进行单元测试和集成测试的支持。

    • Test: 提供了对Spring组件进行测试的功能。

Spring 框架的特点:

  • 轻量级: Spring是一个轻量级的框架,不需要大量的配置信息,可以快速启动和运行。
  • IC容器: Spring使用IoC容器来管理对象的生命周期和配置,将组件之间的依赖关系交给Spring进行管理。
  • AOP支持: 提供了面向切面编程的支持,可以将横切关注点(例如日志、事务管理等)从业务逻辑中分离出来。
  • 事务管理: Spring提供了声明式的事务管理,可以将事务管理逻辑与业务逻辑分离开来,简化了事务管理的配置和使用。
  • 模块化和可扩展: Spring框架被设计为由多个模块组成,开发者可以根据需要选择使用特定的模块,使得Spring具有很好的可扩展性。

2 IOC

IOC(Inversion of Control,控制反转) 是一种软件设计思想,它将传统的程序控制流方向颠倒过来,即将原本由程序员控制的对象实例化和执行流程的权力,交由外部容器(通常是框架)来管理,实现了程序的解耦和灵活性增强。

在传统的程序设计中,应用程序通过直接调用类(或对象)的方法来实现各种功能。而在 IOC 容器中,所有的组件(对象)的创建、销毁、依赖关系的维护等都由 IOC 容器来管理,应用程序只需通过配置的方式告诉 IOC 容器需要哪些组件以及这些组件之间的关系,IOC 容器负责实例化对象、维护对象之间的关系,并且在需要的时候将对象提供给应用程序使用。

在 Spring 框架中,IOC 容器主要有两种实现方式:BeanFactory 和 ApplicationContext。

BeanFactory :是 Spring 框架的基础容器,提供了基本的 IOC 功能。 它是 Spring 内部使用的接口,不提供给开发人员使用, 加载 applicationContext.xml 文件的时候不会创建对象,在使用对象的时候才去创建对象。

ApplicationContext : 它是 BeanFactory 的子接口,提供了更强大的功能,一般由开发人员来使用,加载 applicationContext.xml 文件的时候会创建对象。

IOC 的实现方式主要有两种:

  1. 依赖注入(Dependency Injection,DI): 在 IOC 容器中,对象的依赖关系不再通过对象自身控制,而是由外部容器注入。这种方式可以通过构造函数、Setter 方法、接口注入等方式实现。

    构造函数注入: 通过构造函数向对象注入依赖的对象。

    public Student() {}public Student(String name, Integer age) {this.name = name;this.age = age;}
    

    Setter方法注入: 通过Setter方法向对象注入依赖的对象。

    public String getName() {return name;}public void setName(String name) {this.name = name;}
    

    xml配置:

     <!--set方法注入--><bean id="student" class="com.kdx.entity.Student"><property name="name" value="kong"/><property name="age" value="18"/></bean><!--构造函数注入--><bean id="student1" class="com.kdx.entity.Student"><constructor-arg name="name" value="xing"/><constructor-arg name="age" value="18"/></bean><!--p--><bean id="student2" class="com.kdx.entity.Student" p:name="de" p:age="18">
    
  2. 控制反转(Inversion of Control,IOC): 控制反转是 IOC 的核心,它是一种设计思想,实现了对象的解耦。在 IOC 容器中,对象的生命周期由容器管理,对象之间的依赖关系由容器来维护。

3 Bean管理

在Spring框架中,Bean的管理是指Spring容器负责创建、配置和组装应用中的对象。在Spring中,Bean可以通过XML配置、Java注解或Java代码方式进行管理。

3.1 XML配置方式

在XML配置文件中,可以定义Bean的配置,例如:

<bean id="userService" class="com.kdx.UserService"><property name="userDao" ref="userDao"/>
</bean><bean id="userDao" class="com.kdx.UserDao">
</bean>

上述配置表示创建了一个名为userService的Bean,它的类型是com.kdx.UserService,并且它依赖另一个名为userDao的Bean。

3.2 Java注解方式

使用注解方式,可以在类上使用@Component或其派生注解(如@Service@Repository@Controller)来标识Bean,例如:

@Component
public class UserService {// class implementation
}

Spring会自动扫描这些注解,并将标记的类作为Bean进行管理。

3.3 Java代码方式

在Java配置类中,可以使用@Bean注解定义Bean的配置,例如:

@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService(userDao());}@Beanpublic UserDao userDao() {return new UserDao();}
}

上述代码定义了两个Bean,分别是userServiceuserDao。Spring容器会自动调用这些方法,将它们返回的对象注册为Bean。

3.4 对象之间调用示例

员工和部门之间关系:
Dept实体类:

public class Dept implements Serializable {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Dept{" +"name='" + name + '\'' +'}';}
}

员工实体类:

public class Employee implements Serializable {private String name;private String sex;//员工隶属于哪个部门private  Dept dept;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", sex='" + sex + '\'' +", dept=" + dept +'}';}
}

配置 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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--第一种调用--><bean id="employee" class="com.kdx.entity.Employee"><property name="name" value="kdx"/><property name="sex" value=""/><property name="dept"><bean id="dept" class="com.kdx.entity.Dept"><property name="name" value="开发"></property></bean></property></bean><!--第二种调用--><bean id="employee1" class="com.kdx.entity.Employee"><property name="name" value="sun"/><property name="sex" value=""/><property name="dept" ref="dept1"/></bean><bean id="dept1" class="com.kdx.entity.Dept"><property name="name" value="开发"></property></bean>
</beans>

测试:

public class DeptEmployeeTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");Employee employee = applicationContext.getBean("employee", Employee.class);System.out.println(employee);Employee employee1 = applicationContext.getBean("employee1", Employee.class);System.out.println(employee1);}
}

运行结果:

Employee{name='kdx', sex='男', dept=Dept{name='开发'}}
Employee{name='sun', sex='男', dept=Dept{name='开发'}}

3.5 自动装配

在Spring框架中,autowire 是一种自动装配(autowiring)的机制,用于自动处理bean之间的依赖关系。当一个bean的某个属性需要另一个bean的引用时,Spring可以根据指定的autowire模式,在容器中自动查找匹配的bean并将其注入。

在Spring中,autowire 属性可以在bean的配置中使用,以下是几种常用的autowire模式:

  1. no(默认值):默认值,不使用自动装配。所有的依赖关系需要手动指定。

    <bean id="beanA" class="com.example.BeanA" autowire="no"><!-- 其他配置 -->
    </bean>
    
  2. byName:根据属性名进行自动装配。Spring容器会查找和属性名相同的bean,并将其注入。

    <bean id="beanA" class="com.example.BeanA" autowire="byName"><!-- 其他配置 -->
    </bean>
    
  3. byType:根据属性类型进行自动装配。Spring容器会查找和属性类型匹配的bean,并将其注入。如果存在多个匹配的bean,会抛出异常。

    <bean id="beanA" class="com.example.BeanA" autowire="byType"><!-- 其他配置 -->
    </bean>
    
  4. constructor:根据构造函数参数类型进行自动装配。类似于byType,但是应用于构造函数参数。

    <bean id="beanA" class="com.example.BeanA" autowire="constructor"><!-- 其他配置 -->
    </bean>
    
  5. autodetect:根据属性名和类型进行自动装配。如果找到符合名称和类型的bean,则按照byName进行装配,如果找不到,则按照byType进行装配。

    <bean id="beanA" class="com.example.BeanA" autowire="autodetect"><!-- 其他配置 -->
    </bean>
    

使用autowire可以减少配置文件中的冗余,但是在大型应用中,谨慎使用自动装配,以避免因为自动装配引发的意外错误。

还是员工和部门的例子:
配置 ApplicationContext.xml:

	<bean id="employee" class="com.kdx.entity.Employee" autowire="byName"><property name="name" value="kong"/><property name="sex" value="男"/></bean><bean id="dept" class="com.kdx.entity.Dept"><property name="name" value="开发部"/></bean>

测试:

public class EmpTest {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");Employee employee = ac.getBean("employee ", Employee .class);System.out.println(employee );}
}

运行结果:

Employee{name='kong', sex='男', dept=Dept{name='开发部'}}

4 单例和多例

在Spring中,Bean(组件)可以被配置为单例(Singleton)或多例(Prototype)的作用域。

4.1 单例(Singleton)

  1. 单例模式:在单例模式下,Spring容器中只会存在一个实例(对象)的拷贝。无论有多少个客户端请求,Spring容器每次都返回相同的实例。

Spring容器创建对象默认是单例模式。

  1. 特点

    • 在应用程序的整个生命周期内,只创建一个Bean的实例。
    • 默认的作用域是单例,如果不显式指定作用域,Spring会将Bean配置为单例。
    • 单例模式可以提高性能,因为只有一个实例需要被创建和管理。
  2. 配置方式:在XML配置文件中,可以使用以下方式将Bean配置为单例:

    <bean id="user" class="com.kdx.entity.User" scope="singleton"></bean>
    

测试:

public class UserTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");User user1 = applicationContext.getBean("user", User.class);User user2 = applicationContext.getBean("user", User.class);System.out.println(user1 == user2);}
}

打断点进行调试:
在这里插入图片描述
结果表明 Spring 容器每次都返回相同的实例。

4.2 多例(Prototype)

  1. 多例模式:在多例模式下,每次客户端请求Bean时,Spring容器都会创建一个新的实例。

  2. 特点

    • 每次请求时,都会创建一个新的Bean实例。
    • 每个客户端请求都会得到一个独立的Bean实例。
  3. 配置方式:在XML配置文件中,可以使用以下方式将Bean配置为多例:

    <bean id="myBean" class="com.kdx.entity.MyBean" scope="prototype"> </bean>
    

测试:

public class UserTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");User user1 = applicationContext.getBean("user", User.class);User user2 = applicationContext.getBean("user", User.class);System.out.println(user1 == user2);}
}

同样的代码,打断点调试:
在这里插入图片描述
结果表明每次客户端请求 Bean 时,Spring 容器都会创建一个新的实例。并且加载 Spring 配置文件不会创建对象,在调用getBean方法创建多实例对象。

5 生命周期

生命周期就是从对象的创建到对象的销毁的过程。

Student实体类:

public class Student implements Serializable {private String name;public Student() {System.out.println("1.执行无参构造方法");}public String getName() {return name;}public void setName(String name) {System.out.println("2.执行set方法设置属性值");this.name = name;}public void initMethod(){System.out.println("3.执行初始化方法");}public void destroyMethod(){System.out.println("5.执行销毁方法");}
}

配置 ApplicationContext.xml:

<bean id="student" class="com.kdx.entity.Student" init-method="initMethod" destroy-method="destroyMethod"><property name="name" value="kong"/>
</bean>

测试类:

public class StudentTest {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");Student student = ac.getBean("student", Student.class);System.out.println("4.获取bean的实例对象");//销毁bean((ClassPathXmlApplicationContext) ac).close();}
}

运行结果:

1.执行无参构造方法
2.执行set方法设置属性值
3.执行初始化方法
4.获取bean的实例对象
5.执行销毁方法

生命周期的后置处理器:BeanPostProcessor

public interface BeanPostProcessor {//在 Bean 的初始化方法(例如在 init-method 指定的方法)执行之前调用。@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}//在 Bean 的初始化方法执行之后调用。@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}

将上面演示生命周期的部分加上初始化前后操作
MyBean:

public class MyBean implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("---初始化之前执行的方法---");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("---初始化之后执行的方法---");return bean;}
}

配置 ApplicationContext.xml:

<bean id="myBean" class="com.kdx.entity.MyBean"></bean>

再次运行测试类结果:

1.执行无参构造方法
2.执行set方法设置属性值
---初始化之前执行的方法---
3.执行初始化方法
---初始化之后执行的方法---
4.获取bean的实例对象
5.执行销毁方法

6 注解管理Bean

在Spring框架中,可以使用注解来管理Bean。以下是几个常用的注解:

6.1 @Component、@Service、@Repository和@Controller

这些注解用于标识类为Spring的Bean,并由Spring进行管理。它们的作用是相同的,只是在语义上有所区别:

  • @Component:通用的组件注解,用于标识任意的Spring组件。

    @Component
    public class MyComponent {// 类定义
    }
    
  • @Service:用于标识业务逻辑层的Bean。

    @Service
    public class MyService {// 类定义
    }
    
  • @Repository:用于标识数据访问层(DAO)的Bean。

    @Repository
    public class MyRepository {// 类定义
    }
    
  • @Controller:用于标识控制器层的Bean(Spring MVC中使用)。

    @Controller
    public class MyController {// 类定义
    }
    

6.2 @Autowired

@Autowired 用于自动装配Bean,可以标注在构造方法、Setter方法、字段或者方法上。

  • 在构造方法上使用:

    @Service
    public class MyService {private MyRepository repository;@Autowiredpublic MyService(MyRepository repository) {this.repository = repository;}
    }
    
  • 在Setter方法上使用:

    @Service
    public class MyService {private MyRepository repository;@Autowiredpublic void setRepository(MyRepository repository) {this.repository = repository;}
    }
    
  • 在字段上使用:

    @Service
    public class MyService {@Autowiredprivate MyRepository repository;
    }
    
  • 在方法上使用:

    @Service
    public class MyService {private MyRepository repository;@Autowiredpublic void setRepository(MyRepository repository) {this.repository = repository;}
    }
    

7 AOP

AOP(面向切面编程)是一种编程范式,它允许将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,使得代码更加模块化、易于维护和扩展。横切关注点通常包括日志、事务管理、安全性、缓存、性能监控等。在AOP中,这些关注点被称为切面(Aspect),它们可以被模块化地织入到应用程序的不同部分,而不需要修改这些部分的源代码。

7.1 几个要点

  • 切面(Aspect) 是动作,把通知应用到切入点的过程称为切面。

  • 通知(Advice):定义了在何时(例如方法执行前、方法执行后)执行横切逻辑的代码。通知类型包括前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)。

    public class MyAspect {// 前置通知public void beforeAdvice() {// 在方法执行前执行的逻辑}// 后置通知public void afterReturningAdvice() {// 在方法执行后(正常返回时)执行的逻辑}// 异常通知public void afterThrowingAdvice(Exception ex) {// 在方法抛出异常时执行的逻辑}// 最终通知public void afterAdvice() {// 在方法执行后(无论正常返回还是抛出异常时)执行的逻辑}// 环绕通知public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 在方法执行前后执行的逻辑Object result = joinPoint.proceed();// 在方法执行后(正常返回时)执行的逻辑return result;}
    }
    
  • 切点(Pointcut):定义了在哪些连接点(例如方法调用、方法执行等)应用通知。切点使用表达式指定连接点的规则。

    @Aspect
    public class MyAspect {@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}@Before("serviceMethods()")public void beforeServiceMethods() {// 前置通知逻辑}
    }
    
  • 连接点(Join Point): 是在程序执行过程中能够插入切面的点。通俗地说,连接点就是程序中可能会被拦截到的点,比如方法调用、方法执行、异常抛出等。在AOP中,切点(Pointcut)用于定位连接点。也可以说类里面可以被增强的方法称为连接点。

  • public void myMethod() {// 连接点}
    
  • 切入点表达式
    作用:知道哪个类中的哪个方法被增强 
    格式:execution([权限修饰符][返回值类型][类全路径][方法名][参数列表])

    对 cn.kdx.dao.BookDao 类里面的add方法进行增强 
    execution(* cn.kdx.dao.BookDao.add(..))对 cn.kdx.dao.BookDao 类里面的所有方法进行增强 
    execution(* cn.kdx.dao.BookDao.*(..))对 cn.kdx.dao 包里面的所有类中所有方法进行增强 
    execution(* cn.kdx.dao.*.*(..))
    

7.2 通知类型

  • 前置通知(Before):在方法执行前执行的通知。

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {// 前置通知逻辑
    }
    
  • 后置通知(AfterReturning):在方法正常返回后执行的通知。

    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void afterReturningAdvice(Object result) {// 后置通知逻辑
    }
    
  • 异常通知(AfterThrowing):在方法抛出异常后执行的通知。

    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void afterThrowingAdvice(Exception ex) {// 异常通知逻辑
    }
    
  • 最终通知(After):在方法执行后(无论正常返回还是抛出异常时)执行的通知。

    @After("execution(* com.example.service.*.*(..))")
    public void afterAdvice() {// 最终通知逻辑
    }
    
  • 环绕通知(Around):在方法执行前后都可以自定义逻辑的通知。

    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 在方法执行前的逻辑Object result = joinPoint.proceed();// 在方法执行后的逻辑return result;
    }
    

7.3 配置AOP

在Spring中,配置AOP主要使用XML配置或者注解配置。

7.3.1 使用XML配置AOP的示例

User类

public class User implements Serializable {public void show(){System.out.println("showing...");}
}

UserProxy增强类:

public class UserProxy{public void before(){System.out.println("前置通知");}public void afterReturning(){System.out.println("后置通知");}public void after(){System.out.println("最终通知");}public void afterThrowing(){System.out.println("异常通知");}public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{System.out.println("环绕之前通知");proceedingJoinPoint.proceed();System.out.println("环绕之后通知");}
}

配置 ApplicationContext.xml:

	<bean id="user" class="com.kdx.entity.User"></bean><bean id="userProxy" class="com.kdx.entity.UserProxy"></bean><aop:config><!--切入点--><aop:pointcut id="p" expression="execution(* com.kdx.entity.User.show(..))"/><!--切面--><aop:aspect ref="userProxy"><!--前置增强--><aop:before method="before" pointcut-ref="p"></aop:before><!--后置增强--><aop:after-returning method="afterReturning" pointcut-ref="p"></aop:after-returning><!--最终增强--><aop:after method="after" pointcut-ref="p"></aop:after><!--异常增强--><aop:after-throwing method="afterThrowing" pointcut-ref="p"></aop:after-throwing><!--环绕增强--><aop:around method="around" pointcut-ref="p"></aop:around></aop:aspect></aop:config>

测试:

public class UserTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");User user = applicationContext.getBean("user", User.class);user.show();}
}

运行结果:

前置通知
环绕之前通知
showing...
环绕之后通知
最终通知
后置通知

7.3.2 使用注解配置AOP的示例

Student类:

@Service
public class Student implements Serializable {public void study(){System.out.println("studying...");}
}

StudentProxy增强类:

@Service
@Aspect
public class StudentProxy {/*抽取相同代码*/@Pointcut(value = "execution(* com.kdx.domain.Student.study(..))")public void proxy(){}@Before(value = "proxy()")public void before(){System.out.println("前置通知");}@AfterReturning(value = "proxy()")public void afterReturning(){System.out.println("后置通知");}@After(value = "proxy()")public void after(){System.out.println("最终通知");}@AfterThrowing(value = "proxy()")public void afterThrowing(){System.out.println("异常通知");}@Around(value = "proxy()")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{System.out.println("环绕之前通知");proceedingJoinPoint.proceed();System.out.println("环绕之后通知");}
}

配置 ApplicationContext.xml:

	<!--扫描--><context:component-scan base-package="com.kdx.domain"></context:component-scan><!--开启Aspect生成代理对象--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试:

public class StudentTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext1.xml");Student student = applicationContext.getBean("student", Student.class);student.study();}
}

运行结果:

环绕之前通知
前置通知
studying...
环绕之后通知
最终通知
后置通知

8 JdbcTemplate

JdbcTemplate 是 Spring 框架提供的一个简化 JDBC 开发的模板类。它封装了使用 JDBC 进行数据库操作的常见任务,如查询、更新、删除等,使得开发者无需编写冗长的 JDBC 代码,提高了代码的简洁性和可读性。

8.1 配置数据源和 JdbcTemplate

配置 ApplicationContext.xml:

	<!--配置数据源--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="jdbc:mysql:///mybatis"/><property name="username" value="root"/><property name="password" value="kdx010908"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/></bean><!--配置jdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><!--启用自动扫描--><context:component-scan base-package="com.kdx"></context:component-scan>

8.2 使用 JdbcTemplate 进行数据库操作

public interface UserDao {List<User> findAll();int addUser(User user);int updateUser(User user);int deleteUser(int id);
}
@Service
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic List<User> findAll() {String sql = "select * from user";BeanPropertyRowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);List<User> userList = jdbcTemplate.query(sql, mapper);return userList;}@Overridepublic int addUser(User user) {String sql = "insert into user (username) values(?)";Object [] args = {user.getUsername()};int rows = jdbcTemplate.update(sql, args);return rows;}@Overridepublic int updateUser(User user) {String sql = "update user set username = ? where id = ?";Object [] args = {user.getUsername(),user.getId()};int rows = jdbcTemplate.update(sql, args);return rows;}@Overridepublic int deleteUser(int id) {String sql = "delete from user where id = ?";int rows = jdbcTemplate.update(sql,id);return rows;}
}

在上面的示例中,JdbcTemplate 的 query 方法用于查询结果集,update 方法用于更新、插入和删除操作。JdbcTemplate 会处理 SQL 的执行、异常捕获和资源释放等繁琐的操作,使得我们只需关注 SQL 语句和数据映射即可。

9 事务管理

在Spring框架中,事务管理是一项关键的功能,它可以确保一组操作要么全部成功执行,要么全部回滚到初始状态。

9.1 事务的传播行为

在Spring框架中,事务的传播行为定义了在方法调用链中的多个方法都具有事务性时,这些事务应该如何交互。

Spring框架定义了七种事务传播行为:

  1. REQUIRED(默认): 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常见的传播行为,也是默认的行为。

  2. SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。

  3. MANDATORY: 必须在一个已有的事务中执行,否则抛出异常。如果当前没有事务,则抛出异常。

  4. REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则将当前事务挂起。新的事务与外部事务无关,是独立的。

  5. NOT_SUPPORTED: 以非事务的方式执行,如果当前存在事务,则将当前事务挂起。

  6. NEVER: 以非事务的方式执行,如果当前存在事务,则抛出异常。

  7. NESTED: 如果当前存在事务,则在嵌套事务内执行。嵌套事务可以独立提交或回滚,但是外部事务的提交或回滚会影响到所有嵌套事务。

在使用@Transactional注解时,可以设置propagation属性来指定事务的传播行为。例如:

@Transactional(propagation = Propagation.REQUIRED)
public void method() { 
}

在XML配置中,可以这样设置传播行为:

<tx:method name="method" propagation="REQUIRED"/>

9.2 事务的隔离级别

Spring框架支持四种事务隔离级别,用来控制并发事务执行时的相互影响程度。这些事务隔离级别在标准的JDBC事务中也是适用的:

  1. READ_UNCOMMITTED(读未提交): 允许读取还未提交的事务的修改。可能会导致脏读、不可重复读和幻读的问题。

  2. READ_COMMITTED(读已提交): 确保一个事务只能读取到已提交的事务所做的修改。避免了脏读,但是可能会出现不可重复读和幻读的问题。

  3. REPEATABLE_READ(可重复读)(默认) 确保一个事务在多次读取同一数据行时,能够看到同样的数据版本。避免了脏读和不可重复读,但是可能会出现幻读的问题。

  4. SERIALIZABLE(串行化): 最高的隔离级别,在事务执行期间,其他事务无法并发执行。避免了脏读、不可重复读和幻读,但是性能较差,因为事务是串行执行的。

在Spring事务管理中,可以使用@Transactional注解的isolation属性来设置事务的隔离级别。例如:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void method() {    
}

在XML配置中,可以这样设置隔离级别:

<tx:method name="method" isolation="READ_COMMITTED"/>

事务隔离级别效果:
在这里插入图片描述

9.3 案例:银行转账

A和B原本都有1000元,A向B转账100元,数据库对应操作A-100元,B+100元

Bank 实体类:

public class Bank implements Serializable {private Integer id;private String username;private Integer money;public Bank() {}public Bank(Integer id, String username, Integer money) {this.id = id;this.username = username;this.money = money;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getMoney() {return money;}public void setMoney(Integer money) {this.money = money;}
}

BankDao接口:

public interface BankDao {//加钱void addMoney();//减钱void reduceMoney();
}

9.3.1 基于注解的声明式事务管理

1.在实体类Bank上面加上注解 @Service
2.BankDaoImpl实现BankDao接口

@Service
public class BankDaoImpl implements BankDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void addMoney() {String sql = "update bank set money = money + ? where id = ?";jdbcTemplate.update(sql,100,1);}@Overridepublic void reduceMoney() {String sql = "update bank set money = money - ? where id = ?";jdbcTemplate.update(sql,100,2);}
}

3.BankService业务逻辑

@Service
public class BankService {@Autowiredprivate BankDao bankDao;@Transactional(propagation = Propagation.REQUIRED,//传播行为isolation = Isolation.REPEATABLE_READ,//隔离级别readOnly = false //不是只读timeout = 1000, //超时rollbackFor = Exception.class) //回滚public void accountMoney(){bankDao.reduceMoney();System.out.println("-------");//发生异常,事务回滚System.out.println(1/0);bankDao.addMoney();System.out.println("转账成功!");}
}

如果注解中没有配置rollbackFor = Exception.class,则当发生异常时,事务不会进行回滚,数据库数据出现异常,只有A减了100元,而B还是1000元。

4.配置 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:p="http://www.springframework.org/schema/p"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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!--配置数据源--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="jdbc:mysql:///mybatis"/><property name="username" value="root"/><property name="password" value="kdx010908"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/></bean><!--配置jdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><!--开启扫描--><context:component-scan base-package="com.kdx"></context:component-scan><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!--开启注解事务--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

5.测试

public class BankServiceTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");BankService bankService = applicationContext.getBean("bankService", BankService.class);bankService.accountMoney();}
}

由于发生异常,事务回滚,所以数据库没有发生变化。

9.3.2 基于XML配置的声明式事务管理

1.BankDao1Impl 实现 BankDao 接口

public class BankDao1Impl implements BankDao {private JdbcTemplate jdbcTemplate;public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;}public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic void addMoney() {String sql = "update bank set money = money + ? where id = ?";jdbcTemplate.update(sql,100,3);}@Overridepublic void reduceMoney() {String sql = "update bank set money = money - ? where id = ?";jdbcTemplate.update(sql,100,4);}
}

2.BankService1

public class BankService1 {private BankDao bankDao;public BankDao getBankDao() {return bankDao;}public void setBankDao(BankDao bankDao) {this.bankDao = bankDao;}public void accountMoney1(){bankDao.reduceMoney();System.out.println("-------");//发生异常,事务回滚System.out.println(1/0);bankDao.addMoney();System.out.println("转账成功!");}
}

3.配置 ApplicationContext1.xml

	<!--配置数据源--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="jdbc:mysql:///mybatis"/><property name="username" value="root"/><property name="password" value="kdx010908"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/></bean><!--配置jdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="bankDao1" class="com.kdx.dao.impl.BankDao1Impl"><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean><bean id="bankService1" class="com.kdx.service.BankService1"><property name="bankDao" ref="bankDao1"></property></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!--配置通知--><tx:advice id="advice"><!--配置通知参数--><tx:attributes><tx:method name="accountMoney1" propagation="REQUIRED" rollback-for="Exception.class"/></tx:attributes></tx:advice><!--配置切入点和切面--><aop:config><!--配置切点--><aop:pointcut id="p" expression="execution(* com.kdx.service.BankService1.accountMoney1(..))"/><!--配置切面--><aop:advisor advice-ref="advice" pointcut-ref="p"></aop:advisor></aop:config>

在上述配置中,transactionManager是事务管理器,advice是事务通知器,它定义了哪些方法(这里是accountMoney1方法)需要进行事务管理。

若没有配置<tx:method rollback-for="Exception.class"/>,则当发生异常时,事务不会进行回滚,数据库数据出现异常,只有A减了100元,而B还是1000元。

4.测试

public class BankService1Test {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext1.xml");BankService1 bankService1 = applicationContext.getBean("bankService1", BankService1.class);bankService1.accountMoney1();}
}

由于发生异常,事务回滚,所以数据库没有发生变化。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据可视化】—大屏数据可视化展示

【数据可视化】—大屏数据可视化展示 一、数据可视化 数据可视化的目的&#xff1a;借助于图形化工具&#xff0c;清晰有效的传达与沟通信息。 数据可视化可以把数据从冰冷的数字转换成图形&#xff0c;揭示蕴含在数据中的规律和道理。 二、 免费数据可视化库 Echarts 百度…

stable-diffusion-webui sdxl模型代码分析

采样器这块基本都是用的k-diffusion&#xff0c;模型用的是stability的原生项目generative-models中的sgm&#xff0c;这点和fooocus不同&#xff0c;fooocus底层依赖comfyui中的models&#xff0c;comfy是用load_state_dict的方式解析的&#xff0c;用的load_checkpoint_guess…

【从零开始学习Redis | 第三篇】在Java中操作Redis

前言&#xff1a; 本文算是一期番外&#xff0c;介绍一下如何在Java中使用Reids &#xff0c;而其实基于Java我们有很多的开源框架可以用来操作redis&#xff0c;而我们今天选择介绍的是其中比较常用的一款&#xff1a;Spring Data Redis 目录 前言&#xff1a; Spring Data…

萝卜刀真的太危险了,于是我用Cocos做了一个

点击上方亿元程序员关注和★星标 引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 昨天&#xff0c;我女儿和我说想买一把萝卜刀&#xff0c;众所周知&#xff0c;萝卜刀在潜意识当中是存在一定的危险的&#xff0c;所以我果断拒绝了&…

安防视频监控EasyCVR视频汇聚平台与萤石云平台的适配方案分析

随着科技的不断发展&#xff0c;互联网技术逐渐深入到我们生活的各个领域。其中&#xff0c;安防监控领域受益于互联网技术的发展&#xff0c;逐渐呈现出智能化、高清化、远程化的趋势。本文将介绍一种基于萤石云与EasyCVR平台的安防视频监控解决方案&#xff0c;以满足用户对安…

MTK6877/MT6877天玑900安卓5G核心板_安卓开发板主板定制开发

2021年5月13日&#xff0c;MediaTek 宣布发布旗下的天玑900系列芯片&#xff0c;又名MT6877。天玑900基于6nm先进工艺制造&#xff0c;搭载硬件级4K HDR视频录制引擎&#xff0c;支持1.08亿像素摄像头、5G双全网通和Wi-Fi 6连接、旗舰级存储规格和120Hz的FHD超高清分辨率显示&a…

凉鞋的 Unity 笔记 201. 第三轮循环:引入变量

201. 第三轮循环&#xff1a;引入变量 在这一篇&#xff0c;我们进行第三轮 编辑-测试 循环。 在之前我们编写了 输出 Hello Unity 的脚本&#xff0c;如下: using System.Collections; using System.Collections.Generic; using UnityEngine;public class FirstGameObject …

【EI会议征稿】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于202…

半监督学习介绍(为什么半监督学习是机器学习的未来)

文章目录 半监督学习的好处半监督学习原理半监督范式总结 半监督学习是一种利用标记和未标记数据的机器学习方法。半监督学习的目标是结合监督学习和无监督学习的优点&#xff1b;利用标记数据的准确性以及未标记数据的丰富性和较低成本。半监督学习可以被认为是 监督学习&…

latex:表格水平宽度调整

解决方案 结果如下&#xff1a; 源代码如下&#xff1a; \documentclass{article} % \usepackage[utf8]{ctex} \usepackage{multirow} \usepackage{graphicx} \usepackage{booktabs} \usepackage{caption}\begin{document}\captionsetup{font{large}}\begin{table}[] \centeri…

酒店报修管理系统哪家好?设备巡检系统对酒店运营有什么帮助?

酒店报修管理系统是一款关键的软件工具&#xff0c;可以帮助酒店员工和客户更有效地管理酒店的各项运营活动。下面我们将通过问答形式&#xff0c;深入探讨酒店管理系统的特性和功效&#xff0c;以便了解它如何提升酒店员工的工作效率&#xff0c;以及如何将酒店的各个部门和员…

【论文阅读】 Cola-Dif; An explainable task-specific synthesis network

文章目录 CoLa-Diff: Conditional Latent Diffusion Model for Multi-modal MRI SynthesisAn Explainable Deep Framework: Towards Task-Specific Fusion for Multi-to-One MRI Synthesis CoLa-Diff: Conditional Latent Diffusion Model for Multi-modal MRI Synthesis 论文…