文章目录

  • 1. 链接
  • 2. Spring概述
    • 2.1 Sprig框架概述
    • 2.2 入门案例
  • 3. IOC容器
    • 3.1 IOC和DI
    • 3.2IOC容器在Spring中的实现
    • 3.3举例说明IOC为什么可以降低代码耦合度
    • 3.4 bean管理操作
      • 3.4.1基于xml方式
        • 使用set方法
        • 使用有参的构造方法
        • 通过级联属性赋值
        • 通过 p 名称空间注入属性值
        • xml注入其他类型的属性
        • 对集合属性赋值
        • 集合类型的bean
      • 3.4.2基于注解方式
    • 3.5工厂bean FactoryBean
    • 3.6 bean 的高级配置
      • 3.6.1 bean的作用域
        • 单例模式
        • 原型模式
      • 3.6.2 bean的生命周期
        • spring IOC管理下的bean生命周期
        • 添加 BeanPostProcessor 后的 bean 生命周期
      • 3.6.3 xml自动装配
      • 3.6.4 读取 properties 文件
      • 3.6.5 配置信息的继承
      • 3.6.6 bean 之间的依赖
    • 3.7 基于注解方式操作bean管理
      • 3.7.1注解概述
      • 3.7.2标识组件
      • 3.7.3 扫描组件
      • 3.7.4组件装配
      • 3.7.5完全注解开发
  • 4. AOP
    • 4.1 什么是AOP
    • 4.2 底层原理
    • 4.3 JDK动态代理示例
    • 4.4 AOP术语
    • 4.5 AOP操作
      • 4.5.1 准备工作
      • 4.5.2 AspectJ注解方式
        • 基本步骤示例
        • Spring 5.2.8 版本下的测试
        • 异常情况下通知的执行顺序
        • 完全使用注解方式进行 aop 开发
        • 重用切入点定义
        • 指定切面的优先级
      • 4.5.3 AspectJ配置文件方式
  • 5. JDBCTemplate
    • 5.1 概述
    • 5.2 准备工作
    • 5.3 jdbcTemplate操作数据库
  • 6. 声明式事务管理

1. 链接

尚硅谷永远滴神
csdn大佬的笔记

2. Spring概述

2.1 Sprig框架概述

  1. Spring是轻量级的开源JavaEE框架
  2. Spring可以解决企业应用开发的复杂性
  3. Spring有两个核心部分:IOC和AOP
  • IOC:控制反转,把创建对象的过程交给Spring进行管理。是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度,其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。Spring 就是采用依赖注入的方式,为我们管理容器中的 bean 实例对象
  • AOP:面向切面,可以在不修改源代码的前提下,通过预编译方式和运行期间动态代理方式实现对原有代码的增强(添加新功能)
  1. Spring特点:
  • 方便解耦,简化开发
  • AOP编程的支持
  • 方便程序的测试
  • 方便集成各种框架
  • 降低JavaAPI开发难度
  • 方便进行事务的操作

2.2 入门案例

目标

使用 Spring 创建 bean 实例对象,并为其属性赋值

创建maven工程,引入相关依赖

因为后续测试还会创建其他 maven 工程,因此我们先创建一个 maven 父工程(spring5-learn),再创建本章对应的 maven 子工程(spring-getting-start)

在 spring-getting-start 工程中引入 spring-beansspring-contextspring-corespring-expression 的依赖,这四个依赖正是 Spring Core Container 所需的依赖,此外 Springn 还需依赖 commons-logging 实现日志功能

如下是 pom.xml 中引入的依赖配置,为了测试方便,我们引入了 spring-testjupiter 相关依赖

<dependencies><!-- spring-beans --><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.6.RELEASE</version></dependency><!-- spring-context --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.6.RELEASE</version></dependency><!-- spring-core --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.6.RELEASE</version></dependency><!-- spring-expression --><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.6.RELEASE</version></dependency><!-- commons-logging --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!-- jupiter --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test</scope></dependency><!-- spring-test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.6.RELEASE</version><scope>test</scope></dependency></dependencies>

创建实体类

public class Student {private Integer stuId;private String stuName;public Student() {}public Student(Integer stuId, String stuName) {this.stuId = stuId;this.stuName = stuName;}public Integer getStuId() {return stuId;}public void setStuId(Integer stuId) {this.stuId = stuId;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}@Overridepublic String toString() {return "Student{" +"stuId=" + stuId +", stuName='" + stuName + '\'' +'}';}
}

编写Spring配置文件


注:resource 包下的配置文件在执行时会被拷贝至类路径的根目录

<?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元素定义一个由IOC容器创建的对象 --><!-- class属性指定用于创建bean的全类名 --><!-- id属性指定用于引用bean实例的标识 --><bean id="student" class="Student"><!-- 使用property子元素为bean的属性赋值 --><property name="stuId" value="007"/><property name="stuName" value="lbw"/></bean></beans>

编写测试代码

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSpring {@Testpublic void  gettingStart() {//1.创建 IOC容器对象ApplicationContext iocContainer = new ClassPathXmlApplicationContext("getting-start.xml");//2.根据id获取bean实例对象Student student= (Student) iocContainer.getBean("student");//3.打印beanSystem.out.println(student);}
}

3. IOC容器

3.1 IOC和DI

IOC(Inversion of Control):反转控制

早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了,因为大多数应用程序都是由两个或是更多的类通过彼此的合作来实现企业逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试

在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式。比如 ClassA 中需要用到 ClassB 的对象,一般情况下,需要在 ClassA 的代码中显式的 new 一个 ClassB 的对象

反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可。采用依赖注入技术之后,ClassA 的代码只需要定义一个私有的 ClassB 对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将 ClassB 对象在外部 new 出来并注入到 A 类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如 XML)来指定.

DI(Dependency Injection):依赖注入

可以将 DI 看作是 IOC 的一种实现方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于 IOC 而言,这种表述更直接

3.2IOC容器在Spring中的实现

在通过IOC容器读取bean的实例之前,需要先将IOC容器本身实例化,Spring提供了IOC容器的两种实现方式:
1.beanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的
2. ApplicationContext:beanFactory的子接口,提供了更多高级特性。面向使用Spring的开发者

beanFactory:加载配置文件时不会创建对象,在获取对象(使用)时才会去创建对象
ApplicationContext加载配置文件时就会创建对象
在使用时更倾向于使用第二种,在服务器启动时就创建对象,而不是在操作时完成,给用户一种比较快的错觉

ctrl+h打开ApplicationContext的结构

ApplicationContext的主要实现类:
ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

在初始化时就创建单例的bean,也可以通过配置的方式指定创建的bean是多实例的

ConfigurableApplicationContext 接口:
ApplicationContext的子接口,包含一些扩展方法
refresh()close()ApplicationContext具有启动、关闭和刷新上下文的能力

WebApplicationContext 接口:
WebApplicationContext,是继承于ApplicationContext的一个接口,扩展了ApplicationContext,是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化

3.3举例说明IOC为什么可以降低代码耦合度

1.原始方式

自己new一个对象,再通过setter方法注入属性值

