Spring框架

Spring初识

Spring简介

Spring 是一个开源的轻量级 JavaEE(现在称为 Jakarta EE)开发框架,用于构建企业级应用程序和分布式系统。它提供了一种简化和解耦应用程序组件的方式,使开发人员能够更加专注于业务逻辑的实现,而不需要过多关心底层的技术细节。Spring 框架包含多个模块,每个模块提供不同的功能,如依赖注入、面向切面编程、事务管理、Web 开发等。

Spring核心概念

  • IoC(Inversion of Control)控制反转: IoC 是 Spring 的核心思想,它通过将对象的创建、组装和管理交给 Spring 容器来实现,从而减少了代码之间的耦合。开发人员只需要声明需要的组件,而不需要自己创建和管理对象。Spring 使用依赖注入(DI)来实现 IoC。
  • DI(Dependency Injection)依赖注入: 依赖注入是 IoC 的具体实现,通过注入组件之间的依赖关系,实现了对象之间的解耦。Spring 通过注解或配置文件来声明依赖关系,然后将需要的依赖自动注入到对象中。
  • AOP(Aspect-Oriented Programming)面向切面编程: AOP 是一种编程范式,它允许开发人员将横切关注点(如日志记录、事务管理)从业务逻辑中分离出来,以便更好地维护和管理。Spring AOP 提供了切面的实现,使得开发人员能够在方法执行前、执行后或抛出异常时插入额外的逻辑。
  • Spring MVC: Spring MVC 是 Spring 框架的一个模块,用于构建 Web 应用程序。它提供了 MVC(Model-View-Controller)的架构,使开发人员能够将应用程序的不同方面进行分离,从而更好地管理和扩展。
  • Spring Boot: Spring Boot 是 Spring 生态系统中的一个子项目,旨在简化 Spring 应用程序的构建和部署。它提供了自动化配置、快速开发和微服务的支持,让开发人员能够更加轻松地创建独立的、可执行的 Spring 应用程序。
  • Spring Data: Spring Data 提供了一种统一的方式来访问和操作各种不同类型的数据源,如关系型数据库、NoSQL 数据库、搜索引擎等。它简化了数据访问层的开发,并提供了常见的 CRUD 操作以及查询的支持。
  • Spring Security: Spring Security 是 Spring 框架的安全性模块,用于实现身份验证、授权、安全配置等功能,保护应用程序免受各种安全威胁。
  • Spring Cloud: Spring Cloud 是用于构建分布式系统和微服务架构的一组工具和框架。它提供了服务注册与发现、负载均衡、断路器、配置管理等功能,帮助开发人员构建可靠的、可扩展的分布式应用。
  • Spring Integration: Spring Integration 提供了一种将不同系统和应用程序集成到一起的方式,支持消息传递、异步通信、数据转换等。

搭建Spring框架工程

  1. 新建maven项目
  2. 导入spring context jar包
<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13.2version>
        <scope>testscope>
    dependency>
dependencies>
  1. 创建bean对象
package com.acaiblog.spring.pojo;

public class User {
    private Integer id;
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    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;
    }
}
  1. 创建配置文件applicationContext.xml

<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.acaiblog.spring.pojo.User">
        <property name="id" value="1"/>
        <property name="name" value="acai"/>
    bean>
beans>
  1. 测试Spring
import com.acaiblog.spring.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        // 创建容器对象
        ApplicationContext aplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取配置bean
        User user = aplicationContext.getBean("user", User.class);
        System.out.println(user);
    }
}
  1. 测试结果
User{id=1, name='acai'}

Spring特性

  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的APi
  • 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
  • 组件化:Spring实现了简单的组件化配置组合成一个复杂的应用,在Spring中可以使用XML和JAVA注解组合这些对象
  • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库

Spring获取bean的三种方式

  • getBean(String beanId):通过beanId获取对象;缺点:需要强制类型转换,不灵活。
  • getBean(Class clazz):通过Class对象获取对象;缺点:容器中有多个bean会报错
  • getBean(String beanId,Class clazz):通过beanId和Class对象

Bean标签

在Spring Framework中,“bean” 是一个核心概念,用于定义应用程序中的各种组件、对象或服务。Spring的IoC(Inversion of Control,控制反转)容器负责管理和实例化这些bean。在XML配置文件中,使用标签来定义和配置这些bean。

Bean标签属性

属性 描述
id 表示bean的唯一标识符,应该在整个Spring上下文中是唯一的。
class 指定bean的全限定类名,用于告诉Spring容器应该实例化哪个类来创建bean对象。
scope 定义bean的作用域,常用的有singleton(单例,默认)、prototype(原型)、request、session等。例如:scope="singleton"
init-method 指定在bean初始化之后调用的方法。
destroy-method 指定在bean销毁之前调用的方法。

Bean子标签property属性

property主要是用来给类对象方法注入参数

属性 描述
name 指定bean的属性名。
value 设置基本数据类型的属性值。
ref 引用其他bean作为属性值。

Bean子标签constructor-arg属性

constructor-arg标签主要是用来给构造器注入参数

属性名称 描述
index 构造函数参数的索引(从0开始)。
type 参数的数据类型。
value 参数的值,可以是字符串、基本类型或引用。
ref 参数的引用,指向另一个bean的id。
name 参数的名称,用于指定构造函数的参数名。

SpringIOC底层实现

BeanFactory与ApplicationContext

  • BeanFactory:IOC容器的基本实现,是Spring内部的接口。
  • ApplicationContext:是BeanFactory的子接口,提供更多高级的特性。面向Spring使用者,几乎所有场合都使用ApplicationContext而不是BeanFactory

Spring数值的注入方式

  • set方法
  • 通过构造器
  • 名称空间注入

set注入

语法

<bean id="user" class="com.acaiblog.spring.pojo.User">
    <property name="id" value="1"/>
    <property name="name" value="acai"/>
bean>

构造器注入

<bean id="user" class="com.acaiblog.spring.pojo.User">
    <constructor-arg name="id" value="2"/>
    <constructor-arg name="name" value="acai"/>
bean>

名称空间注入

<bean id="user"
      class="com.acaiblog.spring.pojo.User"
      p:id="1"
      p:name="acaiblog">
bean>

外部已声明bean

如果想通过查询员工信息的同时获取员工所在部门信息就需要使用外部已声明bean

  1. 员工bean
package com.acaiblog.spring.pojo;

public class Employee {
    private Integer id;
    private String name;
    private String email;
    private Dept dept;

    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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", dept=" + dept +
                '}';
    }
}
  1. 部门bean
package com.acaiblog.spring.pojo;

public class Dept {
    private Integer id;
    private String name;

    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;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
  1. Spring配置文件
<bean id="dept1" class="com.acaiblog.spring.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="运维部"/>
bean>
<bean id="employee" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="1"/>
    <property name="name" value="acai"/>
    <property name="email" value="[email protected]"/>
    <property name="dept" ref="dept1"/>
bean>
  1. 测试代码
import com.acaiblog.spring.pojo.Employee;
import com.acaiblog.spring.pojo.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        // 创建容器对象
        ApplicationContext aplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取配置bean
        Employee employee = aplicationContext.getBean("employee", Employee.class);
        System.out.println(employee);

    }
}
  1. 测试结果
Employee{id=1, name='acai', email='[email protected]', dept=Dept{id=1, name='运维部'}} 

bean级联属性赋值

<bean id="dept1" class="com.acaiblog.spring.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="运维部"/>
bean>
<bean id="employee" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="1"/>
    <property name="name" value="acai"/>
    <property name="email" value="[email protected]"/>
    <property name="dept" ref="dept1"/>
    
    <property name="dept.name" value="测试部"/>
bean>

内部bean属性赋值

内部bean不会覆盖外部bean的属性值

<bean id="employee" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="1"/>
    <property name="name" value="acai"/>
    <property name="email" value="[email protected]"/>
    <property name="dept">
        <bean class="com.acaiblog.spring.pojo.Dept">
            <property name="id" value="2"/>
            <property name="name" value="研发部"/>
        bean>
    property>
bean>

bean List集合注入赋值

需求

比如云计算事业部,有两个员工。使用List集合的方式展示数据

  1. 在Dept pojo中新增employeeList类属性,并且新增get、set、tostring方法
public class Dept {
    private List<Employee> employeeList;
    public List<Employee> getEmployeeList() {
        return employeeList;
    }

    public void setEmployeeList(List<Employee> employeeList) {
        this.employeeList = employeeList;
    }
    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", employeeList=" + employeeList +
                '}';
    }
}
  1. 编辑bean
<bean id="zhangsan" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="1"/>
    <property name="name" value="zhangsan"/>
    <property name="email" value="[email protected]"/>
bean>
<bean id="lisi" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="2"/>
    <property name="name" value="lisi"/>
    <property name="email" value="[email protected]"/>
bean>

<bean id="dept" class="com.acaiblog.spring.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="云计算事业部"/>
    <property name="employeeList">
        <list>
            <ref bean="zhangsan"/>
            <ref bean="lisi"/>
        list>
    property>
bean>
  1. 测试代码
public class TestSpring {
    @Test
    public void test(){
        // 创建容器对象
        ApplicationContext aplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取配置bean
        Dept dept = aplicationContext.getBean("dept", Dept.class);
        System.out.println(dept);

    }
}
  1. 测试结果
Dept{id=1, name='云计算事业部', employeeList=[Employee{id=1, name='zhangsan', email='[email protected]', dept=null, empList=null}, Employee{id=2, name='lisi', email='[email protected]', dept=null, empList=null}]} 

bean Map集合注入赋值

  1. 在Dept pojo类新增map属性
public class Dept {
    private Map<String, Employee> employeeMap;
    public Map<String, Employee> getEmployeeMap() {
        return employeeMap;
    }

    public void setEmployeeMap(Map<String, Employee> employeeMap) {
        this.employeeMap = employeeMap;
    }
    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", employeeList=" + employeeList +
                ", employeeMap=" + employeeMap +
                '}';
    }
}
  1. 编辑Spring bean配置文件
<bean id="zhangsan" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="1"/>
    <property name="name" value="zhangsan"/>
    <property name="email" value="[email protected]"/>
bean>
<bean id="lisi" class="com.acaiblog.spring.pojo.Employee">
    <property name="id" value="2"/>
    <property name="name" value="lisi"/>
    <property name="email" value="[email protected]"/>
bean>

<bean id="dept" class="com.acaiblog.spring.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="云计算事业部"/>
    <property name="employeeMap">
        <map>
            <entry>
                <key><value>111value>key>
                <ref bean="zhangsan"/>
            entry>
            <entry>
                <key><value>222value>key>
                <ref bean="lisi"/>
            entry>

        map>
    property>