Student stu = new Student();
stu.setStuId(7);
stu.setStuName("Oneby");

2.进阶方式:通过工厂创建对象

可以先通过 XML 配置文件配置 bean 的属性

<bean id="student" class="Student"><property name="stuId" value="007"/><property name="stuName" value="lbw"/>
</bean>

再通过工厂模式 + 反射的方法创建该对象的实例,并注入属性值

public class StudentFactory {public static Student getStudent(){String className = ...; // 通过 XML 解析获取全类名String[] fieldNames = ..; // 通过 XML 解析获取字段名String[] fieldValues = ...; // 通过 XML 解析获取字段值Class clazz = Class.forName(className); // 通过反射创建对象实例for (int i = 0; i < fieldNames.length; i++) {// 依次为字段赋值}return clazz; // 返回创建的实例对象}
}


IOC过程:xml解析、工厂模式、反射

反射:java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射就是把java类中的各种成分映射成一个个java对象


见入门案例

3.4 bean管理操作

什么是bean管理操作

  • Spring创建对象
  • Spring注入属性

bean管理操作的两种方式

  • 基于xml配置文件
  • 基于注解方式

3.4.1基于xml方式

基于xml方式创建对象

  • 在spring的配置文件中使用bean标签,标签中添加属性,就可以实现对象创建

  • 常用属性:

    • id:唯一的标识
    • class:创建对象所在类的全路径(包类路径)
  • 创建对象时,默认也是执行无参的构造方法完成对象创建

基于xml方式注入属性

  • DI:依赖注入,就是注入属性

使用set方法

通过property标签指定属性名,spring会找到对应的setter方法,注入其属性值

使用有参的构造方法

使用constructor-arg标签为对象的属性赋值

通过级联属性赋值

添加computer类

public class Computer {String computerId;String computerName;public Computer() {}public Computer(String computerId, String computerName) {this.computerId = computerId;this.computerName = computerName;}public String getComputerId() {return computerId;}public String getComputerName() {return computerName;}public void setComputerId(String computerId) {this.computerId = computerId;}public void setComputerName(String computerName) {this.computerName = computerName;}@Overridepublic String toString() {return "Computer{" +"computerId='" + computerId + '\'' +", computerName='" + computerName + '\'' +'}';}
}

在Student类中添加Computer属性

public class Student {private Integer stuId;private String stuName;private Computer computer;public Student() {}public Student(Integer stuId, String stuName, Computer computer) {this.stuId = stuId;this.stuName = stuName;this.computer = computer;}public Integer getStuId() {return stuId;}public void setStuId(Integer stuId) {this.stuId = stuId;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public Computer getComputer() {return computer;}public void setComputer(Computer computer) {this.computer = computer;}@Overridepublic String toString() {return "Student{" +"stuId=" + stuId +", stuName='" + stuName + '\'' +", computer=" + computer +'}';}
}

演示级联属性的赋值

<?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元素定义一个由IOC容器创建的对象 --><!-- class属性指定用于创建bean的全类名 --><!-- id属性指定用于引用bean实例的标识 --><bean id="student" class="Student"><!-- 使用property子元素为bean的属性赋值 --><property name="stuId" value="007"/><property name="stuName" value="lbw"/><property name="computer" ref="computer"/><property name="computer.computerId" value="128"/><property name="computer.computerName" value="hp"/></bean><bean id="computer" class="Computer"></bean></beans>

通过 p 名称空间注入属性值

为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。Spring从2.5版本开始引入了一个新的p命名空间,可以通过元素属性的方式配置Bean的属性。使用p命名空间后,基于XML的配置方式将进一步简化。

<!--演示 p 名称空间注入属性值--><bean id="student" class="Student" p:stuId="1" p:stuName="zx"/>

xml注入其他类型的属性

  1. 字面量

在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。 --百度百科

有个不太好,但是足以说明问题的解释,那就是 对象字面量就是引号引起来的部分,必须是等号右边的部分。虽然这样的解释不太好,但是确实如此,这就是字面量。

  • 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
  • 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
  • 若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
  1. null

通过 <null/> 标签将引用类型字段的值设置为 null

<!--演示null值的使用--><bean id="student" class="Student"><property name="stuId" value="233"/><!-- 将 stuName 字段的值设置为 null --><property name="stuName"><null/></property><!-- 将 computer 字段的值设置为 null --><property name="computer"><null/></property></bean>

3.引用外部 bean

通过 <property> 标签中的 ref 属性引用外部 bean

<!-- 引用外部声明的 bean -->
<bean id="student" class="Student"><property name="stuId" value="233"/><property name="stuName" value="Oneby"/><!-- 通过 ref 属性引用外部的 bean --><property name="computer" ref="computer"/>
</bean>
<!-- 外部 bean -->
<bean id="computer" class="Computer"><property name="computerId" value="255"/><property name="computerName" value="HP"/>
</bean>
  1. 引用内部 bean

当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property><constructor-arg>元素里,不需要设置任何id或name属性,内部bean不能使用在任何其他地方

<property> 标签中不使用ref 属性引用外部 bean,而是直接定义一个 内部 bean

<!-- 引用内部声明的 bean -->
<bean id="student" class="Student"><property name="stuId" value="233"/><property name="stuName" value="Oneby"/><property name="computer"><!-- 通过 <bean> 标签定义内部 bean --><bean class="\Computer"><property name="computerId" value="255"/><property name="computerName" value="HP"/></bean></property>
</bean>

对集合属性赋值

在Spring中可以通过一组内置的XML标签来配置集合属性,比如:<array><list><set><map><props>,并且可以用过引入util名称空间来提取集合类型的 bean

新建CollectionExample 类,演示对集合属性的操作

import java.util.*;
public class CollectionExample {private String[] array;private List<String> list;private Set<String> set;private Map<String,String> map;private Properties properties;public String[] getArray() {return array;}public void setArray(String[] array) {this.array = array;}public List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}public Set<String> getSet() {return set;}public void setSet(Set<String> set) {this.set = set;}public Map<String, String> getMap() {return map;}public void setMap(Map<String, String> map) {this.map = map;}public Properties getProperties() {return properties;}public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "CollectionExample{" +"array=" + Arrays.toString(array) +", list=" + list +", set=" + set +", map=" + map +", properties=" + properties +'}';}
}