bean>
  1. 测试代码
public class TestSpring {
    @Test
    public void test(){
        // 创建容器对象
        ApplicationContext aplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取配置bean
        Dept dept = aplicationContext.getBean("dept", Dept.class);
        System.out.println(dept);

    }
}
  1. 测试结果
Dept{id=1, name='云计算事业部', employeeList=null, employeeMap={111=Employee{id=1, name='zhangsan', email='[email protected]', dept=null, empList=null}, 222=Employee{id=2, name='lisi', email='[email protected]', dept=null, empList=null}}} 

Spring管理第三方bean

Spring管理druid

  1. 导入jar依赖包
<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.2.18version>
dependency>
<dependency>
    <groupId>org.mariadb.jdbcgroupId>
    <artifactId>mariadb-java-clientartifactId>
    <version>3.1.4version>
dependency>
  1. 编写数据库配置文件
#db.properties
db.driver=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://acaiblog.top:3306/spring
db.username=root
db.password=123456
  1. 在beans配置文件中加载数据库配置文件
<context:property-placeholder location="classpath:db.properties">context:property-placeholder>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${db.driver}"/>
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
bean>
  1. 测试代码
public class TestSpring {
    @Test
    public void test() throws Exception{
        // 创建容器对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取bean
        DruidDataSource druidDataSource = applicationContext.getBean("dataSource", DruidDataSource.class);
        // 连接数据库对象
        DruidPooledConnection druidPooledConnection = druidDataSource.getConnection();
        System.out.println(druidPooledConnection);
    }
}

Spring FactoryBean

FactoryBean 是 Spring 框架中的一个接口,用于创建和管理 Spring 容器中的对象。它允许开发人员在创建 bean 实例时进行更加灵活和自定义的操作。通过实现 FactoryBean 接口,您可以控制 bean 的实例化、初始化和销毁过程,以及对外暴露的对象类型。

内置方法

方法 描述
getObject() 实际创建和返回 bean 实例,可以执行自定义逻辑,如从外部资源加载数据、实例化其他对象等。
getObjectType() 返回由 FactoryBean 创建的对象的类型,通常用于告诉 Spring 容器实际创建的 bean 类型。
isSingleton() 返回一个布尔值,指示由 FactoryBean 创建的对象是否是单例(singleton)或原型(prototype)。

实现步骤

  1. 重写FactoryBean方法
package com.acaiblog.spring.Factory;
import com.acaiblog.spring.pojo.Dept;
import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBean implements FactoryBean<Dept> {
    @Override
    public boolean isSingleton() {
        /**设置参数是否为单例*/
        return FactoryBean.super.isSingleton();
    }

    @Override
    public Dept getObject() throws Exception {
        /**参数对象创建的方法,比如:创建一个部门然后将部门返回*/
        Dept dept = new Dept(1,"运维部");
        return dept;
    }

    @Override
    public Class<?> getObjectType() {
        /**设置参数对象class*/
        return Dept.class;
    }
}
  1. 装配Bean
<bean id="factoryBean" class="com.acaiblog.spring.Factory.MyFactoryBean">
  1. 测试代码
public class TestSpring {
    @Test
    public void test() throws Exception{
        // 创建容器对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取bean
        Dept dept = applicationContext.getBean("factoryBean", Dept.class);
        System.out.println(dept);
    }
}
  1. 测试结果
Dept{id=1, name='运维部', employeeList=null, employeeMap=null} 

Spring Bean的作用域

语法

在 Spring 配置文件中使用 标签的 scope 属性来显式指定 Bean 的作用域。例如:

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

作用域

作用域 描述
singleton 每个 Spring 容器中只会存在一个单例的 Bean 实例。在应用程序生命周期内,只会创建一个 Bean 实例并缓存以供后续使用。
prototype 每次请求该 Bean 时,都会创建一个新的实例。不同地方请求该 Bean 会得到不同的实例。
request 在 Web 应用中有效,每个 HTTP 请求都会创建一个新的 Bean 实例,在整个请求周期内有效。不同请求间可对应不同的实例。
session 在 Web 应用中有效,每个用户会话(Session)都会创建一个新的 Bean 实例,在整个会话周期内有效。不同用户对应不同的实例。
application 在 Web 应用中有效,每个 ServletContext(Web 应用上下文)都会创建一个新的 Bean 实例,在整个应用生命周期内有效。
websocket 在 Web 应用中有效,每个 WebSocket 会话都会创建一个新的 Bean 实例,在整个 WebSocket 会话周期内有效。
custom 您可以实现自定义的作用域,通过实现 org.springframework.beans.factory.config.Scope 接口来定义更灵活的作用域。

Spring Bean生命周期

在Spring框架中,Bean的生命周期是指一个Bean从被创建到被销毁的整个过程。Spring框架提供了丰富的生命周期回调方法,可以让开发者在不同的阶段进行自定义操作。

BeanFactory接口描述

接口名称 特点
ConfigurableApplicationContext 可配置和修改应用程序上下文,包含刷新和关闭方法。
ApplicationContext 提供核心功能的只读应用程序上下文,无法在运行时修改或重新配置。

生命周期步骤

  1. 创建Student类
package com.acaiblog.spring.pojo;

public class Student {
    private Integer id;
    private String name;

    public void Init(){
        System.out.println("初始化方法");
    }
    public void Destory(){
        System.out.println("销毁方法");
    }

    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
  1. 设置Bean属性值,init-method在示例创建之前调用初始化的方法;destroy-method容器关闭之后调用销毁方法
<bean id="student" class="com.acaiblog.spring.pojo.Student" init-method="Init" destroy-method="Destory">
    <property name="id" value="1"/>
    <property name="name" value="慕容峻才"/>
bean>
  1. 测试代码
public class TestSpring {
    @Test
    public void test() throws Exception{
        // 创建容器对象使用ConfigurableApplicationContext可以关闭容器,用来自动调用销毁方法
        ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取bean
        Student student = applicationContext.getBean("student", Student.class);
        System.out.println(student);
        // 调用Bean中定义的destroy-method方法
        applicationContext.close();
    }
}
  1. 测试结果
初始化方法
Student{id=1, name='慕容峻才'}
销毁方法 

Bean的后置处理器

作用

在初始化前后对bean进行额外的处理

实现步骤

  1. 实现BeanPostProcessor接口
package com.acaiblog.spring.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * Bean初始化之前执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean初始化之前执行");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /**
     * Bean初始化之后会执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean初始化之后执行");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
  1. Bean配置文件装配后置处理器
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="student" class="com.acaiblog.spring.pojo.Student" init-method="Init" destroy-method="Destory">
        <property name="id" value="1"/>
        <property name="name" value="慕容峻才"/>
    bean>
    <bean class="com.acaiblog.spring.processor.MyBeanPostProcessor"/>
beans>

Spring基于xml自动装配

根据bean标签的autowire属性指定装配规则,不需要明确指定。Spring自动将匹配的属性值注入bean中。autowire属性值如下表:

属性名 描述
byName 按组件名称自动装配
byType 按组件类型自动装配
constructor 通过构造函数自动装配

环境搭建

  1. 创建部门接口
package com.acaiblog.spring.dao;

import com.acaiblog.spring.pojo.Dept;

public interface DeptDao {
    /**
     * 添加部门信息
     * @param dept
     */
    public void insertDept(Dept dept);
}
  1. 创建部门的实现类
package com.acaiblog.spring.dao.Impl;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;

public class DeptDaoImpl  implements DeptDao {
    @Override
    public void insertDept(Dept dept) {
        System.out.println("添加部门成功");
    }
}
  1. 创建部门的service
package com.acaiblog.spring.service;

import com.acaiblog.spring.pojo.Dept;

public interface DeptService {
    /**
     * 添加部门信息
     * @param dept
     */
    public void save(Dept dept);
}
  1. 创建部门service的实现类
package com.acaiblog.spring.service.Impl;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;

public class DeptServiceImpl implements DeptService {
    private DeptDao deptDao;
    @Override
    public void save(Dept dept) {
        deptDao.insertDept(dept);
    }
}
  1. 测试代码
public class TestSpring {
    @Test
    public void test() throws Exception{
        // 创建容器对象使用ConfigurableApplicationContext可以关闭容器,用来自动调用销毁方法
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
        // 获取bean
        DeptService deptService = applicationContext.getBean("deptService", DeptService.class);
        deptService.saveDept(new Dept());
    }
}

byName自动装配

对象中属性与容器中的beanId进行匹配,如果属性名和beanId匹配成功则自动装配成功

<bean id="deptDao" class="com.acaiblog.spring.dao.Impl.DeptDaoImpl">bean>
<bean id="deptService" class="com.acaiblog.spring.service.Impl.DeptServiceImpl" autowire="byName">bean>

byType自动装配

对象中的属性类型与容器中bean的class进行匹配,如果唯一匹配则自动装配成功

<bean id="deptDao" class="com.acaiblog.spring.dao.Impl.DeptDaoImpl">bean>
<bean id="deptService" class="com.acaiblog.spring.service.Impl.DeptServiceImpl" autowire="byType">bean>

总结

  • 基于xml的自动装配,底层使用set注入
  • 不建议使用byName、byType,建议实现注解的方式实现自动装配

Spring中的注解

搭建环境

创建spring工程day07_spring导入spring相关依赖包

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13.2version>
        <scope>testscope>
    dependency>
dependencies>

创建Employee pojo

package com.acaiblog.spring.pojo;

import java.util.List;

public class Employee {
    private Integer id;
    private String name;
    private String email;
    private Dept dept;

    private List<Employee> empList;

    public List<Employee> getEmpList() {
        return empList;
    }

    public void setEmpList(List<Employee> empList) {
        this.empList = empList;
    }

    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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", dept=" + dept +
                ", empList=" + empList +
                '}';
    }
}

创建dept pojo

package com.acaiblog.spring.pojo;

import java.util.List;
import java.util.Map;

public class Dept {
    private Integer id;
    private String name;
    private List<Employee> employeeList;
    private Map<String, Employee> employeeMap;


    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", employeeList=" + employeeList +
                ", employeeMap=" + employeeMap +
                '}';
    }

    public Map<String, Employee> getEmployeeMap() {
        return employeeMap;
    }

    public void setEmployeeMap(Map<String, Employee> employeeMap) {
        this.employeeMap = employeeMap;
    }

    public List<Employee> getEmployeeList() {
        return employeeList;
    }

    public void setEmployeeList(List<Employee> employeeList) {
        this.employeeList = employeeList;
    }

    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;
    }

}