单元测试代码

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestCollectionExample {@Testpublic void  testCollection() {//1.创建 IOC容器对象ApplicationContext iocContainer = new ClassPathXmlApplicationContext("collection-property-injection.xml");//2.根据id获取bean实例对象CollectionExample collectionExample = (CollectionExample) iocContainer.getBean("collectionExample");//3.打印beanSystem.out.println(collectionExample);}
}

配置xml文件

数组
通过 <array>标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<bean id="collectionExample" class="CollectionExample"><property name="array"><array><value>zzz</value><value>xxx</value></array></property></bean>

List

通过<list>标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<bean id="collectionExample" class="CollectionExample"><property name="list"><list><value>lsit</value><value>list</value></list></property></bean>

Set

通过<set>标签定义数组集合,并且可以通过<value>指定简单的常量值,通过<ref>指定对其他bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素,甚至可以内嵌其他集合。

<bean id="collectionExample" class="CollectionExample"><property name="set"><set><value>set1</value><value>set2</value></set></property></bean>

Map

Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签,每个<entry>中包含一个键和一个值。因为键和值的类型没有限制,所以可以自由地为它们指定<value><ref><bean><null/>元素。因此对于常量型的key-value键值对可以使用key和value来定义;bean引用通过key-refvalue-ref属性定义。

<bean id="collectionExample" class="CollectionExample"><property name="map"><map><entry key="mapkey" value="mapvalue"></entry><entry key="mapkey2" value="mapvalue2"></entry></map></property></bean>

Properties

使用<props>定义java.util.Properties,该标签使用多个<prop>作为子标签,每个<prop>标签中定义key和value

<property name="properties"><props><prop key="name">Oneby</prop><prop key="hobby">code</prop></props></property>

集合类型的bean

如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用。

引入名称空间:配置集合类型的bean需要引入util名称空间
beans名称空间对应的这两项 xmlns:util="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd,将 beans 全部替换为util 就行啦~

<?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:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd">
<!-- 集合类型的 bean-->
<!--提取list集合类型属性注入-->
<util:list id="list"><value>Oneby</value><value>Heygo</value>
</util:list>
<!--提取list集合类型属性注入使用-->
<bean id="collectionExample" class="CollectionExample"><property name="list" ref="list"/>
</bean>

3.4.2基于注解方式

见 3.7

3.5工厂bean FactoryBean

Spring有两种类型的bean,一种是普通bean,另一种是工厂bean(FactoryBean)

  • 普通bean:在配置文件中定义的bean类型就是返回类型
  • 工厂bean:在配置文件中定义的bean类型可以和返回类型不一样
    • 创建类,让这个类作为工厂bean,实现接口FactoryBean
    • 实现接口内的方法,在实现的方法中定义返回的bean类型

创建StudentFactory类

import org.springframework.beans.factory.FactoryBean;public class StudentFactory implements FactoryBean<Student> {public Student getObject() throws Exception {return new Student(233, "factorybeantest",null);}public Class<?> getObjectType() {return Student.class;}public boolean isSingleton() {return false;}
}

编写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="stu" class="StudentFactory"> </bean>
</beans>

编写测试类

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestFactoryBean {@Testpublic void TestMyBean(){ApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");Student student = context.getBean("stu",Student.class);System.out.println(student);}
}


在上述的例子中,StudentFactory类继承了FactoryBean接口,返回了Student类
在xml文件中class的类型为StudentFactory
在测试中context.getBean获得的是Student类

3.6 bean 的高级配置

3.6.1 bean的作用域

在Spring中,可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。

默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例(单例对象),整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域


当bean的作用域为singleton时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象

单例模式

<!-- 演示 bean 的作用域 --><bean id="student" class="firstexample.Student" scope="singleton"><property name="stuId" value="233" /><property name="stuName" value="singleton" /></bean>
import firstexample.Student;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSingleton {@Testpublic void Test(){ApplicationContext context = new ClassPathXmlApplicationContext("singleton-or-prototype.xml");Student student1 = (Student) context.getBean("student");Student student2 = (Student) context.getBean("student");System.out.println(student1);System.out.println(student2);System.out.println(student1 == student2);}
}

程序运行结果:调用两次 getBean() 方法得到的 Student 对象是同一个实例

原型模式

<bean id="stu_prototype" class="firstexample.Student" scope="prototype"><property name="stuId" value="233" /><property name="stuName" value="prototype" /></bean>
Student student3 = (Student) context.getBean("stu_prototype");Student student4 = (Student) context.getBean("stu_prototype");System.out.println(student3);System.out.println(student4);System.out.println(student3 == student4);

程序运行结果:调用两次 getBean()方法将得到不同的 Student 对象

3.6.2 bean的生命周期

spring IOC管理下的bean生命周期

Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务,Spring IOC容器对bean的生命周期进行管理的过程:

  • 通过构造器或工厂方法创建bean实例(无参构造)
  • 为bean的属性设置值和对其他bean的引用(调用set方法)
  • 调用bean的初始化方法(需要进行配置初始化方法)
  • bean可以使用了
  • 当容器关闭时,调用bean的销毁方法(需要进行配置销毁方法)

在配置bean时,通过init-methoddestroy-method属性为bean指定初始化和销毁方法


实例代码
创建Orders类

package lifecycle;//用来演示bean生命周期
public class Orders {private String oname;public Orders() {System.out.println("第一步,执行无参构造创建bean实例");}public void setOname(String oname) {this.oname = oname;System.out.println("第二步:调用 setter 方法为属性赋值");}//  init-method 初始化方法public void initMethod() {System.out.println("第三步:执行 init-method 初始化方法");}//  destroy-method 销毁方法public void destroyMethod() {System.out.println("第五步:执行 destroy-method 初销毁方法");}
}

xml配置文件

<bean>标签中指定 order 对象的init-method 方法(初始化方法)和 destroy-method 方法(销毁方法)

<?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="orders" class="lifecycle.Orders" init-method="initMethod" destroy-method="destroyMethod"><property name="oname" value="手机"></property></bean>
</beans>

测试类

记得要关闭 IOC 容器才会执行destroy-method 方法,并且接口类型需要上升到 ConfigurableApplicationContext才会提供 close()方法

import lifecycle.Orders;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestBean {@Testpublic void Test() {//1.创建IOC容器对象ConfigurableApplicationContext iocContainer =new ClassPathXmlApplicationContext("lifecycle.xml");//2.根据id值获取bean实例对象Orders orders = iocContainer.getBean("orders", Orders.class);//3.打印beanSystem.out.println("第四步:使用创建好的 order 对象" + orders);//4.关闭IOC容器iocContainer.close();}
}

添加 BeanPostProcessor 后的 bean 生命周期

bean 的后置处理器

  • bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
  • bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
  • bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
    • postProcessBeforeInitialization(Object, String)
    • postProcessAfterInitialization(Object, String)

  • 通过构造器或工厂方法创建bean实例(无参构造)
  • 为bean的属性设置值和对其他bean的引用(调用set方法)
  • 把bean的实例传递给bean的后置管理器
  • 调用bean的初始化方法(需要进行配置初始化方法)
  • 把bean的实例传递给bean的后置管理器
  • bean可以使用了
  • 当容器关闭时,调用bean的销毁方法(需要进行配置销毁方法)

创建MyBeanPost类,继承自 MyBeanPost类,并重写其中的 postProcessBeforeInitialization 和 postProcessAfterInitialization` 方法

package lifecycle;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPost implements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("第3步:执行 postProcessBeforeInitialization 方法");return bean;}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("第5步:执行 postProcessAfterInitialization 方法");return bean;}
}

配置后置处理器

<bean id="orders" class="lifecycle.Orders" init-method="initMethod" destroy-method="destroyMethod"><property name="oname" value="手机"></property></bean><!--配置后置处理器--><bean id="myBeanPost" class="lifecycle.MyBeanPost"></bean>

测试代码

import lifecycle.Orders;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestBean {@Testpublic void Test() {//1.创建IOC容器对象ConfigurableApplicationContext iocContainer =new ClassPathXmlApplicationContext("lifecycle.xml");//2.根据id值获取bean实例对象Orders orders = iocContainer.getBean("orders", Orders.class);//3.打印beanSystem.out.println("第四步:使用创建好的 order 对象" + orders);//4.关闭IOC容器iocContainer.close();}
}

3.6.3 xml自动装配

概念

  • 手动装配:以valueref的方式明确指定属性值都是手动装配
  • 自动装配:根据指定的装配原则(属性名称或者属性类型),Spring自动将匹配的属性值注入

装配模式

  • 根据类型自动装配(byType):将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

  • 根据名称自动装配(byName):必须将目标bean的名称和属性名设置的完全相同

  • 通过构造器自动装配(constructor):当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

选用建议

相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。

代码举例

<bean id="student" class="firstexample.Student" autowire="byType"><property name="stuId" value="231113"/><property name="stuName" value="zx"/></bean><bean id="cpmputer" class="firstexample.Computer" autowire="byType"><property name="computerId" value="666"/><property name="computerName" value="HP"/></bean>

3.6.4 读取 properties 文件

为什么要使用外部properties文件

当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。

准备工作:引入数据库依赖

引入 druidmysql 的驱动

<!-- druid -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.9</version>
</dependency><!-- mysql -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.8</version>
</dependency>

配置文件

配置文件,指定数据库的用户名、用户密码、数据库连接地址、数据库驱动名称

<?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="dataSource " class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="root"/><property name="password" value="123456"/><property name="url" value="jdbc:mysql:///test"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/></bean>
</beans>

引用外部 properties 配置文件单独存放数据库配置信息

引入context名称空间

xmlns="http://www.springframework.org/schema/beans"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd复制,并将出现beans 的位置全部替换为 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">

代码测试

在类路径下创建 jdbc.properties 数据库配置文件

prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver

通过<context:property-placeholder>标签中的location 来制定配置文件的路径,classpath: 表示该配置文件位于类路径下,并通过${prop.userName}的方式来取出配置文件中的属性值

<!-- 引用外部属性文件来配置数据库连接池 -->
<!-- 指定 properties 属性文件的位置,classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="username" value="${prop.userName}"/><property name="password" value="${prop.password}"/><property name="url" value="${prop.url}"/><property name="driverClassName" value="${prop.driverClass}"/>
</bean>

3.6.5 配置信息的继承

Spring允许继承bean的配置,被继承的bean称为父bean,继承这个父bean的bean称为子bean

子bean从父bean中继承配置,包括bean的属性配置,子bean也可以覆盖从父bean继承过来的配置


注意事项:配置信息的继承

父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>abstract 属性为true,这样Spring将不会实例化这个bean


实例

配置信息的继承:Heygo 的配置信息继承于 Oneby(指定 bean 的 parent 属性),自然就获得了 Oneby 社畜的所有配置信息,只需要重写自己不一样的配置信息即可

<!-- 演示配置信息的继承 -->
<bean id="corporateSlave1" class="com.oneby.entity.CorporateSlave"><property name="id" value="1"/><property name="name" value="Oneby"/><!-- 以下都是重复的属性 --><property name="company" value="OneTech"/><property name="hobby" value="Code"/><property name="profession" value="Programer"/>
</bean><bean id="corporateSlave2" parent="corporateSlave1"><!-- 重写不同值的属性即可 --><property name="id" value="2"/><property name="name" value="Heygo"/>
</bean>

3.6.6 bean 之间的依赖

有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。

例如:要求创建Student对象的时候必须创建Computer。这里需要注意的是依赖关系不等于引用关系,Student即使依赖Computer也可以不引用它

举例一: student 对象依赖 computer 对象,但我们不创建 computer 对象

在配置文件呢中,我们只实例化student对象,并且执行其 depends-on属性等于computer,表示student对象的创建依赖于computer 对象的创建

<!-- 演示 bean 之间的依赖 -->
<bean id="student" class="com.oneby.entity.Student" depends-on="computer"><property name="stuId" value="233" /><property name="stuName" value="Oneby" />
</bean>

举例二: student 对象依赖 computer 对象,我们就创建 computer 对象

既然 student 对象依赖 computer 对象,那么我们在配置文件中创建 computer 对象

<!-- 演示 bean 之间的依赖 -->
<bean id="student" class="com.oneby.entity.Student" depends-on="computer"><property name="stuId" value="233"/><property name="stuName" value="Oneby"/>
</bean><bean id="computer" class="com.oneby.entity.Computer"/>

3.7 基于注解方式操作bean管理

3.7.1注解概述

什么是注解

  • 注解是代码的特殊标记。格式:@注解名称(属性名称=属性值,属性名称=属性值。。)
  • 注解作用在类、属性、方法上面
  • 使用注解的目的是简化xml配置

3.7.2标识组件

用于标识bean的四个注解

普通组件:@Component,用于标识一个受Spring IOC容器管理的组件

持久化层组件:@Respository,用于标识一个受Spring IOC容器管理的持久化层组件

业务逻辑层组件:@Service,用于标识一个受Spring IOC容器管理的业务逻辑层组件

表述层控制器组件:@Controller,用于标识一个受Spring IOC容器管理的表述层控制器组件


注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository@Service@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。

组件命名规则

默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id

使用组件注解的value属性指定bean的id

3.7.3 扫描组件

引入AOP依赖

引入 AOP 相关依赖,不然开启组件扫描时会报错:org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [annotation-config.xml]; nested exception is java.lang.NoClassDefFoundError: org/springframework/aop/TargetSource

<!-- spring-aop -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.6.RELEASE</version>
</dependency>

引入 context 名称空间

xmlns="http://www.springframework.org/schema/beans"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 复制,并将出现 beans的位置全部替换为 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">

开启组件扫描

开启组件扫描,并指明要扫描的包路径

<!-- 开启组件扫描 --><!--1.如果扫描多个包,多个包可以用逗号隔开2.扫描包的上层目录--><context:component-scan base-package="dao,service"/>

组件扫描的细节问题

  • base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。

  • 当需要扫描多个包时可以使用逗号分隔。

  • 如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:

<context:component-scan base-package="com.oneby" resource-pattern="autowire/*.class"/>

  • 包含与排除

    • <context:include-filter>子节点表示要包含的目标类。注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
    • <context:exclude-filter>子节点表示要排除在外的目标类
    • component-scan下可以拥有若干个include-filterexclude-filter子节点
      包扫描举例:
<!--示例1:use-default-filters="false" 表示现在不使用默认filter,自己配置filter context:include-filter用于设置扫描哪些内容(这里配置只扫描 Controller 注解) -->
<context:component-scan base-package="com.oneby" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!--示例2:下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描(这里排除 Controller 注解) -->
<context:component-scan base-package="com.oneby"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

3.7.4组件装配

组件装配的说明

项目中组件装配的需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。


组件扫描的原理
在指定要扫描的包时,<context:component-scan>元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了@Autowired@Resource@Inject注解的属性


@Autowired注解

[1]根据类型实现自动装配。

[2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解

[3]默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。

[4]若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false

[5]默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。

[6]@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。

[7]@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。

[8]@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。


@Qualifier注解

通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称,@Qualifier注解需要和上面@Autowired注解一起使用

@Autowired //根据类型进行注入
@Qualifier(value = "orderDao1") //根据bean名称进行注入
private OrderDao orderDao;

@Resource注解

可以根据类型注入,也可根据名称注入

@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。@Resource是JDK提供的注解,咱还是尽量使用Spring提供的注解吧~

解释上面那句话:如果使用@Resource则表示按照类型进行注入,我觉得等同于@Autowire的效果吧;如果使用@Resource(name="Xxx")则表示根据bean的名称进行注入

//@Resource //根据类型进行注入
@Resource(name = "orderDao1") //根据bean名称进行注入
private OrderDao orderDao;

@Inject注解

@Inject@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。奇怪了,难道 Spring 5.2.6 版本该注解被移除了吗?


@Value注解

@Value注解用于注入普通属性的值,比如@Value(value = "Oneby")表示将"Oneby"字符串注入到属性中

@Value(value = "Oneby")
private String name;

代码实例

1.创建service和dao对象,在service和dao类添加创建对象的注解

UserDao 接口,和接口的实现类UserDaoImpl

package dao;public interface UserDao {public void add();
}

DAO 层推荐使用 @Repository注解标识 bean

package dao;import org.springframework.stereotype.Repository;@Repository
public class UserDaoImpl implements UserDao{public void add() {System.out.println("dao add...");}
}

service类,推荐使用@Service

package service;import org.springframework.stereotype.Service;@Service
public class UserService {public void add() {System.out.println("service add...");}
}

2.在service层注入dao对象,在service类添加dao对象,在属性上面使用注解

package service;import dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {//定义dao类型属性@Autowiredprivate UserDao userDao;public void add() {System.out.println("service add...");userDao.add();}
}

输出

3.7.5完全注解开发

创建 SpringConfig 配置类,代替之前的 XML 配置文件

  • @Configuration 标识这是一个配置类
  • @ComponentScan(basePackages = "com.oneby")配置组件扫描
  • @Bean 用于向 IOC 容器中注入一个普通的 bean
package config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import service.UserService;@Configuration //作为配置类,替代xml文件
@ComponentScan(basePackages = {"dao" ,"service"})
public class SpringConfig {@Beanpublic UserService getOrderService(){return new UserService();}
}

测试代码

@Testpublic void test2(){ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = context.getBean("userService", UserService.class);userService.add();}

4. AOP

4.1 什么是AOP

面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率

不通过修改原代码的方式,在主干功能里添加新功能

4.2 底层原理

AOP底层使用动态代理

有两种情况的动态代理

  • 有接口:使用JDK动态代理

创建接口实现类的代理对象,增强类方法

  • 没有接口:使用CGLIB动态代理

创建子类的代理对象,增强类的方法

4.3 JDK动态代理示例

  1. 使用JDK动态代理,使用Proxy类里的方法创建代理对象
  2. 调用newProxyInstance方法
    该方法有三个参数:
    (1)类加载器
    (2)增强方法所在的类,这个类实现的接口,支持多个接口
    (3)实现这个接口InvocationHandler,创建代理对象,写增强的方法

创建接口,定义方法

public interface UserDao {public int add(int a, int b);public String  update(String id);
}

创建接口实现类,实现方法

public class UserDaoImpl implements UserDao{public int add(int a, int b) {return a + b;}public String update(String id) {return id;}
}

使用Proxy类创建接口代理对象

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;public class JDKProxy {public static void main(String[] args) {//创建接口实现类代理对象Class[] interfaces = {UserDao.class};UserDaoImpl userDao = new UserDaoImpl();UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));int  result = dao.add(1, 2);System.out.println("result:" + result);}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler{//创建的是谁的代理对象,传递过来//有参构造传递private Object obj;public UserDaoProxy(Object obj){this.obj = obj;}//增强的逻辑public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法之前System.out.println("方法之前执行。。。"+method.getName()+"传递的参数:"+ Arrays.toString(args));//被增强的方法执行Object res = method.invoke(obj,args);//方法之后System.out.println("方法之后执行。。。"+obj);return  res;}
}

输出

4.4 AOP术语

通知(Advice):就是你想要的功能,也就是上面说的日志处理、验证处理等。你给先定义好把,然后在想用的地方用一下。

连接点(JoinPoint):这个更好解释了,就是Spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

切入点(Pointcut):上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

切面(Aspect):切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切入点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

引入(introduction):允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

目标(target):引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

代理(proxy):怎么实现整套aop机制的,都是通过代理。

织入(weaving):把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。


4.5 AOP操作

4.5.1 准备工作

AspectJ

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

在 Spring 中使用 AspectJ 进行 AOP 操作

基于AspectJ实现AOP操作的方式

  • 基于xml配置文件
  • 基于注解方式

AspectJ支持5种类型的通知注解

@Before:前置通知,在方法执行之前执行

@After:后置通知,在方法执行之后执行,在连接点返回结果或者抛出异常的时候执行

@AfterRunning:返回通知,在方法返回结果之后执行

@AfterThrowing:异常通知,在方法抛出异常之后执行

@Around:环绕通知,围绕着方法执行

环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。

对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。

在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。

注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用joinPoint.proceed();的返回值,否则会出现空指针异常。


引入依赖

<!-- spring-aop -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.6.RELEASE</version>
</dependency><!-- spring-aspects -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.6.RELEASE</version>
</dependency><!-- aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version>
</dependency><!-- aopalliance -->
<dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version>
</dependency><!-- cglib -->
<dependency><groupId>net.sourceforge.cglib</groupId><artifactId>com.springsource.net.sf.cglib</artifactId><version>2.2.0</version>
</dependency>

编写Spring配置文件

引入contextaop 名称空间;开启组件扫描,并指明包路径;开启自动代理功能

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 组件扫描 --><context:component-scan base-package="com.oneby"/><!-- 开启 Aspect 生成代理对象 --><aop:aspectj-autoproxy/></beans>

切入点表达式

切入点表达式作用:知道对类里的哪个方法进行增强

切入点表达式的语法格式:execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))


切入点表达式的举例一

表达式:execution(* com.atguigu.spring.ArithmeticCalculator.*(..))

含义:增强 ArithmeticCalculator 接口中声明的所有方法

解释说明:第一个“*”代表任意修饰符及任意返回值;第二个“*”代表任意方法;“…”匹配任意数量、任意类型的参数

注:若目标类、接口与该切面类在同一个包中可以省略包名


切入点表达式的举例二

表达式:execution(public * ArithmeticCalculator.*(..))

含义: 增强 ArithmeticCalculator 接口的所有公有方法


切入点表达式的举例三

表达式:execution(public double ArithmeticCalculator.*(..))

含义:增强 ArithmeticCalculator 接口中返回double类型数值的方法


切入点表达式的举例四

表达式:execution(public double ArithmeticCalculator.*(double, ..))

含义:第一个参数为double类型的方法。“…” 匹配任意数量、任意类型的参数


切入点表达式的举例五

表达式: execution(public double ArithmeticCalculator.*(double, double))

含义:参数类型为double,double类型的方法


切入点表达式的举例六:在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。

表达式:execution (* *.add(int,..)) || execution(* *.sub(int,..))

含义:任意类中第一个参数为int类型的add方法或sub方法


4.5.2 AspectJ注解方式

基本步骤示例

引入maven依赖


修改配置文件,引入 contextaop 名称空间;开启组件扫描,并指明包路径;开启自动代理功能

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 组件扫描 --><context:component-scan base-package="annotation"/><!-- 开启 Aspect 生成代理对象 --><aop:aspectj-autoproxy/></beans>

ArithmeticCalculator接口:定义各种数学运算方法

package annotation;public interface ArithmeticCalculator {void add(int i, int j);void sub(int i, int j);void mul(int i, int j);void div(int i, int j);
}

ArithmeticCalculatorImpl 类:实现了ArithmeticCalculator 接口中各种抽象的数学运算方法

package annotation;import org.springframework.stereotype.Component;@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator{public void add(int i, int j) {int result = i + j;System.out.println("计算器计算得到的结果为: " + result);}public void sub(int i, int j) {int result = i - j;System.out.println("计算器计算得到的结果为: " + result);}public void mul(int i, int j) {int result = i * j;System.out.println("计算器计算得到的结果为: " + result);}public void div(int i, int j) {int result = i / j;System.out.println("计算器计算得到的结果为: " + result);}
}

切面类的定义

@Aspect注解标识这是一个切面类
@Component 注解将这个切面类对象交由 Spring IOC 进行管理
execution(* com.oneby.calc.ArithmeticCalculator.*(..))表示增强ArithmeticCalculator接口中的所有方法

package annotation;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Aspect
public class CalculatorLoggingAspect {@Before(value = "execution(* annotation.ArithmeticCalculator.*(..))")public void before(JoinPoint joinPoint){System.out.println("@Before 前置通知");}@After(value = "execution(* annotation.ArithmeticCalculator.*(..))")  //最终通知public void after(JoinPoint joinPoint) {System.out.println("@After 后置通知");}@AfterReturning(value = "execution(* annotation.ArithmeticCalculator.*(..))")  //后置通知(返回通知)public void afterReturning(JoinPoint joinPoint) {System.out.println("@AfterReturning 返回后通知");}@AfterThrowing(value = "execution(* annotation.ArithmeticCalculator.*(..))")public void afterThrowing(JoinPoint joinPoint) {System.out.println("@AfterThrowing 异常通知");}@Around(value = "execution(* annotation.ArithmeticCalculator.*(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("@Around 环绕通知之前");proceedingJoinPoint.proceed(); // 执行目标方法System.out.println("@Around 环绕通知之后");}
}

测试代码

import annotation.ArithmeticCalculator;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.SpringVersion;public class TestAOP {@Testpublic void test() {//1.创建IOC容器对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-aop.xml");//2.根据id值获取bean实例对象ArithmeticCalculator arithmeticCalculator = context.getBean(ArithmeticCalculator.class);//3.调用bean中的方法System.out.println("spring版本:" + SpringVersion.getVersion() + "下的测试");arithmeticCalculator.add(1, 1);}
}

输出:


Spring 5.2.8 版本下的测试

将 Spring 版本改为 5.2.8 后,通知的执行顺序就正常啦:环绕通知包裹住其他通知;并且 @Atfer@AfterReturning 之后执行


异常情况下通知的执行顺序

首先目标方法没有执行;没有@AtferReturning 通知,之前 @AtferReturning 通知的地方变为了 @AtferThrowing;Around 后置环绕通知也没有执行


完全使用注解方式进行 aop 开发

创建 SpringAopConfig配置类:

@Configuration 表示这是一个配置类;

@ComponentScan(basePackages = "com.oneby")配置包扫描路径;

@EnableAspectJAutoProxy(proxyTargetClass = true)表示开启 AOP 自动代理


@Configuration
@ComponentScan(basePackages = "com.oneby")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringAopConfig {}

测试代码

public class SpringTest {@Testpublic void testAnnotation() {//1.创建IOC容器对象ApplicationContext iocContainer =new AnnotationConfigApplicationContext(SpringAopConfig.class);//2.根据id值获取bean实例对象ArithmeticCalculator arithmeticCalculator = iocContainer.getBean(ArithmeticCalculator.class);//3.调用bean中的方法System.out.println("spring版本:" + SpringVersion.getVersion() + "下的测试");arithmeticCalculator.div(1, 0);}}

重用切入点定义

在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式。但同一个切点表达式可能会在多个通知中重复出现。

AspectJ切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的。

切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。

其他通知可以通过方法名称引入该切入点

指定切面的优先级

在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。

切面的优先级可以通过实现Ordered接口或利用@Order注解指定。

实现Ordered接口,getOrder()方法的返回值越小,优先级越高。

若使用@Order注解,序号出现在注解中

@Component
@Aspect
@Order(0)
public class CalculatorValidationAspect {@Component
@Aspect
@Order(1)
public class CalculatorLoggingAspect {

4.5.3 AspectJ配置文件方式

配置切面

在bean配置文件中,所有的Spring AOP配置都必须定义<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端bean实例。切面bean必须有一个标识符,供<aop:aspect>元素引用。

声明切入点

  • 切入点使用<aop:pointcut>元素声明。
  • 切入点必须定义在<aop:aspect>元素下,或者直接定义在<aop:config>元素下。
  • 定义在<aop:aspect>元素下:只对当前切面有效
  • 定义在<aop:config>元素下:对所有切面都有效
  • 基于XML的AOP配置不允许在切入点表达式中用名称引用其他切入点。

声明通知

  • 在aop名称空间中,每种通知类型都对应一个特定的XML元素。
  • 通知元素需要使用<pointcut-ref>来引用切入点,或用<pointcut>直接嵌入切入点表达式。
  • method属性指定切面类中通知方法的名称

<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 开启 Aspect 生成代理对象 --><aop:aspectj-autoproxy/><!-- 创建切面类(CalculatorLoggingAspect)和被增强类(ArithmeticCalculatorImpl)的对象 --><bean  id="arithmeticCalculatorImpl" class="annotation.ArithmeticCalculatorImpl"/><bean id="calculatorLoggingAspect" class="annotation.CalculatorLoggingAspect"/><!-- 配置 aop 切入点 --><aop:config><!-- 配置切入点表达式 --><aop:pointcut id="calcPointcut" expression="execution(* annotation.ArithmeticCalculator.*(..))"/><!-- 配置切面 --><aop:aspect ref="calculatorLoggingAspect"><!-- 配置通知的类型,以及具体切哪些方法 --><aop:before method="before" pointcut-ref="calcPointcut"/><aop:after method="after" pointcut-ref="calcPointcut"/><aop:after-returning method="afterReturning" pointcut-ref="calcPointcut"/><aop:after-throwing method="afterThrowing" pointcut-ref="calcPointcut"/><aop:around method="around" pointcut-ref="calcPointcut"/></aop:aspect></aop:config></beans>

测试代码

public class SpringTest {@Testpublic void test() {//1.创建IOC容器对象ApplicationContext iocContainer =new ClassPathXmlApplicationContext("spring-aop-xml.xml");//2.根据id值获取bean实例对象ArithmeticCalculator arithmeticCalculator = iocContainer.getBean(ArithmeticCalculator.class);//3.调用bean中的方法System.out.println("spring版本:" + SpringVersion.getVersion() + "下的测试");arithmeticCalculator.div(1, 0);}@Testpublic void testAnnotation() {//1.创建IOC容器对象ApplicationContext iocContainer =new AnnotationConfigApplicationContext(SpringAopConfig.class);//2.根据id值获取bean实例对象ArithmeticCalculator arithmeticCalculator = iocContainer.getBean(ArithmeticCalculator.class);//3.调用bean中的方法System.out.println("spring版本:" + SpringVersion.getVersion() + "下的测试");arithmeticCalculator.div(1, 0);}@Testpublic void testXml() {//1.创建IOC容器对象ApplicationContext iocContainer =new ClassPathXmlApplicationContext("spring-aop-xml.xml");//2.根据id值获取bean实例对象ArithmeticCalculator arithmeticCalculator = iocContainer.getBean(ArithmeticCalculator.class);//3.调用bean中的方法System.out.println("spring版本:" + SpringVersion.getVersion() + "下的测试");arithmeticCalculator.div(1, 0);}}

5. JDBCTemplate

5.1 概述

为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。

作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。

可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和我们之前使用过的DBUtils风格非常接近。

5.2 准备工作

引入依赖


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>Spring5-learn</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>spring-jdbcTemplate</artifactId><dependencies><!-- spring-jdbc --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.8.RELEASE</version></dependency><!-- spring-tx --><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.2.8.RELEASE</version></dependency><!-- spring-orm --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.2.8.RELEASE</version></dependency><!-- druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.9</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.8</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.6.RELEASE</version><scope>compile</scope></dependency></dependencies></project>

在spring配置文件配置数据库连接池,开启组件扫描

配置JdbcTemplate对象,注入DataSource

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 组件扫描 --><context:component-scan base-package="dao,service"/><!--数据库连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"><property name="url" value="jdbc:mysql:///spring5-learn"/><property name="username" value="root"/><property name="password" value="123456"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/></bean><!-- 配置 JdbcTemplate 对象,并注入 dataSource 数据源 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean>
</beans>

创建数据库

创建service类dao类,在dao类中注入jdbcTemplate对象

package dao;
public interface BookDao {}
package dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class BookDaoImpl implements BookDao{//注入JDBCTemplate@Autowiredprivate JdbcTemplate jdbcTemplate;
}
package service;import dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BookService {//注入dao@Autowiredprivate BookDao bookDao;
}

5.3 jdbcTemplate操作数据库

book实体类

package entiy;public class Book {private String bookId;private String bookname;private String bookCategory;public String getBookId() {return bookId;}public void setBookId(String bookId) {this.bookId = bookId;}public String getBookname() {return bookname;}public void setBookname(String bookname) {this.bookname = bookname;}public String getBookCategory() {return bookCategory;}public void setBookCategory(String bookCategory) {this.bookCategory = bookCategory;}
}

在dao进行数据库添加操作

调用JdbcTemplate对象里的update()方法实现添加操作

update(String sql, Object... args)
第一个参数为SQL语句,第二个参数为可变参数,设置sql语句值

package dao;import entiy.Book;public interface BookDao {void add(Book book);
}
package dao;import entiy.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class BookDaoImpl implements BookDao{//注入JDBCTemplate@Autowiredprivate JdbcTemplate jdbcTemplate;public void add(Book book) {String sql = "insert into t_books values(?,?,?)";Object [] args = {book.getBookId(),book.getBookname(),book.getBookCategory()};//int update = jdbcTemplate.update(sql, book.getBookId(),book.getBookname(),book.getBookCategory());int update = jdbcTemplate.update(sql, args);System.out.println(update);}
}
package service;import dao.BookDao;
import entiy.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BookService {//注入dao@Autowiredprivate BookDao bookDao;//添加的方法public void addBook(Book book){bookDao.add(book);}
}

测试代码

import entiy.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.BookService;public class TestBook {@Testpublic void testJdbcTemplate(){ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");BookService bookService = context.getBean("bookService", BookService.class);Book book = new Book();book.setBookId("1");book.setBookname("java");book.setBookCategory("a");bookService.addBook(book);}}

输出


综合
BookDao接口的定义

public interface BookDao {// 添加一本图书public int addBook(Book book);// 删除一本图书public int deleteBook(String bookId);// 更新一本图书的信息public int updateBook(Book book);// 查询一本图书public Book findBookInfo(int bookId);// 查询所有图书的数量public int findBookCount();// 查询所有图书的集合public List<Book> findAllBookInfo();// 批量添加图书public int[] batchAddBook(List<Book> books);// 批量修改图书信息public int[] batchUpdateBook(List<Book> books);// 批量删除图书public int[] batchDeleteBook(List<Integer> bookId);}

BookDaoImpl实现类的定义

增删改使用update() 方法
查询某个值或对象 queryForObject()方法
查询对象集合使用query()方法
批量处理使用batchUpdate()方法


增删改

增删改用这个函数:public int update(String sql, @Nullable Object... args) throws DataAccessException,通过 sql 指明要执行的 SQL 语句,并通过可变长参数 args 指明 SQL 语句的参数

查询某个值或对象(查询单行)

查询某个值或对象用这个函数:public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException,通过 sql 指明要执行的 SQL 语句,通过 RowMapper 对象指明从数据库查询出来的参数应该如何封装到指定的对象中,并通过可变长参数args指明 SQL 语句的参数


RowMapper接口

RowMapper接口是一个函数式接口,其中只有一个方法:T mapRow(ResultSet rs, int rowNum) throws SQLException,该方法的具体作用是将查询得到的每行数据映射到 ResultSet


BeanPropertyRowMapper

BeanPropertyRowMapper 类实现了RowMapper 接口,其功能是:将查询得到的结果集的值,注入到对象属性中


查询对象集合(查询多行)

查询对象集合使用这个函数:public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException,通过 sql 指明要执行的 SQL 语句,通过 RowMapper 对象指明从数据库查询出来的参数应该如何封装到指定的对象中,并通过可变长参数args 指明 SQL 语句的参数

批量操作

批量操作包括批量增加、批量更新、批量删除操作,这些操作都可以使用public int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException 函数完成:通过 sql 指明要执行的 SQL 语句,并通过参数 batchArgs指明批处理 SQL 语句的参数




/*** @ClassName BookDaoImpl* @Description TODO* @Author Oneby* @Date 2021/2/23 20:25* @Version 1.0*/
@Repository
public class BookDaoImpl implements BookDao {// 注入 JdbcTemplate 对象@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic int addBook(Book book) {// 创建 SQL 语句String sql = "insert into t_books (book_name, book_category) values (?, ?) ";// SQL 语句参数Object[] args = {book.getBookName(), book.getBookCategory()};// 执行 SQL 语句int insertRows = jdbcTemplate.update(sql, args);return insertRows;}@Overridepublic int deleteBook(String bookId) {// 创建 SQL 语句String sql = "delete from t_books where book_id = ?";// 执行 SQL 语句int deleteRows = jdbcTemplate.update(sql, bookId);return deleteRows;}@Overridepublic int updateBook(Book book) {// 创建 SQL 语句String sql = "update t_books set book_name = ?, book_category = ? where book_id = ?";// SQL 语句参数Object[] args = {book.getBookName(), book.getBookCategory(), book.getBookId()};// 执行 SQL 语句int insertRows = jdbcTemplate.update(sql, args);return insertRows;}@Overridepublic Book findBookInfo(int bookId) {// 创建 SQL 语句String sql = "select * from t_books where book_id = ?";// 执行 SQL 语句Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), bookId);return book;}@Overridepublic int findBookCount() {// 创建 SQL 语句String sql = "select count(*) from t_books";// 执行 SQL 语句Integer count = jdbcTemplate.queryForObject(sql, Integer.class);return count;}@Overridepublic List<Book> findAllBookInfo() {// 创建 SQL 语句String sql = "select * from t_books";// 执行 SQL 语句List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));return books;}@Overridepublic int[] batchAddBook(List<Book> books) {// 创建 SQL 语句String sql = "insert into t_book (book_name, book_category) values (?, ?)";// 构造参数List<Object[]> batchArgs = new ArrayList<>();for (Book book : books) {batchArgs.add(new Object[]{book.getBookName(), book.getBookCategory()});}// 批量执行int[] batchAffectedRows = jdbcTemplate.batchUpdate(sql, batchArgs);return batchAffectedRows;}@Overridepublic int[] batchUpdateBook(List<Book> books) {// 创建 SQL 语句String sql = "update t_books set book_name = ?, book_category = ? where book_id = ?";// 构造参数List<Object[]> batchArgs = new ArrayList<>();for (Book book : books) {batchArgs.add(new Object[]{book.getBookName(), book.getBookCategory(), book.getBookId()});}// 批量执行int[] batchAffectedRows = jdbcTemplate.batchUpdate(sql, batchArgs);return batchAffectedRows;}@Overridepublic int[] batchDeleteBook(List<Integer> bookIds) {// 创建 SQL 语句String sql = "delete from t_books where book_id = ?";// 构造参数List<Object[]> batchArgs = new ArrayList<>();for (Integer bookId : bookIds) {batchArgs.add(new Object[]{bookId});}// 批量执行int[] batchAffectedRows = jdbcTemplate.batchUpdate(sql, batchArgs);return batchAffectedRows;}}

6. 声明式事务管理

未完待续。。。

见大佬博客
csdn大佬的笔记

Spring5框架基础知识小结相关推荐

  1. 好程序员分享24个canvas基础知识小结

    好程序员分享24个canvas基础知识小结,非常全面详尽,推荐给大家. 现把canvas的知识点总结如下,以便随时查阅. 1.填充矩形 fillRect(x,y,width,height); 2.绘制 ...

  2. Android技能树 — 树基础知识小结(一)

    前言: 现在安卓面试,对于数据结构的问题也越来越多了,也经常看到别人发的面试题都是问什么红黑树,二叉树查找等,所以我们虽然不会马上就会各种难的面试题,但起码树的基础知识还是要会的,这样才能去进一步学. ...

  3. 好程序员分享24个canvas基础知识小结 1

    好程序员分享24个canvas基础知识小结,非常全面详尽,推荐给大家. 现把canvas的知识点总结如下,以便随时查阅. 1.填充矩形 fillRect(x,y,width,height); 2.绘制 ...

  4. WinCE流驱动基础知识小结

    WinCE流驱动基础知识小结 1.基础知识: 1)系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件细节,在应用程序看来硬件只 ...

  5. Spring框架基础知识

    本人博客文章网址:https://www.peretang.com/basic-knowledge-of-spring-framework/ Spring框架简介 Spring , 一个开源的框架 , ...

  6. Android技能树 — 树基础知识小结(一),kotlin开源项目

    根据上面的基础知识我画了一个归总的图(这样我就不需要写文字介绍了,啊哈哈): [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zd87TfMP-1637304075403 ...

  7. Django框架基础知识汇总(有项目版)

    Web框架本质## web系统概念 1. Http,无状态,短连接 2. 浏览器(socket客户端).网站(socket服务端) web框架本质 import socket def handle_r ...

  8. 关于TP5.1框架一些基础知识小结

    文章目录 默认入口文件 TP5支持的URL模式 URL大小写问题 响应输出 使用框架类方法读取配置文件注意事项(TP5.1) TP5.1配置文件优先级 Thinkphp概念个人理解 默认入口文件 首先 ...

  9. 后端 学习 前端 Vue 框架基础知识

    文章目录 一.Vue 基础语法学习 1.Vue 语法指令 2.Vue 实例属性 (1)el (2)data (3)methods (4)computed 3.事件修饰符 4.按键修饰符 5.Vue实例 ...

最新文章

  1. 区块链论文9 FlyClient-加密货币的超轻客户端
  2. 一个Demo展示Storyboard的强大
  3. IE edge是怎么了??
  4. “象征界”的奇观:刘天怜花鸟工笔作品印象
  5. 公共服务领域英文译写规范_公共领域日:对版权和公共领域重要性的思考
  6. My97DatePicker详细说明
  7. 09 动态数组和数据
  8. python循环控制语句将数值转化成字符串_python基础入门详解(文件输入/输出内建类型字典操作使用方法)...
  9. 深度强化学习DQN(附DQN训练Flappy Bird源代码)
  10. OFD文件怎么编辑修改?
  11. Hive学习(7)pmod()函数详解
  12. Gate仿真配置安装--Linux系统
  13. 二维数组传参||传值
  14. linux下文件的重命名方法
  15. 一个菜鸟实习生的月总结
  16. 永久关闭Windows10防火墙(Windows7也合适)
  17. 信息收集之——旁站、C段
  18. 工具 · 移动端调试工具:weinre
  19. 你想成为管理人才吗?必须去读《孙子兵法》!
  20. android.uid.system无法读取SD卡信息

热门文章

  1. 3个开关与3盏灯的问题
  2. Python主要应用领域2
  3. 你的云桌面和阿里的云主机有什么区别?
  4. Storm流式计算入门
  5. <C++模板:(函数模板)+(类模板)--详细说明>
  6. 中国三大首富合体的腾百万为何玩不转了?
  7. Dalvik虚拟机ART虚拟机
  8. Lebesgue外测度—实变函数与泛函分析
  9. 车载通信与导航(九):python实现街道模型GPRS通信D2D通信
  10. oracle的redolog停止,ORACLE REDO LOG 频繁切换问题解决与分析