创建Dept接口

package com.acaiblog.spring.dao;

import com.acaiblog.spring.pojo.Dept;

public interface DeptDao {
    /*
    * 新增部门信息*/
    public void insertDept(Dept dept);
}

创建DeptDaoImpl

package com.acaiblog.spring.dao.impI;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;

public class DeptDaoImpl implements DeptDao {

    @Override
    public void insertDept(Dept dept) {
        System.out.println("添加部门成功");
    }
}

创建DeptService

package com.acaiblog.spring.service;

import com.acaiblog.spring.pojo.Dept;

public interface DeptService {
    /*
    * 添加部门*/
    public void saveDept(Dept dept);
}

创建DeptServiceImpl

package com.acaiblog.spring.service.impl;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;

public class DeptServiceImpl implements DeptService {
    private DeptDao deptDao;
    @Override
    public void saveDept(Dept dept) {
        deptDao.insertDept(dept);
    }
}

创建DeptController

package com.acaiblog.spring.controller;

import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;

public class DeptController {
    private DeptService deptService;
    public void saveDept(){
        deptService.saveDept(new Dept());
    }
}

创建spring配置文件


<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="deptDao" class="com.acaiblog.spring.dao.impI.DeptDaoImpl">bean>
beans>

创建测试代码,验证环境没有问题

import com.acaiblog.spring.dao.impI.DeptDaoImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        DeptDaoImpl deptDao = context.getBean("deptDao", DeptDaoImpl.class);
        System.out.println("deptDao:" + deptDao);
    }
}

使用注解将对象装配到IOC容器中

注解的位置: 在类的上面标识
使用注解装配:默认将类名首字母小写作为bean id;可以使用value属性为类的bean id指定id;当注解中只使用一个value属性时,value关键字可以省略
使用注解的步骤:导入相关jar包,spring默认已经导入;开启组件扫描;使用注解标识组件

注解 描述
@Autowired 自动装配依赖关系。
@Component 通用的Spring组件注解。
@Controller 标识控制器类。
@Service 标识服务类。
@Repository 标识数据访问对象(DAO)类。

注解实现步骤

  1. DeptDaoImpl中定义注解
package com.acaiblog.spring.dao.impI;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;
import org.springframework.stereotype.Repository;

@Repository("deptDao")
public class DeptDaoImpl implements DeptDao {

    @Override
    public void insertDept(Dept dept) {
        System.out.println("添加部门成功");
    }
}
  1. pojo中定义注解
package com.acaiblog.spring.pojo;

import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
@Component
public class Dept {
}
  1. service中定义注解
package com.acaiblog.spring.service.impl;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;
import org.springframework.stereotype.Service;

@Service
public class DeptServiceImpl implements DeptService {
    private DeptDao deptDao;
    @Override
    public void saveDept(Dept dept) {
        deptDao.insertDept(dept);
    }
}
  1. controller中定义注解
package com.acaiblog.spring.controller;

import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;
import org.springframework.stereotype.Controller;

@Controller
public class DeptController {
    private DeptService deptService;
    public void saveDept(){
        deptService.saveDept(new Dept());
    }
}
  1. spring配置文件设置扫描注解

<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 https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.acaiblog.spring">context:component-scan>
beans>
  1. 测试代码
import com.acaiblog.spring.controller.DeptController;
import com.acaiblog.spring.dao.impI.DeptDaoImpl;
import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.impl.DeptServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Dept dept = context.getBean("dept", Dept.class);
        DeptDaoImpl deptDao = context.getBean("deptDao", DeptDaoImpl.class);
        DeptServiceImpl deptService = context.getBean("deptServiceImpl", DeptServiceImpl.class);
        DeptController deptController = context.getBean("deptController", DeptController.class);
        System.out.println("dept:" + dept);
        System.out.println("deptDao:" + deptDao);
        System.out.println("deptService:" + deptService);
        System.out.println("deptController:" + deptController);
    }
}

使用注解装配对象中的属性[自动装配]

@Autowired

@Autowired注解作用:自动装配对象中的属性
@Autowired注解装配原理:反射机制
@Autowired注解装配方式:先byType匹配,如果匹配0个返回错误;如果匹配1个正常允许;如果匹配多个再按照byName进行唯一筛选匹配,筛选成功对象中的属性名称等同于beanId,筛选是吧对象中的属性名称比等于beanId。
@Autowired注解required属性为true,表示被标识的属性必须装配数值,如未装配会报错;属性为false,表示被标识的属性必须装配属性,如未装配不会报错。

实现步骤

添加注解

package com.acaiblog.spring.service.impl;

import com.acaiblog.spring.dao.DeptDao;
import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptDao deptDao;
    @Override
    public void saveDept(Dept dept) {
        deptDao.insertDept(dept);
    }
}

测试代码

import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;
import com.acaiblog.spring.service.impl.DeptServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        DeptService deptService = context.getBean("deptServiceImpl", DeptServiceImpl.class);
        deptService.saveDept(new Dept());
    }
}

测试结果

添加部门成功

@Qualifier

@Qualifier和@Autowired配合使用,将设置的beanId名称装配到对象属性中。

Spring组件扫描

扫描包下所有子包

<context:component-scan base-package="com.acaiblog.spring">context:component-scan>

扫描指定的多个包

<context:component-scan base-package="com.acaiblog.aaa,com.acaiblog.bbb">context:component-scan>

包含扫描


<context:component-scan base-package="com.acaiblog.spring" use-default-filters="false">
    
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    
    <context:include-filter type="assignable" expression="com.acaiblog.spring.service.impl.DeptServiceImpl"/>
context:component-scan>

排除扫描

<context:component-scan base-package="com.acaiblog.spring">

    <context:exclude-filter type="assignable" expression="org.springframework.stereotype.Controller"/>
context:component-scan>

Spring完全注解开发

@Configuration 将该注解的类代替xml配置文件
@ComponentSan 配置要扫描的包

实现步骤

创建配置类,在类上面添加注解

package com.acaiblog.spring.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.acaiblog.spring")
public class SpringConfig {
}

使用AnnotationConfigApplicationContext容器对象

import com.acaiblog.spring.config.SpringConfig;
import com.acaiblog.spring.pojo.Dept;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestSpring {
    @Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        Dept dept = context.getBean("dept", Dept.class);
        System.out.println("dept: "+ dept);
    }
}

测试结果

dept: Dept{id=null, name='null', employeeList=null, employeeMap=null}

Spring集成Junit4

导入jar包

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-testartifactId>
    <version>5.3.10version>
    <scope>testscope>
dependency>

指定Spring配置文件路径和Spring环境下运行Junit4的运行器

import com.acaiblog.spring.pojo.Dept;
import com.acaiblog.spring.service.DeptService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration(locations = "classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit4Test {
    @Autowired
    private DeptService deptService;
    @Test
    public void test(){
        deptService.saveDept(new Dept());
    }
}

Spring AOP

AOP概述

AOP:Aspect-Oriented Programming,面向切面编程;是对OOP(Object-Oriented Programming,面向对象编程)的一种扩展,是一种通过动态代理实现程序功能扩展和统一维护的技术。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提供程序的可重用行,同时提高了开发的效率。

AOP专业术语

  • 横切关注点:非核心代码,称之为横切关注点
  • 切面:将横切关注点提取到类,称之为切面类
  • 通知:将横切关注点提取到类之后,横切关注点更名为通知
  • 目标:目标对象,指的是需要被代理的对象(实现类)
  • 代理:代理对象可以理解为中转
  • 连接点:通知方法需要指定通知位置,这个位置称之为连接点(通知之前)
  • 切入点:通知方法需要指定通知位置,这个位置称之为连接点(通知之后)

AspectJ框架

AspectJ是java社区里最完整的AOP框架,在Spring2.0以上版本中可以使用基于AspectJ注解或基于xml配置的AOP。

切入点表达式

语法

@Before(value = "execution(权限修饰符 返回值类型 包名.类名.方法(参数类型))")

通配符

通配符 描述
* 可以代表任意权限修饰符和返回值类型,可以代表任意包名、类名、方法名
.. 代表任意参数类型及参数个数

重用切入点表达式

使用@PointCut注解,提取可重用的切入点表达式

@PointCut("execution(* com.acaiblog.aop.CalcImpl.*(..))")
public vold MyPoint(){}

使用方法名引入切入点表达式:

@Before(value = "MyPoint()")
public vold BeforeMethod(JoinPoint joinPoint){}

JoinPoint对象

作用

  • 获取方法名称:joinPoint.getSignature().getName();
  • 获取参数名称:joinPoint.getArgs();

AspectJ 通知

前置通知

语法:@Before
执行时机:指定方法执行之前执行,指定方法是切入点表达式设置位置

后置通知

语法:@After
执行时机:指定方法执行之后执行

返回通知

语法:@AfterReturning
执行时机:指定方法返回结果时执行
注意事项:@AfterReturning中的returning属性与方法入参参数名一致

异常通知

语法:@AfterThrowing
执行时机:指定方法出现异常时执行

环绕通知

语法:@Around
作用:整合前面4个通知
注意:参数中必须使用ProceedingJoinPoint

定义切面的优先级

语法:@Oder(value=index),index是int类型,默认值是int可存储的最大值;数值越小优先级越高

使用AspectJ框架

pom.xml添加jar包

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13.2version>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-testartifactId>
        <version>5.3.10version>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>5.3.10version>
    dependency>
dependencies>

编写applicationContext.xml配置文件


<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <context:component-scan base-package="com.acaiblog.aop">context:component-scan>

        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>

在MyLogging中配置切面类

package com.acaiblog.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Arrays;
@Component
@Aspect
public class MyLogging {
    @Before(value = "execution(public int add(int,int))")
    public static void beforeMethod(JoinPoint joinPoint){
//        获取方法名称
        String methodName = joinPoint.getSignature().getName();
//        获取参数
        Object[] args = joinPoint.getArgs();
        System.out.println("Calc:" + methodName + "方法,参数:" + Arrays.toString(args));
    }
    public static void afterMethod(String methodName, Object[] args){
        System.out.println("Calc:" + methodName + "方法,参数:" + Arrays.toString(args));
    }
}

JDBCTemplate

什么是JDBCTemplate

JDBCTemplate是一个Spring提供的小型持久化框架,简化jdbc的代码。

基本使用

pom.xml导入jar包

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-ormartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>org.mariadb.jdbcgroupId>
        <artifactId>mariadb-java-clientartifactId>
        <version>3.1.4version>
    dependency>
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druidartifactId>
        <version>1.2.19version>
    dependency>
dependencies>

resource下创建db配置文件db.properties

db.driver=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://127.0.0.1:3306/jdbc
db.username=root
db.password=123

resource下创建spring配置文件application.xml


<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">
    
    <context:property-placeholder location="classpath:db.properties">context:property-placeholder>
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>
beans>

编写测试类

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        System.out.println(jdbcTemplate);
    }
}

JDBCTemplate常用API

增加

方法 描述
update(String sql) 执行给定的 SQL 更新语句,返回受影响的行数。
update(String sql, Object... args) 执行带参数的 SQL 更新语句,返回受影响的行数。
update(String sql, SqlParameterSource paramSource) 执行带参数的 SQL 更新语句,返回受影响的行数。
batchUpdate(String sql, BatchPreparedStatementSetter pss) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。
batchUpdate(String sql, Map[] batchValues) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。

删除

方法 描述
update(String sql) 执行给定的 SQL 更新语句,返回受影响的行数。
update(String sql, Object... args) 执行带参数的 SQL 更新语句,返回受影响的行数。
update(String sql, SqlParameterSource paramSource) 执行带参数的 SQL 更新语句,返回受影响的行数。
batchUpdate(String sql, BatchPreparedStatementSetter pss) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。
batchUpdate(String sql, Map[] batchValues) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。

修改

方法 描述
update(String sql) 执行给定的 SQL 更新语句,返回受影响的行数。
update(String sql, Object... args) 执行带参数的 SQL 更新语句,返回受影响的行数。
update(String sql, SqlParameterSource paramSource) 执行带参数的 SQL 更新语句,返回受影响的行数。
batchUpdate(String sql, BatchPreparedStatementSetter pss) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。
batchUpdate(String sql, Map[] batchValues) 批量执行带参数的 SQL 更新语句,每个更新语句在事务中执行。

查询

方法 描述
query(String sql, RowMapper rowMapper) 执行给定的 SQL 查询语句,将结果映射为对象列表。
queryForObject(String sql, Class elementType) 执行给定的 SQL 查询语句,返回单个结果对象。
queryForList(String sql) 执行给定的 SQL 查询语句,返回结果列表。
queryForMap(String sql) 执行给定的 SQL 查询语句,将结果映射为键值对。
queryForList(String sql, Class elementType) 执行给定的 SQL 查询语句,将结果映射为指定类型的对象列表。
queryForMap(String sql, Object... args) 执行带参数的 SQL 查询语句,将结果映射为键值对。
queryForObject(String sql, Class elementType, Object... args) 执行带参数的 SQL 查询语句,返回单个结果对象。
queryForList(String sql, Object... args) 执行带参数的 SQL 查询语句,返回结果列表。
query(String sql, RowMapper rowMapper, Object... args) 执行带参数的 SQL 查询语句,将结果映射为对象列表。
queryForMap(String sql, SqlParameterSource paramSource) 执行带参数的 SQL 查询语句,将结果映射为键值对。
queryForObject(String sql, SqlParameterSource paramSource, Class elementType) 执行带参数的 SQL 查询语句,返回单个结果对象。
queryForList(String sql, SqlParameterSource paramSource) 执行带参数的 SQL 查询语句,返回结果列表。
query(String sql, RowMapper rowMapper, SqlParameterSource paramSource) 执行带参数的 SQL 查询语句,将结果映射为对象列表。

设计数据库表结构

create table dept(
    id int auto_increment
        primary key,
    name varchar(255) not null
);

增加数据

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        System.out.println("test");
        String sql = "insert into dept(name) values(?)";
//        jdbcTemplate自动提交事务
        jdbcTemplate.update(sql,"人事部");
    }
}

修改数据

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "update dept set name=? where id=?";
//        jdbcTemplate自动提交事务
        jdbcTemplate.update(sql,"运维部",3);
    }
}

删除数据

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "delete from dept where id = ?";
        jdbcTemplate.update(sql,2);
    }
}

批量增加

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        List<Object[]> deptList = new ArrayList<>();
        deptList.add(new Object[]{"测试部"});
        deptList.add(new Object[]{"运维部"});
        deptList.add(new Object[]{"测试部"});
        String sql = "insert into dept(name) values(?)";
        jdbcTemplate.batchUpdate(sql,deptList);
    }
}

查询

查询单个数值

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "select count(1) from dept";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        System.out.printf("员工数量: %d%n", count);
    }
}

查询单个对象

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.acaiblog.jdbc.pojo.Dept;
import org.springframework.jdbc.core.RowMapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "select name from dept where id = ?";
        RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
        Dept dept = jdbcTemplate.queryForObject(sql, rowMapper, 3);
        System.out.printf("dept: %s", dept);
    }
}

查询多个对象

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.acaiblog.jdbc.pojo.Dept;
import org.springframework.jdbc.core.RowMapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class TestSpringJDBCTemplate {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class);
        String sql = "select name from dept";
        RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
        List<Dept> dept = jdbcTemplate.query(sql, rowMapper);
        System.out.printf("dept: %s", dept);
    }
}

Spring声明式事务管理

编程式事务管理

  • 获取数据库连接Connection对象
  • 取消事务的自动提交
  • 执行操作
  • 正常完成操作时手动提交事务
  • 执行失败时回滚事务
  • 关闭相关资源

声明式事务管理【AOP管理事务】

  • 先横向提取事务管理代码,再动态置入

使用声明式事务管理

创建数据库表

create table book(
    isbn varchar(50) primary key,
    book_name varchar(100),
    price int
);
create table book_stock(
    isbn varchar(50) primary key,
    stock int
);
create table account(
    id int primary key,
    username varchar(50),
    balance int
);

pom.xml中添加依赖包

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-ormartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aspectsartifactId>
        <version>5.3.10version>
    dependency>
    <dependency>
        <groupId>org.mariadb.jdbcgroupId>
        <artifactId>mariadb-java-clientartifactId>
        <version>3.1.4version>
    dependency>
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druidartifactId>
        <version>1.2.19version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13.2version>
        <scope>testscope>
    dependency>
dependencies>

resources下创建db.properties配置文件

db.driver=org.mariadb.jdbc.Driver
db.url= jdbc:mariadb://localhost:3306/jdbc
db.username=root
db.password=123456

resources下创建spring配置文件


<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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <context:component-scan base-package="com.acaiblog.spring"/>
    
    <context:property-placeholder location="classpath:db.properties"/>
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>
    
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>
    
    <tx:annotation-driven>tx:annotation-driven>
beans>

创建图书接口

package com.acaiblog.spring.dao;

public interface BookShopDao {
//    根据图书编号查询图书价格
    public Integer findBookPriceByIsbn(String isbn);
//    根据图书编号修改图书库存
    public void updateBookStock(String isbn);
//    根据图书价格修改用户余额
    public void updateUserAccount(String username, Integer price);
}

创建图书接口扩展

package com.acaiblog.spring.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao{
    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    /**
     * 根据图书编号查询图书价格
     * @param isbn
     * @return
     */
    @Override
    public Integer findBookPriceByIsbn(String isbn){
        String sql = "select price from book where isbn = ?";
        return jdbcTemplate.queryForObject(sql, Integer.class,isbn);
    }

    /**
     * 根据图书编号修改图书库存,每次只能购买一本图书,如果图书库存小于0返回异常
     * @param isbn
     */
    @Override
    public void updateBookStock(String isbn) {
//        修改库存
        String sql = "update book_stock set stock=stock-1 where isbn = ?";
        jdbcTemplate.update(sql);
//        验证库存
        String sql2 = "select stock from book_stock where isbn = ?";
        Integer newStock = jdbcTemplate.queryForObject(sql2, Integer.class,isbn);
        if(newStock <=0){
            throw new RuntimeException("库存不足");
        }
    }

    /**
     * 根据图书价格修改用户余额,如果余额不足抛出异常
     * @param username
     * @param price
     */
    @Override
    public void updateUserAccount(String username, Integer price) {
//        查询余额是否充足
        String sql = "select balance from account where username = ?";
        Integer balance = jdbcTemplate.queryForObject(sql, Integer.class, username);
        if(balance < price){
            throw new RuntimeException("账户余额小于图书价格");
        }
//        修改用户余额
        String sql1 = "update account set balance = balance-? where username = ?";
        jdbcTemplate.update(sql1,price,username);
    }
}

声明式事务管理属性

@Transacitional注解属性

  • 事务传播行为Propagation:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播;
传播行为 描述
REQUIRED 如果当前存在事务,则加入该事务;否则,新建一个事务。
SUPPORTS 如果当前存在事务,则加入该事务;否则,以非事务方式执行。
MANDATORY 要求当前存在事务,如果不存在则抛出异常。
REQUIRES_NEW 无论当前是否存在事务,都会创建一个新的事务。
NOT_SUPPORTED 以非事务方式执行方法,如果当前存在事务,则将其挂起。
NEVER 以非事务方式执行方法,如果当前存在事务,则抛出异常。
NESTED 如果当前存在事务,则在一个嵌套的事务中执行;如果没有事务,则按REQUIRED行为创建一个新事务。
  • 事务隔离级别:一个事务与其他事务之间的隔离等级
隔离级别 描述
DEFAULT (使用数据库默认) 使用数据库的默认隔离级别(通常为数据库配置的默认级别)。
READ_UNCOMMITTED (读未提交) 允许读取尚未被其他事务提交的数据,存在脏读和不可重复读的风险。
READ_COMMITTED (读已提交) 只能读取已经被其他事务提交的数据,避免脏读,但仍可能存在不可重复读。
REPEATABLE_READ (可重复读) 确保在事务执行期间,其他事务不能修改数据,避免脏读和不可重复读。
SERIALIZABLE (串行化) 最高隔离级别,确保事务串行执行,避免脏读、不可重复读和幻读。
  • 事务超时:设置超时时间,到达设置的时间之后会强制事务回滚;类型int,单位秒;默认值-1未设置强制回滚时间
  • 事务只读:一般事务中只有查询操作时才会设置事务只读
  • 事务回滚:rollbackFor,设置回滚的异常class;norollbackFor,不设置回滚异常的class。

基于xml配置声明式事务管理


<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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

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

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

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
     bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>

    <tx:annotation-driven>tx:annotation-driven>

    <tx:advice id="tx" transaction-manager="transactionManager">

        <tx:attributes>

            <tx:method name="find*" read-only="true"/>
        tx:attributes>
    tx:advice>
beans>

你可能感兴趣的:(spring,java,后端)