Spring

*Spring 实战

spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

添加Context命名空间

//开启命名空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

在这里插入图片描述

开启aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

开启tx命名空间

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

主要内容
Spring 简介
SpringIOC 容器
DI 依赖注入
Spring 整合持久层技术
代理模式
AOP 编程
Spring 事务管理
Spring 基于注解装配 Bean 对象
Junit 的使用
Spring 整合 Junit
Spring 整合 Servlet
Spring 知识点梳理

学习目标

在这里插入图片描述

一、 Spring 简介

在这里插入图片描述

1 Spring 介绍

Spring 是一个开源框架,最早由 Rod Johnson 发起。Spring 为简化企业级开发而生,使
用 Spring 开发可以将 Bean 对象交给 Spring 容器来管理,这样使得很多复杂的代码在 Spring
中开发会变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。

2 Spring 的四种关键策略

 基于 POJO 的轻量级和最小侵入性编程;
 通过依赖注入和面向接口实现松耦合;
 基于切面和惯例进行声明式编程;
 通过切面和模板减少样板式代码;

3 Spring 特点

 非侵入式:基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API。

 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期。

 控制反转:IOC (Inversion of Control),指的是将对象的创建权交给 Spring 去创建。
使用 Spring 之前,对象的创建都是由我们自己在代码中 new 创建。而使用 Spring
之后。对象的创建都是由给了 Spring 框架。

 依赖注入:DI (Dependency Injection),是指依赖的对象不需要手动调用 setXX 方法
去设置,而是通过配置赋值。

 面向切面编程:Aspect Oriented Programming——AOP。

 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中
可以使用 XML 和 Java 注解组合这些对象。

 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。

Spring 官网地址:

https://spring.io/

4 Spring 体系结构

Spring版本不同包含的模块也不同,每个项目必须包含Core Container(基础模块)

Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块可根据作
用分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core
Container 和 Test。

在这里插入图片描述

Spring 框架的这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据
需求有选择性地使用所需要的模块。

4.1Data Access/Integration(数据访问/集成)

数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。

 JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。

 ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 MyBatis 提供了的集成。

 OXM 模块:提供了一个支持对象/XML 映射的抽象层实现。

 JMS 模块:指 Java 消息服务,包含的消息生产和消费的处理。

 Transactions 事务模块:支持编程和声明式事务处理。

4.2Web

Spring 的 Web 层由 spring-web、spring-webmvc、spring-websocket 和 spring-webflux
组成,具体介绍如下。

 Spring-web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用
的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。

 Spring-webmvc模块:也称为Web-Servlet模块,包含用于web应用程序的Spring MVC
和 REST Web Services 实现。Spring MVC 框架提供了领域模型代码和 Web 表单之间
的清晰分离,并与 Spring Framework 的所有其他功能集成。

 Spring-websocket 模块:Spring4.0 以后新增的模块,它提供了 WebSocket 和 SocketJS的实现。

 Spring-webflux 模块:是一个新的非堵塞函数式 Reactive Web 框架, 可以用来建
立异步的, 非阻塞,事件驱动的服务, 并且扩展性非常好。

4.3Core Container(核心容器)

Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上
下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。

 Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。

 Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。

 Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配
置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。

 Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。是对
JSP2.1 规范中规定的统一表达式语言(Unified EL)的扩展。(语法和EL表达式相同)

4.4其他模块

Spring 的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。

 AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按
照功能进行分离,以降低耦合性。

 Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。

 Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。

 Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。

5 Spring 的 jar 包下载与介绍

5.1下载地址:

https://repo.spring.io/simple/libs-release-local/org/springframework/spring/

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

-dist 后缀表示该文件夹下存放的是 jar 包,文档和 xsd 文件;

-docs 后缀表示该文件夹下存放相关文档,开发指南和 API;

-schema 里存放了 Spring 所用的 xsd 文件。

libs 目录文件说明:

.RELEASE.jar: 开发时需要引用的 jar
.RELEASE-javadoc.jar: 文档
.RELEASE-sources.jar: 源码

一个jar包由三部分构成(jar包,文档,源码)

在这里插入图片描述

5.2jar 包介绍

在这里插入图片描述

在这里插入图片描述

二、 Spring IOC 容器

1 Spring IOC 容器介绍

1.1IOC 思想

IOC (Inversion of Control) 是指在程序开发中,对象实例的创建不再由调用者管理,而是
==由 Spring 容器创建。==Spring 容器会负责控制程序之间的关系,而不是由程序代码直接控制,
因此,控制权由程序代码转移到了 Spring 容器中,控制权发生了反转,这就是 Spring 的IOC 思想。

1.2IOC 容器概念

IOC 容器就是具有依赖注入功能的容器,IOC 容器负责实例化、定位、配置应用程序中
的对象及建立这些对象间的依赖。应用程序无需直接在代码中 new 相关的对象,应用程序
由 IOC 容器进行组装。在 Spring 中 BeanFactory 是 IOC 容器的实际代表者。

1.3Bean 的概念

在 Spring 中,被 Spring 容器所管理的对象称之为”Bean”对象。一个 Spring 的 Bean 对象
可以是任何形式的 POJO。(任何一个class都可以被Spring实例化,且不需要实现任何接口)

2 Spring IOC 容器类型

Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext。

2.1BeanFactory

BeanFactory 是基础类型的 IoC 容器。
它由 org.springframework.beans.facytory.BeanFactory 接口定义,并提供了完整的 IoC
服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种
Bean,并调用它们的生命周期方法。

2.2ApplicationContext

==ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。==该接口的全路径
为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,
还添加了对国际化、资源访问、事件传播等方面的良好支持。

ApplicationContext 接口有两个常用的实现类:

2.2.1 ClassPathXmlApplicationContext

实例化:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);

FileconfigLocation 参数用于指定 Spring 配置文件的名称和位置。

2.2.2 SystemXmlApplicationContext
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation)

它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,
FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文
件的位置,它可以获取类路径之外的资源,如“F:/workspaces/applicationContext.xml”;

3 Spring IOC 容器的使用

3.1搭建环境
3.1.1 创建项目

在这里插入图片描述

3.1.2 添加 jar 包

在这里插入图片描述

3.1.3 添加 Spring 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

在这里插入图片描述

3.2通过 IOC 容器管理 Bean 对象
3.2.1 创建 UsersService 接口与实现类
package com.bjsxt.service;

public interface UsersService {

    void addUsers();
}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {


    @Override
    public void addUsers() {
        System.out.println("UsersService addUsers() .......");
    }
}
3.2.2 修改配置文件
<?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">

    <!--id:唯一标识     class:类的全名-->
    <bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl"/>
</beans>
3.2.3 获取 IOC 容器中的对象
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UsersServiceTest {
    public static void main(String[] args) {
        /*UsersService usersService = new UsersServiceImpl();
        usersService.addUsers();*/

        //启动 Spring IOC容器
        //实例化Spring IOC容器   这个接口实现类会到ClassPath(SRC)下找配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IOC容器中获取对象   通过id获取在IOC容器中缓存的对象  返回Object类型,需强转
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //调用方法
        usersService.addUsers();
    }
}
3.3Spring IOC 容器创建 Bean 对象的三种方式
3.3.1 通过构造方法创建 Bean 对象

实例化对象时,对象的构造方法会被执行,如果没有添加任何构造方法默认会添加一个无参的构造方法,(构造方法的名称必须和类名相同)

Spring IOC容器在实例化Bean对象时和正常new Bean对象是一样的都是通过调用对象的构造方法完成实例化

在这里插入图片描述

package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {

    //添加一个无参构造方法
    public UsersServiceImpl(){
        System.out.println("Init.........");
    }

    @Override
    public void addUsers() {
        System.out.println("UsersService addUsers() .......");
    }
}
3.3.2 通过静态工厂方法创建对象
3.3.2.1 创建静态工厂方法
package com.bjsxt.factory;

import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;

public class ObjectFactory {
    //静态工厂模式,静态方法
    public static UsersService getInstance(){
        return new UsersServiceImpl();
    }
}
3.3.2.2 修改配置文件

Spring IOC容器在启动时发现有factory-method就不会再实例化ObjectFactory,而是通过这个类调用给定的方法来创建对象,这个方法一旦被调用会把创建的对象放到Spring IOC容器中

在这里插入图片描述

<!--通过静态工厂方法实例化对象   class:给工厂的全名-->
<bean id="usersService2" class="com.bjsxt.factory.ObjectFactory" factory-method="getInstance"/>
3.3.2.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ObjectFactoryTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        UsersService usersService = (UsersService) applicationContext.getBean("usersService2");
        usersService.addUsers();
    }
}

在这里插入图片描述

3.3.3 通过动态工厂方法创建对象
3.3.3.1 创建动态工厂方法
package com.bjsxt.factory;

import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;

/**
 * 动态工厂
 */
public class DynamicObjectFactory { 	
    //如果用static关键字修饰就变成了静态工厂    没有static关键字修饰必须实例化该类才能调用非静态方法
    public UsersService getInstance(){
        return new UsersServiceImpl();
    }
}
3.3.3.2 修改配置文件

在这里插入图片描述

<!--通过动态工厂方法实例化对象-->    <!--Spring在解析配置文件时会先实例化动态工厂的类,再调用factory-bean中给定的对象的方法生成对象,再将生成的对象与usersService3绑定-->
<bean id="dynamicObjectFactory" class="com.bjsxt.factory.DynamicObjectFactory"/>
<!--factory-bean:指定工厂bean对象,给bean的id    factory-method:指定产生对象的方法-->
<bean id="usersService3" factory-bean="dynamicObjectFactory" factory-method="getInstance"/>
3.3.3.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DynamicFactoryTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UsersService usersService = (UsersService) applicationContext.getBean("usersService3");
        usersService.addUsers();
    }
}
3.4在 Spring IOC 容器中获取 Bean 对象的方式
3.4.1 通过 id 或 name 获取 Bean 对象
3.4.1.1 配置文件

在这里插入图片描述

<!--id:唯一标识     class:类的全名  name:给bean对象取别名,多个名字之间用逗号分隔,但不予许重复-->
<bean id="usersService" name="name1,name2,name3" class="com.bjsxt.service.impl.UsersServiceImpl"/>
3.4.1.2 获取 bean 对象

通过name获取bean对象

在这里插入图片描述

在这里插入图片描述

3.4.2 通过类型获取 Bean 对象

如果同类型的Bean对象有多个通过id获取更简洁

通过类型来或取 Bean 时,要求类型必须是唯一的。(在Spring IOC容器中对于同类型的对象只能有一个)

3.4.2.1 获取 Bean 对象

通过类型获取bean对象时直接返回该类型的对象不需要强转

//通过类型获取bean对象
UsersService usersService = applicationContext.getBean(UsersServiceImpl.class);

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

此时Spring IOC容器中有两个usersServiceImple类型的bean标签,违反了通过类型获取bean对象类型必须是唯一的标准,所以报错

Init…执行了两次,说明对象被创建了两次

在这里插入图片描述

在这里插入图片描述

期望是匹配到一个但是现在匹配到两个分别是usersService,usersService2

在这里插入图片描述

3.4.3 通过 id 或 name 与类型获取 Bean 对象

在 SpringIOC 容器中,通过类型获取对象时,如果同一类型存在多个对象,我们可以使
用 id 或 name 来识别需要获取的对象。

3.4.3.1 方式一

在这里插入图片描述

通过ID区别

在这里插入图片描述

通过name区别

在这里插入图片描述

3.4.3.2 方式二

在这里插入图片描述

在这里插入图片描述

/*方式二:*/
//获取Spring IOC容器中所有bean对象的id    返回一个数组,在Spring IOC容器中所有bean对象的id
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
    System.out.println(name);
}
//通过数组下标获取id用以区分同一类型
UsersService usersService = applicationContext.getBean(beanDefinitionNames[0],UsersServiceImpl.class);
//调用方法
usersService.addUsers();
3.5Spring IOC 容器创建对象的策略
3.5.1 实例化对象策略

==Spring IOC 容器在启动时默认的会将在配置文件中所配置的所有 Bean 对象立即进行实例化,并保存在 IOC 容器中。==我们可以通过标签 lazy-init 属性的实现延迟实例化对象。

3.5.1.1 立即创建

lazy-init=“false”(默认)
^在 Spring IOC 容器启动时会实例化配置文件中的所有 Bean 对象。^

在配置文件中配置了两个bean标签都去实例化一个对象,构造方法被调用了两次,说明对象被实例化了两次

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.5.1.2 延迟创建

lazy-init=“true”
当调用 getBean 方法是创建对象。

只实例化一次(id=“usersService2”),因为id="usersService"开启了延迟创建,调用getBean()时创建

在这里插入图片描述

在这里插入图片描述

开启了延迟创建,调用getBean()时创建

在这里插入图片描述

package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LazyInitTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
        //在配置文件中开启延迟创建后,调用getBean()方法时才创建对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
    }
}
3.5.2 Bean 对象的作用域

作用域:作用域限定了 Spring Bean 的作用范围,在 Spring 配置文件定义 Bean 时,通过
声明 scope 配置项,可以灵活定义 Bean 的作用范围。

scope 属性的值:
 singleton
 prototype

3.5.2.1 singleton(单例)

==singleton 为 scope 属性的默认值。==当 scope 属性的值为 singleton 时,Spring IOC 容器启动时会立即实例化一次 Bean 对象,并一直被 Spring IOC 容器所缓存,所以生命周期较长。(不会被JVM回收)

singleton 特点:
 Spring IOC 容器启动时会创建 Bean 对象
 每次调用 getBean 都返回 spring 容器中的唯一一个对象,每次返回都是同一个对象

在这里插入图片描述

在这里插入图片描述

3.5.2.2 prototype(多例)

当scope属性的值为prototype时,每次调用调用getBean方法时都会返回一个新的Bean对象,

prototype 特点:
 Spring IOC 容器启动时不会创建 Bean 对象。
 每次调用 getBean 都会创建一个新 Bean 对象。

没打印 Init… 构造方法没被执行,对象没被创建

在这里插入图片描述

打印 两次Init… 构造方法被执行两次,对象被创建两次,两个对象不相同

在这里插入图片描述

在这里插入图片描述

三、 DI 依赖注入

对于 Spring IOC 容器而言我们更多的是使用它的依赖注入。 Spring 创建对象的过程叫做 IOC,创建对象时给对象属性赋值叫做 DI,所以我们可以认为 IOC 和 DI 是同一个事情。

1 什么是依赖注入

DI (Dependency Injection):依赖注入是指在 Spring IOC 容器创建对象的过程中,将所
依赖的对象通过配置进行注入。我们可以通过依赖注入的方式来降低对象间的耦合度。
在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护
成本越高,因此对象的设计应使对象之间的耦合越小越好。

1.1类的关系

继承 实现 依赖 关联 聚合 组合 开发时多用聚合少用组合

1.1.1 依赖关系(Dependency)

【依赖关系】:是一种使用的关系, 即一个类的实现需要另一个类的协助, 所以要尽量不使用双向的互相依赖
【代码表现】:局部变量、方法的参数或者对静态方法的调用
【箭头及指向】:带箭头的虚线,指向被使用者

1.1.2 聚合(Aggregation)

【聚合关系】:是整体与部分的关系.如车和轮胎是整体和部分的关系.聚合关系是关联关系的一种,是强的 关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
【代码体现】:成员变量
【箭头及指向】:带空心菱形的实心线,菱形指向整体

依赖注入:在对象A中需要用到其他对象并不是自己new,而是依赖外界注入进来,把原来new的对象换成成员变量通过Spring把成员变量中的对象注入进来,这个过程叫DI,并且把依赖关系变成聚合关系

1.2关系强度

继承 = 实现 > 组合 > 聚合 > 关联 > 依赖

2 为什么使用依赖注入

2.1开闭原则
2.1.1 定义

OCP (Open Close Principle): 软件本身应该是可扩展的,而不可修改的。也就是,对扩
展开放,对修改封闭的。

2.1.2 开闭原则优点

易扩展。开闭原则的定义就要求对扩展开放。
易维护。软件开发中,对现有代码的修改是一件很有风险的事情,符合开闭原则的设计
在扩展时无需修改现有代码,规避了这个风险,大大提交了可维护性。

2.2高内聚,低耦合

高内聚是指相关度比较高的部分尽可能的集中,不要分散。
低耦合就是说两个相关的模块尽可以能把依赖的部分降低到最小,不要产生强依赖。

3 依赖注入的方式

在使用依赖注入时,如果注入的是 Bean 对象,那么要求注入的 Bean 对象与被注入的
Bean 对象都需要 Spring IOC 容器来实例化。

Spring IOC容器在启动时会实例化这些bean对象,发现usersService这个bean对象有一个属性叫usersDao,且这个属性需要注入的是一个叫usersDao / usersDaoMybatis的bean对象,这时Spring IOC容器就会通过usersService里面的set方法把这个bean对象注入到usersService的属性usersDao中,一旦注入进来usersDao就不为空,就可以拿注入的对象调用方法

面向接口编程,两个实现类都实现UsersDao接口

在这里插入图片描述

注入接口实现类UsersDaoImple

在这里插入图片描述

在这里插入图片描述

添加set方法

在这里插入图片描述

在这里插入图片描述

注入接口实现类UsersDaoMybatisImple

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.1通过 Set 方法注入

需要为注入的成员变量提供 Set 方法。

3.1.1 配置文件

两种写法,意义相同

ref标签和ref属性功能相同

在这里插入图片描述

在这里插入图片描述

<!--将持久层对象放到容器中管理-->
<bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl"/>
<bean id="usersDaoMybatis" class="com.bjsxt.dao.impl.UsersDaoMybatisImpl"/>
<!--id:唯一标识     class:类的全名  name:给bean对象起名字,多个名字之间用逗号分隔     lazy-init:延迟创建,默认为false不开启-->
<bean id="usersService" name="name1,name2,name3" class="com.bjsxt.service.impl.UsersServiceImpl">
    <!--name:给定成员变量名-->
    <property name="usersDao">
        <!--指定注入哪个bean对象-->
        <ref bean="usersDaoMybatis"/>
    </property>
    <!--与上面的写法不同,意义相同-->
    <!--<property name="usersDao" ref="usersDaoMybatis"/>-->
</bean>
3.1.2 Bean 对象 必须通过set方法
package com.bjsxt.service.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    private UsersDao usersDao;

    public UsersDao getUsersDao() {
        return usersDao;
    }
	//必须通过set方法
    public void setUsersDao(UsersDao usersDao) {
        this.usersDao = usersDao;
    }

    //添加一个无参构造方法
    public UsersServiceImpl() {
        System.out.println("Init.........................");
    }

    @Override
    public void addUsers() {
        //调用持久层方法
        usersDao.insertUsers();
        //System.out.println("UsersService addUsers()................");
    }
}

UsersDaoImpl:

package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;

public class UsersDaoImpl implements UsersDao {

    @Override
    public void insertUsers() {
        System.out.println("JDBC ......... Insert Into.............");
    }
}

UsersDaoMybatisImpl:

package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;

public class UsersDaoMybatisImpl implements UsersDao {
    @Override
    public void insertUsers() {
        System.out.println("Mybatis ........ Insert Into............");
    }
}
3.2通过构造方法注入

Bean 对象中需要提供有参的构造方法,constructor-arg标签一个标签表示构造方法中的一个参数

3.2.1 配置文件

两种写法不同,意义相同

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

未打印Init…

在这里插入图片描述

<!--将持久层对象放到容器中管理-->
<bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl"/>
<bean id="usersDaoMybatis" class="com.bjsxt.dao.impl.UsersDaoMybatisImpl"/>
<!--id:唯一标识     class:类的全名  name:给bean对象起名字,多个名字之间用逗号分隔     lazy-init:延迟创建,默认为false不开启-->
<bean id="usersService" name="name1,name2,name3" class="com.bjsxt.service.impl.UsersServiceImpl">

    <!--
	一个constructor-arg标签标示构造方法中的一个参数
    name:根据参数名称识别参数  
    index:根据参数的位置识别参数,从0开始
    type:根据参数的类型识别参数,给全名
    -->
    <constructor-arg name="usersDao">
        <!--把需要注入的对象放到bean属性中-->
        <ref bean="usersDaoMybatis"/>
    </constructor-arg>
    <!--与上面的写法不同,意义相同-->
    <!--<constructor-arg name="usersDao" ref="usersDaoMybatis"/>-->
3.2.2 Bean 对象

在这里插入图片描述

//添加一个有参的构造方法
public UsersServiceImpl(UsersDao usersDao){
    //将注入进来的参数赋给成员变量
    this.usersDao = usersDao;
}
3.3自动注入

自动注入的方式有两种,一种是全局配置自动注入,另一种是局部配置自动注入。
无论全局配置或局部单独配置,都有 5 个值可以选择:

在这里插入图片描述

no:当 autowire 设置为 no 的时候,Spring 就不会进行自动注入。
byName:在 Spring 容器中查找 id 与属性名相同的 bean,并进行注入。需要提供 set 方法。
byType:在 Spring 容器中查找类型与属性名的类型相同的 bean,并进行注入。需要提供 set 方法。
constructor:仍旧是使用 byName 方式,只不过注入的时候,使用构造方式进行注入。
default:全局配置的 default 相当于 no,局部的 default 表示使用全局配置设置。

byName:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

byType:

两个bean对象都实现类usersDao接口,根据usersDao接口类型去找bean对象会找到两个所以报错,得去掉一个

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

constructor:

在这里插入图片描述

调用有参构造方法,而不调用无参构造方法,所以不打印无参构造方法里的输出语句

在这里插入图片描述

3.3.1 局部自动注入

通过 bean 标签中的 autowier 属性配置自动注入。
有效范围:仅针对当前 bean 标签生效。

3.3.2 全局自动注入

通过 beans 标签中的 default-autowire 属性配置自动注入。
有效范围:配置文件中的所有 bean 标签都生效。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

属性名和id不一致,报空指针异常

在这里插入图片描述

在这里插入图片描述

空指针异常:usersDao未注入进来,拿个空对象调方法报空指针异常

在这里插入图片描述

属性名和id一致不报空指针异常

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4 依赖注入的数据类型

4.1注入 Bean 对象

在Spring IOC容器中管理的所有对象统称为bean对象

方式一

在这里插入图片描述

方式二

在这里插入图片描述

4.2注入基本数据类型和字符串

ref标签是用来注入Spring IOC 容器中的bean对象,不能用于注入基本数据类型,使用value标签注入即可

方式一

在这里插入图片描述

添加set方法

在这里插入图片描述

两种写法,意义相同

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

方式二

在这里插入图片描述

4.3注入 List
package com.bjsxt.pojo;

public class Users {
    private String username;
    private int userage;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getUserage() {
        return userage;
    }

    public void setUserage(int userage) {
        this.userage = userage;
    }

    @Override
    public String toString() {
        return "Users{" +
                "username='" + username + '\'' +
                ", userage=" + userage +
                '}';
    }
}

添加set方法

在这里插入图片描述

Spring IOC容器解析配置文件发现id=usersService的bean对象中有个property属性是name=users,users中有个list,list中有个bean,这时会先将bean对象实例化并完成注入属性,然后创建list,再将bean放到list中

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.4注入 Set

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.5注入 Map

方式一

如果使用value属性,那么map的value的泛型只能是八种基本数据类型的包装类,如果是其他对象类型得用value-ref属性

<property name="FieldName" >
<map>
<entry key="KeyName" value="Content"/>
</map>
</property>

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

方式二

<bean id="users1" class="......">
<bean id="users1" class="......">
<property name="FieldName">
<map>
<entry key="key1" value-ref="users1"/>
<entry key="key2" value-ref="users2"/>
</map>
</property>

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.6注入 Properties

key和value只能是字符串类型,一个prop标签对应一个键值对

<property name="FieldName" >
<props>
<prop key="KeyName">Content</prop>
</props>
</property>

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、 Spring 整合持久层技术

1 Spring 整合 JDBC

Spring 为了提供对 Jdbc 的支持,在 Jdbc API 的基础上封装了一套实现用于简化 JDBC 操作的模板。
JDBC 模板的设计目的是为不同类型的 JDBC 操作提供模板方法,每个模板方法都能控制整个过程,通过这种方式, 可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低。

1.1搭建环境
1.1.1 创建项目

在这里插入图片描述

1.1.2 添加 jar 包

核心jar包

在这里插入图片描述

1.1.3 创建配置文件
<?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">
</beans>
1.1.4 创建实体类
package com.bjsxt.pojo;

public class Users {
    private int userid;
    private String username;
    private String usersex;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", usersex='" + usersex + '\'' +
                '}';
    }
}
1.1.5 创建持久层
package com.bjsxt.dao;

public interface UsersDao {

}
package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;

public class UsersDaoImpl implements UsersDao {

}
1.1.6 创建业务层
package com.bjsxt.service;

public interface UsersService {

}
package com.bjsxt.service.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    private UsersDao usersDao;

    public UsersDao getUsersDao() {
        return usersDao;
    }

    public void setUsersDao(UsersDao usersDao) {
        this.usersDao = usersDao;
    }
}
1.2配置 Spring
1.2.1 配置解析 properties 文件
1.2.1.1 添加 db.properties 文件

放到SRC根目录下

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm
jdbc.username = root
jdbc.password = root

1.2.1.2 修改 Spring 配置文件
添加Context命名空间

//开启命名空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

在这里插入图片描述

添加context:property-placeholder/解析properties文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置解析properties文件的工具类-->
    <!--location:指定properties文件的位置和名称,默认去src的根目录下找-->
    <context:property-placeholder location="db.properties"/>
</beans>

会把src根目录下所有properties文件都解析出来

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

有选择的解析多个properties文件

<context:property-placeholder location="db.properties"/>
<context:property-placeholder location="db1.properties"/>
1.2.2 配置数据源
1.2.2.1 什么是数据源

作用是帮助创建Connection对象

JDBC2.0 提供了 javax.sql.DataSource 接口,在接口中定义获取 Connection 对象的标准。
数据源的作用是负责建立与数据库的连接,当在应用程序中访问数据库时不必编写数据库连
接代码,直接引用 DataSource 获取操作数据库的 Connection 对象即可。

1.2.2.2 添加数据源配置

在这里插入图片描述

<!--配置数据源对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!--Spring的表达式语言exception是建立在EL表达式基础上的,语法结构相同-->
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
1.2.3 配置 JdbcTemplate(Spring下基于JDBC操作数据库的模板)

在这里插入图片描述

<!--配置JdbcTemplate-->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>
1.2.4 配置其他 Bean 对象的依赖注入
1.2.4.1 配置持久层依赖注入

1.2.4.1.1 修改接口实现类

在这里插入图片描述

public class UsersDaoImpl implements UsersDao {
    //通过Spring IOC容器注入进来
    private JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

1.2.4.1.2 修改 Spring 配置文件

在这里插入图片描述

<!--配置持久层UsersDao-->
<bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl">
    <!--为JdbcTemplate属性注入-->
    <property name="jdbcTemplate" ref="JdbcTemplate"/>
</bean>

1.2.4.2 配置业务层依赖注入

在这里插入图片描述

在这里插入图片描述

<!--配置业务层UsersService-->
<bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl">
    <!--为usersDao属性注入-->
    <property name="usersDao" ref="usersDao"/>
</bean>

配置数据源,配置JdbcTemplate将数据源注入到JdbcTemplate中,再配置持久层接口实现类将JdbcTemplate注入到持久层接口实现类中,配置业务层接口实现类将持久层接口实现类注入到业务层接口实现类中

在这里插入图片描述

1.3JdbcTemplate 的使用
1.3.1 执行单条 DML 语句

单条的 insert、delete、update。

1.3.1.1 修改持久层
package com.bjsxt.dao;

import com.bjsxt.pojo.Users;

public interface UsersDao {

    //添加用户
    int insertUsers(Users users);

}
/**
 * 添加用户
 * @param users
 * @return
 */
@Override
public int insertUsers(Users users) {
    String sql = "insert into users values(default,?,?)";
    //创建封装参数的数组     数组里元素位置和占位符?位置一一对应
    Object[] params = new Object[]{users.getUsername(), users.getUsersex()};
    //通过注入进来的jdbcTemplate下的API完成参数的绑定和SQL语句的执行
    return this.jdbcTemplate.update(sql,params);
}
1.3.1.2 修改业务
package com.bjsxt.service;

import com.bjsxt.pojo.Users;

public interface UsersService {

    //添加用户
    int addUsers(Users users);
}
package com.bjsxt.service.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    private UsersDao usersDao;

    public UsersDao getUsersDao() {
        return usersDao;
    }

    public void setUsersDao(UsersDao usersDao) {
        this.usersDao = usersDao;
    }

    /**
     * 添加用户
     *
     * @param users
     * @return
     */
    @Override
    public int addUsers(Users users) {
        //直接通过注入的持久层对象调用方法
        return this.usersDao.insertUsers(users);
    }
}
1.3.1.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AddUsersTest {
    //测试类不需要注入 IOC容器
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //创建封装参数的users对象
        Users users = new Users();
        users.setUsername("伏龙芝");
        users.setUsersex("male");
        //调用方法
        int i = usersService.addUsers(users);
        System.out.println(i);
    }
}

在这里插入图片描述

在这里插入图片描述

1.3.2 批量执行 DML 语句
1.3.2.1 修改持久层
//批量添加用户
int[] batchInsertUsers(List<Users> users);
/**
 * 批量添加用户
 * @param users
 * @return
 */
@Override
public int[] batchInsertUsers(List<Users> users) {
    String sql = "insert into users values(default,?,?)";
    //通过匿名内部类new接口
    BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter() {
        //采用接口回调进行数据交互
        @Override
        public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
            //通过迭代因子i从list集合中取出每一个users,再把每一个users通过PreparedStatement绑定到SQL语句上
            Users temp = users.get(i);
            //参数绑定
            preparedStatement.setString(1, temp.getUsername());
            preparedStatement.setString(2, temp.getUsersex());
        }

        @Override
        public int getBatchSize() {
            //获取当前添加用户数量的方法
            return users.size();
        }
    };

    return this.jdbcTemplate.batchUpdate(sql,setter);
}

在这里插入图片描述

1.3.2.2 修改业务层
//批量添加用户
int[] addUsers(List<Users> users);
/**
 * 批量添加用户
 * @param users
 * @return
 */
@Override
public int[] addUsers(List<Users> users) {
    return this.usersDao.batchInsertUsers(users);
}
1.3.2.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

public class BatchUsersTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //创建封装参数的users对象
        Users users = new Users();
        users.setUsername("龙舌兰");
        users.setUsersex("male");
        //创建封装参数的users对象
        Users users1 = new Users();
        users1.setUsername("哥伦比亚");
        users1.setUsersex("male");
        List<Users> list = new ArrayList<>();
        list.add(users);
        list.add(users1);
        usersService.addUsers(list);
    }
}

在这里插入图片描述

1.3.3 查询返回单条数据
1.3.3.1 修改持久层
//根据id查询用户
Users selectUsersById(int userid);

在这里插入图片描述

在这里插入图片描述

/**
 * 根据id查询用户
 *
 * @param userid
 * @return
 */
@Override
public Users selectUsersById(int userid) {
    String sql = "select * from users where userid = ?";
    //创建封装参数的数组
    Object[] params = new Object[]{userid};
    //实现接口的processRow的方法后users对象里就有值了
    Users users = new Users();
	
    //RowCallbackHandler():新建接口而不是类
    this.jdbcTemplate.query(sql, params, new RowCallbackHandler() {
        //通过匿名内部类实现接口
        @Override
        public void processRow(ResultSet resultSet) throws SQLException {
            //结果集映射
            users.setUserid(resultSet.getInt("userid"));
            users.setUsername(resultSet.getString("username"));
            users.setUsersex(resultSet.getString("usersex"));
        }
    });
    return users;
}
1.3.3.2 修改业务层
//根据用户id查询用户
Users findUsersById(int userid);
@Override
public Users findUsersById(int userid) {
    return this.usersDao.selectUsersById(userid);
}
1.3.3.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FindUsersByIdTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        Users users = usersService.findUsersById(27);
        System.out.println(users);
    }
}

在这里插入图片描述

1.3.4 查询返回多条数据
1.3.4.1 修改持久层

在这里插入图片描述

//查询返回多条数据
List<Users> selectUsersByName(String username);
/**
 * 查询用户返回多条数据
 * @param username
 * @return
 */
@Override
public List<Users> selectUsersByName(String username) {
    String sql = "select * from users where username = ?";
    //定义数组传递参数
    Object[] params = new Object[]{username};
    /*返回一个list  list里面放的是查询到的结果集  list是在query里面定义好的*/
    return this.jdbcTemplate.query(sql, params, new RowMapper<Users>() {
        //通过匿名内部类对接口进行实现
        @Override
        public Users mapRow(ResultSet resultSet, int i) throws SQLException {
            Users users = new Users();
            //结果集映射
            users.setUserid(resultSet.getInt("userid"));
            users.setUsername(resultSet.getString("username"));
            users.setUsersex(resultSet.getString("usersex"));
            return users;
        }
    });
}
1.3.4.2 修改业务层
//根据用户名查询
List<Users> findUsersByName(String username);
@Override
public List<Users> findUsersByName(String username) {
    return this.usersDao.selectUsersByName(username);
}
1.3.4.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class FindUsersByNameTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        List<Users> users = usersService.findUsersByName("龙舌兰");
        users.forEach(System.out::println);
    }
}

在这里插入图片描述

1.3.5 查询多条数据简化版

通过 BeanPropertyRowMapper 完成对象的映射处理

1.3.5.1 修改持久层
List<Users> selectUsersByName2(String username);
/**
 * 查询用户返回多条数据
 * @param username
 * @return
 */
@Override
public List<Users> selectUsersByName2(String username) {
    String sql = "select * from users where username = ?";
    //定义数组传递参数
    Object[] params = new Object[]{username};
    //把结果集映射到哪个实体中就给哪个实体的class
    return this.jdbcTemplate.query(sql,params,new BeanPropertyRowMapper<>(Users.class));
}
1.3.5.2 修改业务层
List<Users> findUsersByName2(String username);
@Override
public List<Users> findUsersByName2(String username) {
    return this.usersDao.selectUsersByName2(username);
}
1.3.5.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class FindUsersByName2Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        List<Users> users = usersService.findUsersByName("龙舌兰");
        users.forEach(System.out::println);
    }
}

在这里插入图片描述

1.4JdbcDaoSupport 的使用
1.4.1 创建持久层
package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

public class UsersDaoImplSupport extends JdbcDaoSupport implements UsersDao {


    @Override
    public int insertUsers(Users users) {
        return 0;
    }

    @Override
    public int[] batchInsertUsers(List<Users> users) {
        return new int[0];
    }

    @Override
    public Users selectUsersById(int userid) {
        return null;
    }

    @Override
    public List<Users> selectUsersByName(String username) {
        return null;
    }

    @Override
    public List<Users> selectUsersByName2(String username) {
        String sql = "select * from users where username = ?";
        //定义数组传递参数
        Object[] params = new Object[]{username};
        //将结果集映射到哪个实体类就给哪个实体类的class
        return this.getJdbcTemplate().query(sql,params,new BeanPropertyRowMapper<>(Users.class));
    }
}
1.4.2 修改配置文件

在这里插入图片描述

在Spring—JDBC中对于jdbcTemplate的获取方式有两种:

1:直接注入到持久层

在这里插入图片描述

在这里插入图片描述

2:通过继承一个超类获取,继承超类时需要注入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/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">

    <!--配置解析properties文件的工具类-->
    <!--location:指定properties文件的位置和名称,默认去src的根目录下找-->
    <context:property-placeholder location="db.properties"/>

    <!--配置数据源对象-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--Spring的表达式语言exception是建立在EL表达式基础上的,语法结构相同-->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置持久层UsersDao-->
    <bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl">
        <!--为JdbcTemplate属性注入-->
        <property name="jdbcTemplate" ref="JdbcTemplate"/>
    </bean>

    <!--配置持久层接口实现类  UsersDaoImplSupport-->
    <bean id="usersDaoSupport" class="com.bjsxt.dao.impl.UsersDaoImplSupport">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置业务层UsersService-->
    <bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl">
        <!--为usersDao属性注入-->
        <property name="usersDao" ref="usersDaoSupport"/>
    </bean>

</beans>

有更改:

在这里插入图片描述

在这里插入图片描述

2 Spring 整合 Mybatis

2.1jar 包介绍
2.1.1 Mybatis 的 jar 包:

mybatis-3.5.5.jar
asm-7.1.jar
cglib-3.3.0.jar
commons-logging-1.2.jar
javassist-3.27.0-GA.jar
log4j-1.2.17.jar
log4j-api-2.13.3.jar
log4j-core-2.13.3.jar
ognl-3.2.14.jar
slf4j-api-1.7.30.jar
slf4j-log4j12-1.7.30.jar

2.1.2 Mybatis 整合 Spring 的 jar 包:

mybatis-spring-2.0.4.jar

2.1.3 Spring 的 jar 包:

spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
spring-jdbc-5.2.7.RELEASE.jar
spring-tx-5.2.7.RELEASE.jar
spring-aop-5.2.7.RELEASE.jar

2.1.4 数据库驱动 jar 包:

mysql-connector-java-5.1.48.jar

2.2搭建环境
2.2.1 创建项目

在这里插入图片描述

2.2.2 添加 jar 包

在这里插入图片描述

在这里插入图片描述

2.2.3 创建配置文件

配置文件放到SRC根目录下

<?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">
</beans>
2.2.4 添加 log4j 配置文件
log4j.rootLogger=debug,console

### appender.console输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out

### appender.logfile输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
2.2.5 创建映射配置文件与接口

D:\J2EE项目\MybatisProject\generatordemo\src\com\bjsxt\pojo 从现成项目里复制,或者打开逆向工程修改配置获取接口与实体

在这里插入图片描述

2.2.6 创建实体类

在这里插入图片描述

2.2.7 创建业务层
package com.bjsxt.service;

public interface UsersService {

}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {

}
2.3配置 Spring 与 Mybatis 整合
2.3.1 配置解析 properties 文件
2.3.1.1 添加 db.properties 文件 方到SRC目录下
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm
jdbc.username = root
jdbc.password = root
2.3.1.2 修改 Spring 配置文件

添加 context 命名空间

//开启命名空间
xmlns:context="http://www.springframework.org/schema/context"
//添加命名空间的约束 
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

添加context:property-placeholder/

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置解析properties文件工具类     location:指定要解析的properties文件的名称和位置-->
    <context:property-placeholder location="db.properties"/>

</beans>
2.3.2 配置数据源
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!--Spring 的表达式语言exception是建立在EL表达式基础上的,语法结构相同-->
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
2.3.3 配置 SqlSessionFactoryBean

是Mybatis提供的,在mybatis-spring.jar中

SqlSessionFactoryBean 是初始化 Mybatis 框架的 Bean 对象。它是生产 SqlSessionFactory(mybatis框架的上下文对象,一旦创建意味着启动了mybatis框架)的一种工厂 Bean。在 Spring 整合 Mybatis 中,我们可以不需要 Mybatis 的配置文件,在该Bean 对象中可以完成对 Mybatis 框架的配置。

如果需要在 Mybatis 的配置文件中配置Mybatis 框架时,仍然可以使用 Mybatis 的配置文件,但是需要在 SqlSessionFactoryBean 对象的 configLocation 属性中指定 Mybatis 的配置文件的路径和名称。

<!--配置SqlSessionFactoryBean-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--配置别名-->
    <property name="typeAliasesPackage" value="com.bjsxt.pojo"/>
    <!--引入映射配置文件    给路径而不是包名	*.xml:所有配置文件-->
    <property name="mapperLocations" value="com/bjsxt/mapper/*.xml"/>
    <!--如果有mybatis-cfg.xml配置文件      默认去SRC目录下找;-->
    <!--<property name="configLocation" value="mybatis-cfg.xml"/>-->
</bean>
2.3.4 配置 SqlSessionTemplate

是Mybatis提供的,在mybatis-spring.jar中

<!--配置SqlsessionTemplate-->
<bean id="sqlsessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <!--该类下面没有set方法只有构造方法-->
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
2.3.5 配置业务层依赖
2.3.5.1 接口实现类
//通过Spring IOC容器注入
private SqlSessionTemplate sqlSessionTemplate;

public SqlSessionTemplate getSqlSessionTemplate() {
    return sqlSessionTemplate;
}

public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSessionTemplate = sqlSessionTemplate;
}
2.3.5.2 配置文件
<!--配置业务层-->
<bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl">
    <!--注入SQLSessionTemplate-->
    <property name="sqlSessionTemplate" ref="sqlsessionTemplate"/>
</bean>
2.4SqlSessionTemplate 对象的使用
2.4.1 添加用户业务
2.4.1.1 修改业务层
/*添加用户*/
void addUsers(Users users);
/**
 * 添加用户
 * @param users
 */
@Override
public void addUsers(Users users) {
    //通过模板对象下的方法获取UsersMapper接口实现类的代理对象     使用模板对象下的方法就不需要再获取SQLSession
    UsersMapper mapper = this.sqlSessionTemplate.getMapper(UsersMapper.class);
    //因为id是自增长所以调用insertSelective
    mapper.insertSelective(users);
}
2.4.1.2 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AddUsersTest {
    public static void main(String[] args) {
        //启动Spring IOC容器    配置了SqlSessionFactoryBean     会初始化SQLSessionFactory   在启动Spring框架的同时把mybatis也启动了-
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");

        Users users = new Users();
        users.setUsername("白朗姆");
        users.setUsersex("woman");

        usersService.addUsers(users);
    }
}

在这里插入图片描述

在这里插入图片描述

更新用户
/*修改用户信息*/
void modifyUsersById(Users users);
@Override
public void modifyUsersById(Users users) {
    UsersMapper mapper = this.sqlSessionTemplate.getMapper(UsersMapper.class);
    mapper.updateByPrimaryKey(users);
}
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UpdateUsesTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");

        Users users = new Users();
        users.setUserid(26);
        users.setUsername("冰露");
        users.setUsersex("woman");

        usersService.modifyUsersById(users);
    }
}

在这里插入图片描述

删除用户
/*根据用户id删除用户*/
void deleteUsers(int userid);
@Override
public void deleteUsers(int userid) {
    UsersMapper mapper = this.sqlSessionTemplate.getMapper(UsersMapper.class);
    mapper.deleteByPrimaryKey(userid);
}
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DeleteUsersByIdTest {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");

        usersService.deleteUsers(16);
    }
}

在这里插入图片描述

2.4.2 查询用户业务
2.4.2.1 修改业务
//查询用户
List<Users> findUsersAll();
/**
 * 查询所有用户
 *
 * @return
 */
@Override
public List<Users> findUsersAll() {
    //通过模板对象下的getMapper()方法获取UsersMapper接口实现类的代理对象
    //sqlSessionTemplate对象下提供一个叫getMapper()的方法,这个方法里调用了SQLSession的getMapper()方法
    UsersMapper mapper = this.sqlSessionTemplate.getMapper(UsersMapper.class);
    //不给条件即查询所有
    UsersExample usersExample = new UsersExample();
    return mapper.selectByExample(usersExample);
}
2.4.2.2 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class FindUsersAllTest {
    public static void main(String[] args) {
        //启动Spring IOC容器    配置了SqlSessionFactoryBean     会初始化SQLSessionFactory   在启动Spring框架的同时把mybatis也启动了-
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        List<Users> users = usersService.findUsersAll();
        users.forEach(System.out::println);
    }
}

在这里插入图片描述

在这里插入图片描述

SqlSessionDaoSupport 的使用

2.5SqlSessionDaoSupport 的使用
2.5.1 创建业务层
package com.bjsxt.service.impl;

import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.pojo.UsersExample;
import com.bjsxt.service.UsersService;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

//继承了SqlSessionDaoSupport类之后就不需要注入SQLSessionTemplate,直接通过父类获取SQLSessionTemplate
/*需要给SqlSessionDaoSupport注入sqlSessionFactoryBean对象,
这样SqlSessionDaoSupport就可以拿着SQLSessionFactory获取SQLSession,
这样SqlSessionDaoSupport所提供的SQLSessionTemplate对象就可以获取到SQLSession对象
就可以通过SQLSession对象操作数据库*/
public class UsersServiceImpl2 extends SqlSessionDaoSupport implements UsersService {


    @Override
    public void addUsers(Users users) {

    }

    @Override
    public List<Users> findUsersAll() {
        //调用SqlSessionDaoSupport中的getSqlSessionTemplate()方法获取SQLSessionTemplate
        UsersMapper mapper = this.getSqlSessionTemplate().getMapper(UsersMapper.class);
        //不给条件即查询所有
        UsersExample usersExample = new UsersExample();
        return mapper.selectByExample(usersExample);
    }
}
2.5.2 修改配置文件
<!--配置业务层 UsersServiceImpl2-->
<bean id="usersService2" class="com.bjsxt.service.impl.UsersServiceImpl2">
    <!--注入sqlSessionFactoryBean-->
    <property name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
2.5.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class FindUsersAll2Test {
    public static void main(String[] args) {
        //启动Spring IOC容器    配置了SqlSessionFactoryBean     会初始化SQLSessionFactory   在启动Spring框架的同时把mybatis也启动了-
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService2");
        List<Users> users = usersService.findUsersAll();
        users.forEach(System.out::println);
    }
}

在这里插入图片描述

在这里插入图片描述

2.6MapperScannerConfigurer 对象的使用

在mybatis-Spring.jar包中,作用是帮助自动生成接口代理对象,生成的代理对象被放到Spring IOC容器中,会使用接口的名字作为这个bean对象(接口代理对象)的id

用于以自动扫描形式来配置 MyBatis 中映射器对象(mybatis中接口的代理对象),可以通过配置包路径来自动扫描包接口生成映射器对象。

2.6.1 创建业务层
package com.bjsxt.service.impl;

import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.pojo.UsersExample;
import com.bjsxt.service.UsersService;

import java.util.List;

public class UsersServiceImpl3 implements UsersService {

    /*通过Spring IOC容器注入*/
    private UsersMapper usersMapper;

    public void setUsersMapper(UsersMapper usersMapper) {
        this.usersMapper = usersMapper;
    }

    @Override
    public void addUsers(Users users) {

    }

    @Override
    public void modifyUsersById(Users users) {

    }

    @Override
    public void deleteUsersById(int userid) {

    }

    @Override
    public List<Users> findUsersAll() {
        UsersExample usersExample = new UsersExample();
        /*直接通过注入的UsersMapper调用查询方法*/
        return this.usersMapper.selectByExample(usersExample);
    }
}
2.6.2 创建配置文件

usersMapper对应的就是接口的代理对象(映射器对象),在Spring IOC容器中已经存在了就不需要getMapper()获取了,可以直接注入给业务层

在这里插入图片描述

给的是包名而不是路径,如果给错了则找不到Spring IOC容器中的UsersMapper

在这里插入图片描述

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">  

    <!--配置解析properties文件工具类     location:指定要解析的properties文件的名称和位置-->
    <context:property-placeholder location="db.properties"/>

    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--Spring 的表达式语言exception是建立在EL表达式基础上的,语法结构相同-->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置SqlSessionFactoryBean     会初始化SQLSessionFactory   在启动Spring框架的同时把mybatis也启动了-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置别名-->
        <property name="typeAliasesPackage" value="com.bjsxt.pojo"/>
        <!--mapperLocations:引入映射配置文件    给路径而不是包名-->
        <!--如果使用MapperScannerConfigurer扫描接口,接口和映射配置文件在一个包下就不需要配置mapperLocation-->
        <!--<property name="mapperLocations" value="com/bjsxt/mapper/*.xml"/>-->
        <!--如果有mybatis-cfg.xml配置文件      默认去SRC目录下找;-->
        <!--<property name="configLocation" value="mybatis-cfg.xml"/>-->
    </bean>

    <!--为了将UsersMapper注入进来还得配置UsersServiceImple3-->
    <bean id="usersService3" class="com.bjsxt.service.impl.UsersServiceImpl3">
        <property name="usersMapper" ref="usersMapper"/>
    </bean>

    <!--因为现在可以直接拿到接口的代理对象就不需要调用getMapper()方法获取了,所以就用不到SQLSessionTemplate-->

    <!--配置MapperScannerConfigurer-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--会自动注入SQLSessionFactory-->
        <!--basePackage:指定需要生成代理对象的接口在哪个包下-->
        <property name="basePackage" value="com.bjsxt.mapper"/>
    </bean>

</beans>
2.6.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MapperScannerConfigurerTest {
    public static void main(String[] args) {
        //启动Spring IOC容器    配置了SqlSessionFactoryBean     会初始化SQLSessionFactory   在启动Spring框架的同时把mybatis也启动了-
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
        
        //返回Spring IOC容器下所有bean对象的id
        /*String[] names = applicationContext.getBeanDefinitionNames();
        for (String str:names){
            System.out.println(str);
        }*/

        UsersService usersService3 = (UsersService) applicationContext.getBean("usersService3");
        List<Users> users = usersService3.findUsersAll();
        users.forEach(e -> System.out.println(e));
    }
}

在这里插入图片描述

五、 代理模式

1 什么是代理模式

代理模式(Proxy Pattern):代理模式是 Java 常见的设计模式之一。所谓代理模式是指客
户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。通俗的
来讲代理模式就是我们生活中常见的中介。

(客户去房产中介找业务员租房子)

在这里插入图片描述

2 为什么使用代理模式

隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类
对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
开闭原则:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。

代理模式的优点:
 代理模式能将代理对象与真实对象被调用的目标对象分离。
 一定程度上降低了系统的耦合度,扩展性好。
 保护目标对象。
 增强目标对象。

代理模式的缺点:
 代理模式会造成系统设计中类的数目的增加。
 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
 增加了系统的复杂度。

3 代理模式的实现

代理模式可以分为两种:静态代理、动态代理。

3.1静态代理

静态代理缺点:只能为某一种业务做代理,如果有新的业务只能创建新的代理对象才能对新的业务做代理

静态代理模式由三个部分构成:
 一个公共的接口 (代理对象能实现哪些业务的代理)
 一个被代理角色
 一个代理角色

在这里插入图片描述

3.1.1 创建公共的接口

(中介是代理对象,房东是被代理对象,中介可以完成房屋出租)

package com.bjsxt.staticproxy;

public interface Rent {

    void renting();
}
3.1.2 创建被代理角色
package com.bjsxt.staticproxy;

public class LJX implements Rent {

    @Override
    public void renting() {
        System.out.println("临江仙有一栋别墅需出租");
    }
}
3.1.3 创建代理角色

在这里插入图片描述

package com.bjsxt.staticproxy;

/**
 * 中介
 */
public class StaticProxyRent implements Rent{

    //被代理对象
    private Rent rent;
    //通过构造方法传递被代理对象     这样代理对象就知道被代理的对象是谁了
    public StaticProxyRent(Rent rent){
        this.rent = rent;
    }

    @Override
    public void renting() {
        System.out.println("中介向房客出租房屋");
        //房东出租房屋
        this.rent.renting();
        System.out.println("中介完成售后服务");
    }
}
3.1.4 创建测试类
package com.bjsxt.staticproxy;

public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理对象——房东
        Rent rent = new LJX();
        //创建代理对象——中介        rent:指定被代理对象
        StaticProxyRent staticProxyRent = new StaticProxyRent(rent);
        //中介租房
        staticProxyRent.renting();
    }
}
3.2动态代理的实现

在动态代理中分为两种实现方式:
 使用 JDK 的 Proxy 类实现动态代理
 使用 CGLIB 实现动态代理

3.2.1 使用 JDK 的 Proxy 类实现动态代理

代码在运行时动态的生成代理对象,动态代理可以对任何的对象实现代理处理,可以根据不同的业务对象生成不同的代理对象

3.2.1.1 创建业务接口
package com.bjsxt.dunamicproxy.jdkproxy;

public interface JdkProxyRent {
    void renting();
}
3.2.1.2 创建接口实现类(被代理对象)

使用 JDK 的 Proxy 类实现动态代理被代理的对象必须实现公共接口

package com.bjsxt.dunamicproxy.jdkproxy;

public class JdkProxyLJX implements JdkProxyRent {

    @Override
    public void renting() {
        System.out.println("临江仙有房出租");
    }
}
3.2.1.3 创建生成代理对象的工厂

在这里插入图片描述

java的getClass()函数
Java反射学习

    Java语言中,每个类都有一个函数是getClass(),这个函数可以返回该保存该类型的类型类的一个实例的引用,这个类型类的实例无法直接接受但是我们可以用Object接收
    
    所谓反射,可以理解为在运行时期获取对象类型信息的操作。传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码。严格地说,反射并非编程语言的特性,因为在任何一种语言都可以实现反射机制,但是如果编程语言本身支持反射,那么反射的实现就会方便很多。

1,获得类型类

    我们知道在Java中一切都是对象,我们一般所使用的对象都直接或间接继承自Object类。Object类中包含一个方法名叫getClass,利用这个方法就可以获得一个实例的类型类。类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。例如,有如下一段代码:

A a = new A();

if(a.getClass()==A.class)

System.out.println("equal");

else System.out.println("unequal");

结果就是打印出 “equal”。

    可以看到,对象a是A的一个实例,A某一个类,在if语句中使用a.getClass()返回的结果正是A的类型类,在Java中表示一个特定类型的类型类可以用“类型.class”的方式获得,因为a.getClass()获得是A的类型类,也就是A.class,因此上面的代码执行的结果就是打印出 “equal”。
    特别注意的是,类型类是一一对应的,父类的类型类和子类的类型类是不同的,因此,假设A是B的子类,那么如下的代码将得到 “unequal”的输出:

A a = new A();

if(a.getClass()==B.class)

System.out.println("equal");

else System.out.println("unequal");

因此,如果你知道一个实例,那么你可以通过实例的“getClass()”方法获得该对象的类型类,如果你知道一个类型,那么你可以使用“.class”的方法获得该类型的类型类。

2,获得类型的信息

在获得类型类之后,你就可以调用其中的一些方法获得类型的信息了,主要的方法有:

getName():String:获得该类型的全称名称。

getSuperClass():Class:获得该类型的直接父类,如果该类型没有直接父类,那么返回null。

getInterfaces():Class[]:获得该类型实现的所有接口。

isArray():boolean:判断该类型是否是数组。
			
isEnum():boolean:判断该类型是否是枚举类型。

isInterface():boolean:判断该类型是否是接口。

isPrimitive():boolean:判断该类型是否是基本类型,即是否是intbooleandouble等等。

isAssignableFrom(Class cls):boolean:判断这个类型是否是类型cls的父(祖先)类或父(祖先)接口。

getComponentType():Class:如果该类型是一个数组,那么返回该数组的组件类型。

此外还可以进行类型转换这类的操作,主要方法有:

asSubclass(Class clazz):Class:将这个类型

分类: java

在这里插入图片描述

package com.bjsxt.dunamicproxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyFactory {
    /**
     * 动态生成代理对象
     */
    /*动态生成的代理对象类型不一样	传入目标对象*/
    public static Object getProxyBean(Object target) {
        //获取目标对象的类型类
        Class clazz = target.getClass();
        //实例化增强的对象
        MyAspect myAspect = new MyAspect();

        /**
         * 在JDK中动态生成代理对象的方法
         * clazz.getInterfaces():类实现的所有接口
         * clazz.getClassLoader():获取类加载器
         */
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
            /**
             * 动态生成代理对象中的方法。通用方法,先调用通用方法再调用目标对象下的方法
             *
             * @param proxy  动态生成的代理对象
             * @param method 调用目标方法的方法对象
             * @param args   传递到目标方法中的参数列表
             * @return
             * @throws Throwable
             */
            //通过匿名内部类实现接口
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //调用增强方法
                myAspect.before();
                //执行目标方法	传入目标对象和参数列表
                Object obj = method.invoke(target, args);
                //调用增强方法
                myAspect.after();
                return obj;
            }
        });
    }
}

增强对象

package com.bjsxt.dunamicproxy.jdkproxy;

/**
 * 对目标对象增强的对象
 */
public class MyAspect {
    public void before(){
        System.out.println("带领房客看房子。。。。。签租房协议");
    }
    public void after(){
        System.out.println("售后服务。。。。。。。。。。。");
    }
}
3.2.1.4 创建切面对象

对目标对象做哪些功能扩展(增强)

package com.bjsxt.dunamicproxy.jdkproxy;

public interface JdkProxyRent {
    void renting();
}
3.2.1.5 创建测试类
package com.bjsxt.dunamicproxy.jdkproxy;

public class JdkProxyTest {
    public static void main(String[] args) {
        //实例化目标对象   用接口类型定义
        JdkProxyRent jdkProxyRent = new JdkProxyLJX();
        //通过工厂下的方法生成代理对象
        JdkProxyRent jdkProxyRent1 = (JdkProxyRent) JdkProxyFactory.getProxyBean(jdkProxyRent);
        //调用目标方法    不是立即执行目标方法而是先执行代理工厂下的代理对象下的invoke()方法再执行目标方法
        jdkProxyRent1.renting();
    }
}

在这里插入图片描述

3.2.2 使用 CGLIB 实现动态代理
(性能更强)

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多框架所使
用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字
节码并生成新的类。因此 CGLIB 要依赖于 ASM 的包。
JDK 的动态代理机制只能代理实现了接口的类,而对于没有实现接口的类就不能使用
**JDK 的 Proxy 类生成代理对象,**cglib 是针对类来实现代理的,他的原理是对指定的目标类生
成一个子类并通过回调的方式来实现增强,但因为采用的是继承,所以不能对 final 修饰的
类进行代理。(一个类被final修饰表示是最终的类不予许被继承)

3.2.2.1 添加 jar 包

cglib.jar
asm.jar

3.2.2.2 创建业务接口

在cglib中使用的是继承+回调实现动态代理,目标对象是否实现接口都无所谓

package com.bjsxt.dunamicproxy.cglibproxy;

public interface CglibProxyRent {
    void renting();
}
3.2.2.3 创建接口实现类

被代理对象

package com.bjsxt.dunamicproxy.cglibproxy;

public class CglibProxyLJX implements CglibProxyRent {

    @Override
    public void renting() {
        System.out.println("临江仙有房子出租");
    }
}
3.2.2.4 创建生成代理对象的工厂
package com.bjsxt.dunamicproxy.cglibproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyFactory {
    /* rent:给定需要生成代理对象的目标对象*/
    public static Object getProxyBean(CglibProxyRent rent) {
        //实例化增强对象
        CglibMyAscept myAscept = new CglibMyAscept();
        //创建代理对象的模板对象,在该对象中完成创建代理对象之前的设置
        Enhancer enhancer = new Enhancer();
        //设置超类  目标对象是超类(可继承的)
        enhancer.setSuperclass(rent.getClass());
        //设置回调  直接new接口表示采用匿名内部类的方式实现接口
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * @param o           代理对象的引用
             * @param method      目标对象的方法对象
             * @param objects     目标方法的参数列表
             * @param methodProxy 目标方法的方法对象的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                myAscept.before();
                //调用目标方法    param1:方法的目标对象的引用   param2:目标方法的参数列表
                Object obj = method.invoke(rent, objects);
                myAscept.after();
                return obj;
            }
        });
        //动态创建代理对象的方法
        return enhancer.create();
    }
}
3.2.2.5 创建切面
package com.bjsxt.dunamicproxy.cglibproxy;

public class CglibMyAscept {
    public void before(){
        System.out.println("带领客户看房。。。。签订住房协议");
    }

    public void after(){
        System.out.println("售后服务");
    }
}
3.2.2.6 创建测试类
package com.bjsxt.dunamicproxy.cglibproxy;

public class Test {
    public static void main(String[] args) {
        //实例化目标对象
        CglibProxyRent rent = new CglibProxyLJX();
        //通过工厂获取代理对象    返回的是object类型需强转
        CglibProxyRent rent1 = (CglibProxyRent) CglibProxyFactory.getProxyBean(rent);
        //通过代理对象调用方法
        rent1.renting();
    }
}

在这里插入图片描述

六、 AOP 编程

1 AOP 简介

1.1什么是 AOP

AOP 的全称是 Aspect Oriented Programming,即面向切面编程,它将业务逻辑的各个部
分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在
事务处理、日志管理、权限控制、异常处理等方面。
目前最流行的 AOP 技术有两个,分别为 Spring 框架的 AOP 和 AspectJ 框架。

1.2什么是面向切面编程

把一个个的横切关注点放到某个模块中去,称之为切面。每个切面影响业务的一种功能,
切面的目的就是为了功能增强,将需要增强的方法做成切面,实现对业务的增强,就是面向切面编程。
面向切面编程的目的:将与业务本身无关,却被业务模块所共同调用的功能代码封装成切面,以减少系统的重复代码,降低耦合,提高可扩展性。
面向切面编程的优势:把多个方法前/后的共同代码抽离出来,使用动态代理机制来控
制,先执行抽离出来的代码,再执行每一个真实方法。

在这里插入图片描述

2 AOP 术语

为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含
Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。

在Spring AOP编程中只能以方法作为连接点任何方法都可以成为连接点,因为可以在方法执行过程中添加切面

在这里插入图片描述

在这里插入图片描述

3 Spring AOP 模块

3.1Spring AOP 模块中的通知类型名称

在这里插入图片描述

3.2Spring AOP 模块的使用

在使用 Spring 框架的 AOP 模块开发 AOP 时,需要添加核心容器的 jar 包以及 aop 的 jar包。

在这里插入图片描述

在这里插入图片描述

注意:ThrowsAdvice 接口是一个标识接口没有任何抽象方法。如果通知类型定义为异常
通知,那么除了要实现 ThrowsAdvice 以外,还需要在切面中添加下面 4 个方法中的一个,并在该方法中实现具体的增强处理。
public void afterThrowing(Exception ex)
public void afterThrowing(RemoteException)
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
public void afterThrowing(Method method, Object[] args, Object target, ServletExceptionex)

在这里插入图片描述

在这里插入图片描述

3.2.1 创建切面

别倒错包

在这里插入图片描述

package com.bjsxt.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

//实现通知类型接口     学习的时候都实现了
public class MyAscept implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice {

    /**
     * 前置通知——在目标方法被调用之前调用
     *
     * @param method  目标方法对象
     * @param objects 目标方法的参数列表
     * @param o       目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        //获取目标方法的方法名
        System.out.println("Before......." + method.getName());
    }

    /**
     * 后置通知——调用目标方法之后会被实现
     * @param o     目标方法的返回值
     * @param method    目标方法对象
     * @param objects   目标方法的参数列表
     * @param o1    目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        //获取目标方法的方法名
        System.out.println("After......." + method.getName());
    }

    /**
     * 环绕通知———包含前置通知和后置通知
     *环绕通知若去掉前置通知则变为后置通知,反之同理
     * @param methodInvocation 目标方法对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //前置通知               先拿到目标方法的对象再获取名称
        System.out.println("Around,,,,,Before" + methodInvocation.getMethod().getName());
        //调用目标方法    若目标方法有返回值则返回值为obj
        Object obj = methodInvocation.proceed();
        //后置通知
        System.out.println("Around.....After"+methodInvocation.getMethod().getName());
        return obj;
    }

    /**
     * 异常通知
     * @param ex    目标方法执行过程中抛出的异常对象
     */
    public void afterThrowing(Exception ex){
        System.out.println(ex.getMessage());
    }
}
3.2.2 配置切面

切面是对业务层实现增强

Spring 的 AOP 模块实现 AOP 编程的方式:
使用 org.springframework.aop.framework.ProxyFactoryBean 工厂对象创建代理对象。

3.2.2.1 创建目标对象
package com.bjsxt.service;

public interface UsersService {
    void addUsers();
}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {

    @Override
    public void addUsers() {
        /*String str = null;
        str.length();*/
        System.out.println("目标方法。。。。。。。。。。。。。。。addUsers...............");
    }
}
3.2.2.2 修改 Spring 配置文件

AOP编程缺点:ProxyFactoryBean:一次只能为一个目标对象生成代理对象

<?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="usersService" class="com.bjsxt.service.impl.UsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="myAscept" class="com.bjsxt.aop.MyAscept"/>

    <bean id="toUpperCaseAscept" class="com.bjsxt.aop.ToUppercaseAscept"/>

    <!--配置切面-->
    <bean id="usersServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

        <!--配置目标对象所实现的接口-->
        <property name="proxyInterfaces" value="com.bjsxt.service.UsersService"/>
        <!--配置目标对象-->
        <property name="target" ref="usersService"/>
        <!--
        配置切面对象
        该属性是String[]类型(因为一个目标对象可能被多个切面拦截),如果有多个切面就把每个切面的id配置到list标签下的value属性
        切面的执行顺序按照配置的上下顺序
        -->
        <property name="interceptorNames">
            <list>
                <value>myAscept</value>
                <value>toUpperCaseAscept</value>
            </list>
        </property>
        
        <!--如何生成代理对象    true:CGLIB  false:JDK的Proxy(默认)-->
        <property name="proxyTargetClass" value="true"/>
    </bean>


</beans>
3.2.2.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取ProxyFactoryBean方法所生成的代理对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersServiceProxy");
        //代理对象调用方法
        usersService.addUsers();
    }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.2.3 Spring AOP 模块的使用案例

需求:要求在业务层的 updateUsers 方法执行之前,将 UserName 参数转换大写。

3.2.3.1 修改业务层
void updateUsers(String username);
//目标方法
@Override
public void updateUsers(String username) {
    System.out.println("updateUsers。。。。。"+username);
}
3.2.3.2 创建切面
package com.bjsxt.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class ToUppercaseAscept implements MethodInterceptor {


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //将username转换成大写
        //获取目标方法的参数列表	此目标方法只有一个参数所以数组第一个元素就是了
        Object[] args = methodInvocation.getArguments();
        //toUpperCase()方法是产生一个新的字符串并不是在原来的字符串上做修改,所以还得放回去,这样传递到目标方法中的username才是转换成大写以后的		参数类型是Object[]类型所以需强转
        
        args[0] = ((String) args[0]).toUpperCase();
        //调用目标方法
        Object obj = methodInvocation.proceed();
        return obj;
    }
}
3.2.3.3 配置切面
<!--配置切面对象-->
<bean id="toUpperCaseAscept" class="com.bjsxt.aop.ToUppercaseAscept"/>
<!--
        配置切面对象
        该属性是String[]类型(因为一个目标对象可能被多个切面拦截),如果有多个切面就把每个切面的id配置到list标签下的value属性
        切面的执行顺序按照配置的上下顺序
        -->
<property name="interceptorNames">
    <list>
        <value>myAscept</value>
        <value>toUpperCaseAscept</value>
    </list>
</property>
3.2.3.4 测试类
package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取ProxyFactoryBean方法所生成的代理对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersServiceProxy");
        //代理对象调用方法
        usersService.updateUsers("ljx");
    }
}

给目标对象定义了两个切面对象注意切面执行顺序

在这里插入图片描述

4 AspectJ 框架

4.1AspectJ 框架简介

AspectJ 是一个基于 Java 语言的 AOP 框架。在 Spring 2.0 以后,新增了对 AspectJ 框
架的支持。在 Spring 框架中建议使用 AspectJ 框架开发 AOP。

4.1.1 AspectJ 框架中的通知类型

在这里插入图片描述

4.1.2 Spring 整合 AspectJ 框架所依赖的 Jar 包
4.1.2.1 AspectJ 框架 jar 包

aspectjweaver-1.9.5.jar

4.1.2.2 Spring 框架 jar 包

spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
spring-aop-5.2.7.RELEASE.jar
spring-aspects-5.2.7.RELEASE.jar
commons-logging-1.2.jar

4.1.3 AspectJ 框架配置 AOP 方式

 通过 XML 文件配置 AOP
 通过 AspectJ 配置方式
 通过 Spring 的 Schema_based 方式
 通过注解配置 AOP

4.2AspectJ 框架的使用

在 AspectJ 框架中使用 xml 文件中配置 AOP 的方式:
 AspectJ 配置方式
 Schema-based 配置方式

4.2.1 AspectJ 配置方式

AspectJ 配置方式是指使用 AspectJ 框架的配置方式来配置切面。在使用 AspectJ 配置切面时,切面不需要实现一些特定的接口。(通知类型接口)

在这里插入图片描述

在这里插入图片描述

4.2.1.1 创建切面

在这里插入图片描述

package com.bjsxt.aspectj.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//使用aspectj配置时不需要实现通知接口
public class MyAspect {
    /**
     * 前置通知
     *
     * @param joinPoint 对目标对象的封装
     */
    public void myBefore(JoinPoint joinPoint) {
        /*//获取目标对象
        joinPoint.getTarget();
        //获取目标对象拦截的方法的方法名   获取目标方法名
        joinPoint.getSignature().getName();
        //获取目标方法的参数列表   返回一个object类型数组
        joinPoint.getArgs();
        //获取代理对象
        joinPoint.getThis();*/
        System.out.println("Before "+joinPoint.getSignature().getName());
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("After "+joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知必须有返回值
     * ProceedingJoinPoint:继承了JoinPoint,对其扩展
     *
     * @param proceedingJoinPoint
     * @return
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around Before "+proceedingJoinPoint.getSignature().getName());
        //调用目标方法    会抛出异常
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("Around After "+proceedingJoinPoint.getSignature().getName());
        return obj;
    }

    /**
     * 异常通知
     * @param e
     */
    public void myAfterThrowing(Exception e){
        System.out.println("Exception "+e);
    }

    /**
     * 最终通知
     */
    public void myAfter(){
        System.out.println("最终通知");
    }
}
4.2.1.2 Execution 表达式

Execution 是 AspectJ 框架中的一种表达式,用于配置切点。(拦截哪些目标对象的目标方法)基本语法格式为:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
其中<修饰符模式>与<异常模式>为可选。
execution(public * com.bjsxt.service….(…))
说明:

在这里插入图片描述

示例 1:
execution(public * com.bjsxt.service.impl.Student.test())
切点是 com.bjsxt.service.impl 包中 Student 类中所有 public 修饰的无参数的 test方法。
示例 2:
execution(* com.bjsxt.service.impl.Student.test(String,int))
切点是 com.bjsxt.service.impl 包中 Student 类中参数类型是 String,int 类型的 test方法。
示例 3:
execution(* com.bjsxt.service.impl.Student.test(…))
切点是 com.bjsxt.service.impl 包中 Student 类中任意参数的 test 方法。
示例 4
execution(* com.bjsxt.service..(…))
切点是 com.bjsxt.service 包中所有接口中任意参数的所有方法。

4.2.1.3 使用 AspectJ 方式配置切面

在<aop:aspect 标签里配置切面

4.2.1.3.1 创建目标对象

package com.bjsxt.aspectj.service;

public interface UsersService {
    void addUsers(String username);
}
package com.bjsxt.aspectj.service.impl;

import com.bjsxt.aspectj.service.UsersService;

public class UsersServiceImpl implements UsersService {

    @Override
    public void addUsers(String username) {
        //人为异常
        /*String str = null;
        str.length();*/
        System.out.println("AddUsers            " + username);
    }
}

4.2.1.3.2 开启 aop 命名空间

在这里插入图片描述

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

4.2.1.3.3 配置切面

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

    <!--配置目标对象-->
    <bean id="usersService" class="com.bjsxt.aspectj.service.impl.UsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="myAspect" class="com.bjsxt.aspectj.aop.MyAspect"/>

    <!--配置切面-->
    <aop:config>
        <!--指定为哪个切面做配置-->
        <aop:aspect ref="myAspect">
            <!--配置切点    对哪些对象的哪些方法做增强   service包下所有的接口的所有的方法,如果方法是无参的去掉..-->
            <aop:pointcut id="myPointcut" expression="execution(* com.bjsxt.aspectj.service.*.*(..))"/>
            <!--配置前置通知  method:指定实现前置通知的方法  pointcut-ref:配置切点,把前置通知和切点关联起来-->
            <aop:before method="myBefore" pointcut-ref="myPointcut"/>
            <!--配置后置通知-->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
            <!--配置环绕通知-->
            <aop:around method="myAround" pointcut-ref="myPointcut"/>
            <!--配置异常通知  throwing:当前异常通知方法的参数名字  若目标方法出现异常会把异常对象通过这个参数传递到目标方法中-->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
            <!--配置最终通知-->
            <aop:after method="myAfter" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

4.2.1.3.4 创建测试类

package com.bjsxt.aspectj.test;

import com.bjsxt.aspectj.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContextAspectj.xml");
        //获取业务层bean对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        usersService.addUsers("kgb");
    }
}

在这里插入图片描述

目标方法

在这里插入图片描述

4.2.1.4 多切面以及切面执行顺序的配置

在 AspectJ 配置方式中,可以添加多个aop:aspect标签实现多切面配置。在aop:aspect
标签中包含 order 属性用于配置执行切面的执行顺序。
4.2.1.4.1 创建切面

package com.bjsxt.aspectj.aop;

import org.aspectj.lang.JoinPoint;

public class MyAspect2 {
    //添加前置通知
    public void myAspectBefore(JoinPoint joinPoint){
        //打印目标方法名
        System.out.println("myAspect2  Before  "+joinPoint.getSignature().getName());
    }
}

4.2.1.4.2 配置多切面

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

    <!--配置目标对象-->
    <bean id="usersService" class="com.bjsxt.aspectj.service.impl.UsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="myAspect" class="com.bjsxt.aspectj.aop.MyAspect"/>

    <!--配置切面对象-->
    <bean id="myAspect2" class="com.bjsxt.aspectj.aop.MyAspect2"/>

    <!--配置切面-->
    <aop:config>
        <!--指定为哪个切面做配置      如果只有一个切面不需要加id      order:给定的整数越小执行的优先级越高-->
        <aop:aspect id="my1" ref="myAspect" order="2">
            <!--配置切点    对哪些对象的哪些方法做增强   service包下所有的接口的所有的方法,如果方法是无参的去掉..-->
            <aop:pointcut id="myPointcut" expression="execution(* com.bjsxt.aspectj.service.*.*(..))"/>
            <!--配置前置通知  method:指定实现前置通知的方法  pointcut-ref:配置切点,把前置通知和切点关联起来-->
            <aop:before method="myBefore" pointcut-ref="myPointcut"/>
            <!--配置后置通知-->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
            <!--配置环绕通知-->
            <aop:around method="myAround" pointcut-ref="myPointcut"/>
            <!--配置异常通知  throwing:当前异常通知方法的参数名字  若目标方法出现异常会把异常对象通过这个参数传递到目标方法中-->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
            <!--配置最终通知-->
            <aop:after method="myAfter" pointcut-ref="myPointcut"/>
        </aop:aspect>
        
        <!--指定需配置的切面-->
        <aop:aspect id="my2" ref="myAspect2" order="1">
            <!--配置切点-->
            <aop:pointcut id="myPointcut2" expression="execution(* com.bjsxt.aspectj.service.*.*(..))"/>
            <!--配置前置通知-->
            <aop:before method="myAspectBefore" pointcut-ref="myPointcut2"/>
        </aop:aspect>
        
    </aop:config>

</beans>

执行顺序与order属性相关,并非第一个切面执行完再执行第二个切面,具体执行哪个方法取决于切面里面的通知类型

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.2 Schema_based 配置方式

Schema_based(基础模式)配置方式是指使用 Spring AOP 模块来定义切面并在 AspectJ 框架中对该切面进行配置。要求切面在定义通知类型时,需要实现特定接口。

在这里插入图片描述

4.2.2.1 创建切面
package com.bjsxt.schema_based.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

public class BasedMyAspect implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice {

    /**
     * 前置通知
     * @param method
     * @param objects
     * @param o
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {

    }

    /**
     * 后置通知
     * @param o
     * @param method
     * @param objects
     * @param o1
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("After.");
    }

    /**
     * 环绕通知
     *
     * @param methodInvocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("Around Before..........");
        //调用目标方法
        Object obj = methodInvocation.proceed();
        System.out.println("Around After...........");
        return obj;
    }

    /**
     * 异常通知,方法名必须是四个中的一个,不可为其他的
     * @param e
     */
    public void afterThrowing(Exception e){
        System.out.println("Exception:"+e);
    }
}

4.2.2.2 使用 Schema_based 配置方式配置切面

aspectj配置切面时切面本身不需要实现任何通知类型接口所以在配置文件中就得定义通知类型,而使用Schema_based配置时由于切面已经实现了通知类型接口所以配置文件中就不需要指定通知类型接口直接通过 aop:advisor 标签指定切面即可

4.2.2.2.1 创建目标对象

package com.bjsxt.schema_based.service;

public interface BasedUsersService {
    void addUsers(String username);
}
package com.bjsxt.schema_based.service.impl;

import com.bjsxt.schema_based.service.BasedUsersService;

public class BasedUsersServiceImpl implements BasedUsersService {
    /**
     * 目标方法
     *
     * @param username
     */
    @Override
    public void addUsers(String username) {
        //人为异常
        /*String str = null;
        str.length();*/
        System.out.println("addUsers...." + username);
    }
}

4.2.2.2.2 开启 aop 命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

4.2.2.2.3 配置切面

applicationContextBased.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置目标对象-->
    <bean id="basedUsersService" class="com.bjsxt.schema_based.service.impl.BasedUsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="basedMyAspect" class="com.bjsxt.schema_based.aop.BasedMyAspect"/>

    <!--配置切面    之前使用<aop:aspect标签是因为切面没有实现通知类型接口    现在的切面已经实现类通知接口就不需要用这个标签配置-->
    <aop:config>
        <!--配置切点	对哪些对象下的哪些方法做增强-->
        <aop:pointcut id="basedMyPointcut" expression="execution(* com.bjsxt.schema_based.service.*.*(..))"/>
        <!--配置切面    指定切面    指定对哪些目标的哪些方法做增强-->
        <aop:advisor advice-ref="basedMyAspect" pointcut-ref="basedMyPointcut"/>
    </aop:config>

</beans>

4.2.2.2.4 创建测试类

package com.bjsxt.schema_based.test;

import com.bjsxt.schema_based.service.BasedUsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContextBased.xml");
        BasedUsersService basedUsersService = (BasedUsersService) applicationContext.getBean("basedUsersService");
        basedUsersService.addUsers("kgb");
    }
}

在这里插入图片描述

人为异常

在这里插入图片描述

4.2.2.3 多切面以及切面执行顺序的配置

在 Schema_based 配置方式中,可以添加多个aop:advisor标签实现多切面配置。在
aop:advisor标签中包含 order 属性用于配置执行切面的执行顺序。
4.2.2.3.1 创建切面

package com.bjsxt.schema_based.aop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BasedMyAspect2 implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("BasedMyAspect2....Before");
    }
}

4.2.2.3.2 配置多切面

在这里插入图片描述

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

    <!--配置目标对象-->
    <bean id="basedUsersService" class="com.bjsxt.schema_based.service.impl.BasedUsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="basedMyAspect" class="com.bjsxt.schema_based.aop.BasedMyAspect"/>

    <!--配置切面对象-->
    <bean id="basedMyAspect2" class="com.bjsxt.schema_based.aop.BasedMyAspect2"/>

    <!--配置切面    之前使用<aop:aspect标签是因为切面没有实现通知类型接口    现在的切面已经实现类通知接口就不需要用这个标签配置-->
    <aop:config>
        <!--配置切点    可以配置多个切点-->
        <aop:pointcut id="basedMyPointcut" expression="execution(* com.bjsxt.schema_based.service.*.*(..))"/>
        <aop:pointcut id="basedMyPointcut2" expression="execution(* com.bjsxt.schema_based.service.*.*(..))"/>
        <!--配置切面    指定切面    指定对哪些目标的哪些方法做增强     如果有多个切面需加上id属性  order:指定执行顺序,要整数,越小优先级越高-->
        <aop:advisor id="my1" advice-ref="basedMyAspect" pointcut-ref="basedMyPointcut" order="2"/>
        <aop:advisor id="my2" advice-ref="basedMyAspect2" pointcut-ref="basedMyPointcut" order="1"/>
    </aop:config>

</beans>

注意执行顺序

在这里插入图片描述

在这里插入图片描述

4.2.3 注解配置方式

AspectJ 框架允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并
根据这些注解生成 AOP 代理。

4.2.3.1 常用注解

如果使用注解开发,必须使用@Aspect指定切面

在这里插入图片描述

在这里插入图片描述

4.2.3.2 创建切面
package com.bjsxt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

//如果使用注解开发,必须在切面上添加@Aspect注解,这样Aspect框架才能识别它是切面
@Aspect//指定当前对象为切面对象
public class MyAspect {

    /**
     * 配置切点
     */
    @Pointcut("execution(* com.bjsxt.service.*.*(..))")
    public void myPointcut(){

    }

    /**
     * 前置通知
     * @param joinPoint
     * 在注解开发中切点是可以方法通知类型的注解中,如果只有一个切点需要配置去掉value属性也行
     */
    /*@Before(value = "execution(* com.bjsxt.service.*.*(..))")*/
    @Before("myPointcut()")//通过给定配置切点的方法名来指定对哪些目标对象做增强处理
    public void myBefore(JoinPoint joinPoint){
        /*获取目标方法名称*/
        System.out.println("Before............................."+joinPoint.getSignature().getName());
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    /*@AfterReturning("execution(* com.bjsxt.service.*.*(..))")*/
    @AfterReturning("myPointcut()")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("afterReturnning..."+joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知必须有返回值
     * @param proceedingJoinPoint   继承了JoinPoint
     * @return
     */
    /*@Around("execution(* com.bjsxt.service.*.*(..))")*/
    @Around("myPointcut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知     AroundBefore。。。" + proceedingJoinPoint.getSignature().getName());
        //执行目标方法    需抛出异常或者try catch
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("环绕通知     AroundAfter..." + proceedingJoinPoint.getSignature().getName());
        return obj;
    }

    /**
     * 最终通知
     */
    /*@After("execution(* com.bjsxt.service.*.*(..))")*/
    @After("myPointcut()")
    public void myAfter(){
        System.out.println("最终通知");
    }

    /**
     * 异常通知
     * @param e
     * throwing = "e":指定接收异常对象的参数名
     */
    /*@AfterThrowing(value = "execution(* com.bjsxt.service.*.*(..))",throwing = "e")*/
    @AfterThrowing(value = "myPointcut()",throwing = "e")
    public void myAfterThrowing(Exception e){
        System.out.println("异常通知Exception:              "+e);
    }
}
4.2.3.3 配置注解切面

4.2.3.3.1 创建目标对象

package com.bjsxt.service;

public interface UsersService {
    void addUsers(String username);
}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    @Override
    public void addUsers(String username) {
        System.out.println("AddUsers..."+username);
    }
}

4.2.3.3.2 开启 aop 命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

4.2.3.3.3 配置切面

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

    <!--配置目标对象-->
    <bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="myAspect" class="com.bjsxt.aop.MyAspect"/>

    <!--配置切面-->
    <!--在AspectJ框架中开启注解处理。
    声明自动为IOC容器中的那些配置了@AspectJ注解的切面的bean对象创建代理,织入切面    MyAspect类配置了AspectJ注解
    proxy-target-class="false"(默认):使用JDK的Proxy对象创建代理对象      true:使用CGLIB创建代理对象
    如果发现目标对象没有实现接口会使用CGLIB创建代理对象,如果实现类接口会使用JDK的Proxy对象创建代理对象
    如果设置为true即便是目标对象实现了接口也会使用CGLIB创建代理对象
proxy-target-class属性可配可不配
    -->
    <aop:aspectj-autoproxy proxy-target-class="false"/>

</beans>

4.2.3.3.4 创建测试类

package com.bjsxt.test;

import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        usersService.addUsers("kgb");
    }
}

在这里插入图片描述

4.2.3.4 多切面以及切面执行顺序的配置

多切面执行顺序并不是第一个切面里面的通知行为全部执行完再执行第二个切面里面的全部通知行为

圆圈为目标,纵三条线为切面,箭头方向为执行顺序

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

环绕通知包裹在外层

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.3.4.1 创建切面

package com.bjsxt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

//如果使用注解开发,必须在切面上添加@Aspect注解,这样Aspect框架才能识别它是切面
@Aspect//指定当前对象为切面对象
@Order(1)//给定整数,数字越小优先级越高
public class MyAspect2 {

    /**
     * 配置切点
     */
    @Pointcut("execution(* com.bjsxt.service.*.*(..))")
    public void myPointcut(){

    }

    /**
     * 前置通知
     * @param joinPoint
     * 在注解开发中切点是可以方法通知类型的注解中,如果只有一个切点需要配置去掉value属性也行
     */
    /*@Before(value = "execution(* com.bjsxt.service.*.*(..))")*/
    @Before("myPointcut()")//通过给定配置切点的方法名来指定对哪些目标对象做增强处理
    public void myBefore(JoinPoint joinPoint){
        /*获取目标方法名称*/
        System.out.println("Before222............................."+joinPoint.getSignature().getName());
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    /*@AfterReturning("execution(* com.bjsxt.service.*.*(..))")*/
    @AfterReturning("myPointcut()")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("afterReturnning222..."+joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知必须有返回值
     * @param proceedingJoinPoint   继承了JoinPoint
     * @return
     */
    /*@Around("execution(* com.bjsxt.service.*.*(..))")*/
    @Around("myPointcut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知   222  AroundBefore。。。" + proceedingJoinPoint.getSignature().getName());
        //执行目标方法    需抛出异常或者try catch
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("环绕通知 222    AroundAfter..." + proceedingJoinPoint.getSignature().getName());
        return obj;
    }

    /**
     * 最终通知
     */
    /*@After("execution(* com.bjsxt.service.*.*(..))")*/
    @After("myPointcut()")
    public void myAfter(){
        System.out.println("最终通知222");
    }

    /**
     * 异常通知
     * @param e
     * throwing = "e":指定接收异常对象的参数名
     */
    /*@AfterThrowing(value = "execution(* com.bjsxt.service.*.*(..))",throwing = "e")*/
    @AfterThrowing(value = "myPointcut()",throwing = "e")
    public void myAfterThrowing(Exception e){
        System.out.println("222异常通知Exception:              "+e);
    }
}

4.2.3.4.2 配置多切面

在这里插入图片描述

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

    <!--配置目标对象-->
    <bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl"/>

    <!--配置切面对象-->
    <bean id="myAspect" class="com.bjsxt.aop.MyAspect"/>

    <bean id="myAspect2" class="com.bjsxt.aop.MyAspect2"/>

    <!--配置切面-->
    <!--在AspectJ框架中开启注解处理。
    声明自动为IOC容器中的那些配置了@AspectJ的切面的bean对象创建代理,织入切面    MyAspect类配置了AspectJ注解
    proxy-target-class="false"(默认):使用JDK的Proxy对象创建代理对象      true:使用CGLIB创建代理对象
    如果发现目标对象没有实现接口会使用CGLIB创建代理对象,如果实现类接口会使用JDK的Proxy对象创建代理对象
    如果设置为true即便是目标对象实现了接口也会使用CGLIB创建代理对象
    -->
    <aop:aspectj-autoproxy proxy-target-class="false"/>

</beans>

七、 Spring 事务管理

1 Spring 事务管理简介

在 Spring 框架中事务管理有两种方式:一种是传统的编程式事务管理,即通过编写代码实现的事务管理==(try{}catch(){})==;另一种是基于 AOP 技术实现的声明式事务管理。由于在 Spring 框架中,编程式事务管理很少使用,所以我们只对 Spring 的声明式事务管理进行详细讲解。
Spring 的声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。

Spring 实现声明式事务管理主要有两种方式:
 基于 XML 文件方式的声明式事务管理。
 通过 Annotation 注解方式的事务管理。

2 Spring 声明式事务管理原理

在这里插入图片描述

如果使用声明式事务控制,不仅仅在目标方法中,在其他切面中做数据库DML操作也不需要做事务控制

3 数据库事务的特性

3.1什么是事务

事务是作为一个逻辑单元执行的一系列操作。一个事务工作单元必须有四个特性:

原子******性、一致性、隔离性、持久性。只有这样才能成为一个事务。***

3.2事务的作用

事物对于数据库的作用是对数据的一系列操作,要么全部成功,要么全部失败,防止中间状态的出现,以确保数据库中的数据始终处于正确的状态。

3.3事务的 ACID 特性

数据库的事务的 ACID 特性由数据库的事务管理系统来保证的。

***原子性(Atomicity)😗**事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。

***一致性(Consistency)😗**事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,也就是不一致的状态

***隔离性(Isolation)😗**并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。比如多个用户同时往一个账户转账,最后账户的结果应该和他们按先后次序转账的结果一样。

***持久性(Durability)😗**事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失。
一致性是事务的最终目的,原子性、隔离性、持久性都是为了实现一致性。

3.4事务的隔离性

线程并发的情况下需考虑隔离性

3.4.1 不考虑隔离性会导致的三个问题

1、脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。

T5时间轴读到的余额并不是最终的数据

在这里插入图片描述

2、不可重复读:是指 A 事务读取到了 B 事务已经提交的更改数据,在同个时间段内,两次查询的结果不一致。

update事务

在这里插入图片描述

3、幻读(虚读):A 事务读取到 B 事务提交的新增数据,幻象读一般发生在数据统计事务中。

insert事务

在这里插入图片描述

3.4.2 解决办法(四种隔离级别)

1、Read Uncommited(读取未提交内容)
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。但是,读未提
交产生了脏读,采用 Read Commited 可以解决脏读问题


2、Read Commited(读取提交内容)*
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。读提交,若有
事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能
读取数据,可以解决脏读问题。但是,读提交两次查询会产生不同的查询结果,就会造成不
可重复读问题,采用 Repeatable Read 可以解决此问题。


3、Repeatable Read(重复读)*
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。重复读可以解决不
可重复读问题。应该明白的一点就是,不可重复读对应的是修改,即 UPDATE 操作。但是可
能还会有幻读问题。因为幻读问题对应的是插入 INSERT 操作,而不是 UPDATE 操作。采用
Serializable 可以解决幻读问题


4、Serializable(可串行化)*

Serializable:将原来并发的线程变成串行,该级别最安全,但性能最差

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏
读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
Yes:可能出现。No:不会出现。

在这里插入图片描述

注意:
大多数数据库默认的事务隔离级别是 Read committed,比如 Sql Server , Oracle。

 Mysql 的默认隔离级别是 Repeatable read。

 隔离级别的设置只对当前链接有效(在方法中设置事务级别)。**对于使用 MySQL 命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;**对于 JDBC操作数据库来说,一个 Connection 对象相当于一个链接,而对于 Connection 对象设置的隔离级别只对该 Connection 对象有效,与其他链接 Connection 对象无关。

4 Spring 事务管理器

==在 Spring 框架中提供了多种事务管理器来进行事务管理。Spring 的事务管理器是基于AOP 实现的。==在 Spring 的事务管理器中包含了配置事务传播行为、隔离级别、只读和超时
属性,这些属性提供了事务应用的方法和描述策略。
在 Java EE 项目开发经常会使用分层模式,Spring 的事务处理位于业务逻辑层,它提供
了针对事务的解决方案。

4.1Spring 事务管理接口

在 Spring 的事务模块(spring-tx-5.2.7.RELEASE.jar)中包括事务管理的三个核心接口。

4.1.1 PlatformTransactionManager 接口

PlatformTransactionManager 接口是 Spring 提供的事务管理器接口,用于管理事务。
Spring 将事务的配置详细信息封装到 TransactionDefinition 对象中,然后通过事务管理器的
getTransaction() 方法获得事务的状态(TransactionStatus),并对事务进行下一步的操作。

该接口中提供了三个事务操作方法,具体如下:
 TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
 void commit(TransactionStatus status):用于提交事务。
 void rollback(TransactionStatus status):用于回滚事务。

4.1.2 TransactionDefinition 接口

TransactionDefinition 接口是事务定义(描述)的对象,它提供了事务相关信息获取的
方法,其中包括五个操作,具体如下:

 String getName():获取事务对象名称。
 int getIsolationLevel():获取事务的隔离级别。
 int getPropagationBehavior():获取事务的传播行为。
 int getTimeout():获取事务的超时时间。
 boolean isReadOnly():获取事务是否只读。

4.1.3 TransactionStatus 接口

TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包括六个操作,具体如下:
 void flush() 刷新事务
 boolean hasSavepoint() 获取是否存在保存点
 boolean isCompleted() 获取事务是否完成
 boolean isNewTransaction() 获取是否是新事务
 boolean isRollbackOnly() 获取是否回滚
 void setRollbackOnly() 设置事务回滚

4.2Spring 中包含的事务管理器(切面)

每一个事务管理器相当于一个切面

在这里插入图片描述

5 事务的传播行为

事务传播行为是指:多个含有事务的方法相互调用时,事务如何在这些方法间传播。

在这里插入图片描述

6 Spring 声明式事务管理

Spring 声明式事务管理实现方式:
 基于 XML 文件方式实现声明式事务管理。
 通过 Annotation 注解方式实现声明式事务管理。

6.1Jar 包依赖

Spring 核心容器模块
spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
Spring JDBC 模块
spring-jdbc-5.2.7.RELEASE.jar
Spring 事务模块
spring-tx-5.2.7.RELEASE.jar
Spring AOP 模块
spring-aop-5.2.7.RELEASE.jar
AspectJ 框架
aspectjweaver-1.9.5.jar
Spring 整合 AspectJ 框架模块
spring-aspects-5.2.7.RELEASE.jar
Commons-Loggin 日志
commons-logging-1.2.jar
数据库驱动
mysql-connector-java-5.1.48.jar

6.2使用 XML 文件方式实现声明式事务管理
6.2.1 搭建环境
6.2.1.1 创建项目

在这里插入图片描述

6.2.1.2 创建实体类
package com.bjsxt.pojo;

public class Orders {
    private int orderid;
    private double orderprice;

    public int getOrderid() {
        return orderid;
    }

    public void setOrderid(int orderid) {
        this.orderid = orderid;
    }

    public double getOrderprice() {
        return orderprice;
    }

    public void setOrderprice(double orderprice) {
        this.orderprice = orderprice;
    }
}
package com.bjsxt.pojo;

public class Users {
    private int userid;
    private String username;
    private String usersex;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", usersex='" + usersex + '\'' +
                '}';
    }
}
6.2.1.3 创建持久层
package com.bjsxt.dao;

public interface UsersDao {

}
package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;
import org.springframework.jdbc.core.JdbcTemplate;

public class UsersDaoImpl implements UsersDao {
    //通过Spring IOC容器注入
    private JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}
6.2.1.4 创建业务层
package com.bjsxt.service;

public interface UsersService {

}
package com.bjsxt.service.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    //通过Spring IOC容器注入
    private UsersDao usersDao;

    public UsersDao getUsersDao() {
        return usersDao;
    }

    public void setUsersDao(UsersDao usersDao) {
        this.usersDao = usersDao;
    }
}
6.2.1.5 配置 Spring JDBC
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm
jdbc.username = root
jdbc.password = root
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置解析properties文件的工具类    使用context命名空间下的标签配置-->
    <context:property-placeholder location="db.properties"/>

    <!--培训数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置Jdbctemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置持久层-->
    <bean id="usersDao" class="com.bjsxt.dao.impl.UsersDaoImpl">
        <!--注入JdbcTemplate-->
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--配置业务层-->
    <bean id="usersService" class="com.bjsxt.service.impl.UsersServiceImpl">
        <!--注入持久层-->
        <property name="usersDao" ref="usersDao"/>
    </bean>

</beans>
6.2.2 实现业务案例

业务案例:添加用户的同时添加一个订单。

6.2.2.1 修改持久层
void insertUsers(Users users);

void insertOrders(Orders orders);
/**
 * 添加用户
 * @param users
 */
@Override
public void insertUsers(Users users) {
    String sql = "insert into users values(default,?,?)";
    //新建封装参数的数组
    Object[] args = new Object[]{users.getUsername(), users.getUsersex()};
    //通过JdbcTemplate插入数据
    this.jdbcTemplate.update(sql, args);
}

/**
 * 添加订单
 * @param orders
 */
@Override
public void insertOrders(Orders orders) {
    //外键不用管但因为是完全项插入所以给null
    String sql = "insert into orders values(default,?,null)";
    Object[] args = new Object[]{orders.getOrderprice()};
    this.jdbcTemplate.update(sql, args);
}
6.2.2.2 修改业务层
void addUsersAndOrders(Users users, Orders orders);
/**
 * 添加用户与订单
 * @param users
 * @param orders
 */
@Override
public void addUsersAndOrders(Users users, Orders orders) {
    //添加订单
    this.usersDao.insertOrders(orders);
    //添加用户
    this.usersDao.insertUsers(users);
}
6.2.2.3 创建测试类
package com.bjsxt.test;

import com.bjsxt.pojo.Orders;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-service.xml");
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //准备参数
        Users users = new Users();
        users.setUsername("伏特加生命之水");
        users.setUsersex("male");
        Orders orders = new Orders();
        orders.setOrderprice(800);

        usersService.addUsersAndOrders(users,orders);
    }
}

在这里插入图片描述

在这里插入图片描述

6.2.3 配置 Spring 声明式事务管理
6.2.3.1 开启 aop 命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
6.2.3.2 开启 tx 命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
6.2.3.3 配置声明式事务管理

引入其他配置文件

在这里插入图片描述

applicationContext-tx.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--引入其他配置文件    如果使用IDEA从其他配置文件中查找就不需要引入其他配置文件了-->
    <import resource="applicationContext-service.xml"/>

    <!--配置事务管理器切面对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--需要注入数据源对象   dataSource在applicationContext-service.xml中需要引入这个配置文件否则报错-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事务管理器属性-->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <!--
            为需要做事务管理的方法做事务指定
               name:指定受事务管理的方法
               配置方式1:给定完全方法名
               配置方式2:通过*通配符指定方法名    add*:所有以add开头的方法都受到事务控制
               propagation:事务传播行为,默认就是REQUIRED
               isolation:配置事务的隔离级别DEFAULT(默认):并不做任何事务的隔离,以数据库的数据隔离级别做事务的隔离级别-->
            <!--<tx:method name="addUsersAndOrders"/>-->
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>

    <!--配置切面    使用Schema-Based的配置方式配置,实现接口不需要指定各种通知的方法名-->
    <aop:config>
        <!--配置切点-->
        <aop:pointcut id="txPointcut" expression="execution(* com.bjsxt.service.*.*(..))"/>
        <!--配置切面    给的是配置事务管理器属性的id-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

</beans>
6.2.3.4 启动 Spring 框架加载多个配置文件
//启动Spring IOC容器    只加载了一个配置文件,那么就需要在applicationContext-tx.xml中引入applicationContext-service.xml,或者在String[]里面给定多个需要加载的配置文件
/*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-service.xml");*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext-service.xml","applicationContext-tx.xml"});

测试类

package com.bjsxt.test;

import com.bjsxt.pojo.Orders;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //启动Spring IOC容器加载多个配置文件    
        //只加载了一个配置文件,那么就需要在applicationContext-tx.xml中引入applicationContext-service.xml,或者在String[]里面给定需要加载的配置文件
        /*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-service.xml");*/
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext-service.xml","applicationContext-tx.xml"});
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //准备参数
        Users users = new Users();
        users.setUsername("波尔多111");
        users.setUsersex("male");
        Orders orders = new Orders();
        orders.setOrderprice(2800);

        usersService.addUsersAndOrders(users,orders);
    }
}
6.2.4 声明式事务中属性解释
6.2.4.1 name 属性

name=”方法名称|方法名称*”
指定受事务控制的方法,支持*通配符。

6.2.4.2 propagation 属性

propagation=”传播行为名称”
配置事务的传播行为。

6.2.4.3 isolation 属性

Isolation=”隔离级别名称”
配置事物的隔离级别。

6.2.4.4 readonly 属性

readonly=”true|false”
是否为只读事务。
true:开启只读事务。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,会对性能有一定提升。建议在查询中开启只读事务。
false:不开启只读事务,默认为 false。

这样所有的查询方法就会运行在一个只读事务中

在这里插入图片描述

6.2.4.5 timeout 属性

timeout=”秒”
timeout 是设置超时属性。以秒为单位。当在限定的时间内不能完成所有操作,就会抛异常。

在这里插入图片描述

在数据添加之前休眠4秒钟,超过了超时时间,出现异常,数据也没有添加进来

在这里插入图片描述

6.2.4.6 rollback-for 属性

rollback-for=”异常名称”
Spring 默认情况下会对 RunTimeException 类型异常进行事务回滚。如果是 Exception 类型的异常则不回滚

事务管理器只能对运行时异常做回滚

***注意:如果异常被 try{}catch{}了,事务就不回滚了,如果想让事务回滚则必须再抛出异常。***

如果在业务层中try{} catch{}了一定要在catch{}块中抛出异常,这个异常是用来通知Spring 事务管理器的

EG:

在这里插入图片描述

只把Orders的数据添加进去了,而users的数据没有被添加进去,说明Spring的事物管理器没有做事务回滚

在这里插入图片描述

配置 rollback-for=”异常名称” 相当于告诉Spring 事务管理器即便是发生非运行时异常也要做事务回滚

直接给异常的名称

在这里插入图片描述

删掉之前的记录,配置之后再测试,事务回滚了

在这里插入图片描述

6.2.4.7 no-rollback-for 属性

no-rollback-for=”异常名称”
当出现什么异常时不滚回事务。

没添加之前可以正常回滚

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

即便是目标方法抛出了属性里面给定的异常也不回滚

在这里插入图片描述

在这里插入图片描述

6.3使用注解方式实现声明式事务管理
6.3.1 @Transactional 介绍

@Transactional 注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,
该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该
注解来覆盖类级别的定义。
虽然@Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建
议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生
另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质
决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,
这将被忽略,也不会抛出任何异常。一定得是Public修饰的

6.3.2 使用注解配置声明式事务管理的实现方式
6.3.2.1 注册事务管理驱动

为了解析@Transactional ,实例化一个能够解析这个注解的对象

applicationContext-tx-annotation.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--引入其他配置文件-->
    <import resource="applicationContext-service.xml"/>

    <!--配置事务管理器的切面-->
    <bean id="txTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--这个dataSource在其他配置文件中,需要引入其他配置文件-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--注册事务管理驱动    添加这个标签就相当于实例化了一个能够解析@Transactional的对象
    transaction-manager:指定当前事务管理器切面-->
    <tx:annotation-driven transaction-manager="txTransactionManager"/>

</beans>
6.3.2.2 使用@Transactional 实现事务控制

在这里插入图片描述

新建一个测试类Test2 事务回滚成功

package com.bjsxt.test;

import com.bjsxt.pojo.Orders;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) throws Exception {
        //启动Spring IOC容器加载多个配置文件
        //只加载了一个配置文件,那么就需要在applicationContext-tx.xml中引入applicationContext-service.xml,或者在String[]里面给定需要加载的配置文件
        /*ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-service.xml");*/
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext-service.xml","applicationContext-tx-annotation.xml"});
        //获取业务层对象
        UsersService usersService = (UsersService) applicationContext.getBean("usersService");
        //准备参数
        Users users = new Users();
        users.setUsername("波尔多111");
        users.setUsersex("male");
        Orders orders = new Orders();
        orders.setOrderprice(2800);

        usersService.addUsersAndOrders(users,orders);
    }
}

八、 Spring 基于 Annotation 装配 Bean 对象

在 Spring 中,尽管使用 XML 配置文件可以实现 Bean 的装配工作,但如果应用中
Bean 的数量较多,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
在 Spring 框架中提供了一些用于装配 Bean 对象的注解用于替换 xml 文件的中配置。

1 jar 包依赖

Spring 核心容器模块
spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
Spring AOP 模块
spring-aop-5.2.7.RELEASE.jar
Commons-Loggin 日志
commons-logging-1.2.jar

在这里插入图片描述

在这里插入图片描述

2 在 Spring 的配置文件中开启注解扫描

告诉含有这些注解的类在哪些包中

<context:component-scan base-package="扫描注解的包名"/>

开启context命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解扫描  Spring在启动时会扫描给定的包以及子包       看哪些类上面有注册bean的注解-->
    <context:component-scan base-package="com.bjsxt"/>

</beans>

3 注册 Bean 对象

3.1@Component
———— 用于定义其他层次的bean对象(既不是持久层也不是业务层也不是控制层)

可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念(分不清是具体哪个层次的 ),仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

如果想把一个类放到Spring IOC容器中就把@Component添加到该类上

在这里插入图片描述

在这里插入图片描述

自定义bean对象的id

在这里插入图片描述

在这里插入图片描述

3.2@Service

通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功
能与 @Component 相同。

@Service注解被@Component注解所修饰所以@Component有什么功能@Service也有

在这里插入图片描述

在这里插入图片描述

3.3@Repository

用于将数据访问层(DAO 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

也被@Component注解修饰

在这里插入图片描述

没有扫描到UsersDao,因为没有配置包

在这里插入图片描述

配置包,扫描包及其子包及其子包的子包

在这里插入图片描述

3.4@Controller

通常作用在控制层(如 Spring MVC 的 Controller),用于将控制层的类标识为 Spring 中
的 Bean,其功能与 @Component 相同。

4 Bean 对象依赖注入注解

不需要set()方法

4.1@Autowired

用于对 Bean 的属性变量、方法及构造方法进行标注,完成 Bean 的自动注入处理。
***@Autowired 只能按照 Bean 的类型进行装配。***

默认为true意思是如果在Spring IOC容器中找不到该类型的对象会报错,如果未false即使找不到也不报错那么这个对象就为null

在这里插入图片描述

能够注入进来

在这里插入图片描述

把注解加到方法上在Spring IOC容器启动时该方法会被Spring IOC容器调用,然后通过参数类型去Spring IOC容器找对应类型的bean对象,然后通过参数列表传递到该方法中

在这里插入图片描述

4.2@Resource

该标签在JDK1.8里面可以找到,但是在JDK14里面找不到

在这里插入图片描述

其作用与 Autowired 一样。其区别在于 @Autowired只能 按照 Bean 类型装配,而
***@Resource 是可以按照 Bean ID 或者类型进行装配。***

默认先使用id去找,如果找不到再按照类型去找

在这里插入图片描述

使用name属性即表示指定按照id去找

在这里插入图片描述

在这里插入图片描述

使用type属性表示指定按照类型去找

在这里插入图片描述

不给接口类型给具体的对象类型也可以,对象实现了接口

在这里插入图片描述

4.3@Qualifier

与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

这两个注解配合使用就可以完成按照bean的 id 去查找注入,
用于同一类型的bean对象有多个的情况先按照类型找再按照id识别

在这里插入图片描述

4.4@Value

可获取 Properties 文件中的值。根据key使用EL表达式从properties文件中取值然后赋给属性

在这里插入图片描述

配置解析properties文件的工具类

在这里插入图片描述

在这里插入图片描述

5 Spring 配置相关注解

5.1@Configuration

声明当前类为配置类,相当于 xml 形式的 Spring 配置。该注解需要添加到类上。(相当于配置文件)

有了配置类依然需要配置文件,不然在哪配置注解扫描?当学习Spring boot可以去掉配置文件

5.2@Bean

注解在方法上,声明当前方法的返回值为一个 Bean 对象,该对象会被添加 SpringIOC
容器中。和标签作用的相同。Bean 的实例名称由 @Qualifier 注解的参数指定。

已在配置类中配置,所以方法上的注解可以去掉

在这里插入图片描述

在这里插入图片描述

配置类

package com.bjsxt.config;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Spring IOC容器在启动时会扫描,
 * 一旦扫描到了@Configuration就会把这个配置类里面所有标记为@Bean注解的方法自动执行
 * 然后这个方法所返回的对象都会被放到Spring IOC容器中,完成bean对象的注册
 */
@Configuration
public class SpringConfig {
    @Bean//默认使用当前方法的方法名作为bean对象的id
    public UsersDao usersDaoInit(){
        return new UsersDaoImpl();
    }
}

默认会使用当前方法的方法名作为bean对象的id

在这里插入图片描述

@Qualifier("suibianDao")//使用该注解为bean对象取名

在这里插入图片描述

输出的是所有bean对象的id而@Qualifier(“suibianDao”)//使用该注解为bean对象取名,bean对象name属性的值

在这里插入图片描述

九、 Junit

1 Junit 简介

Junit 是 Java 编程语言的单元测试框架,用于编写和运行可重复的自动化测试。
单元测试(Unit Testing),是指对软件中的最小可测试单元进行检查和验证。对于单元
测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如 C 语言中单元指一个
函数,Java 里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单
元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的
测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
Junit 特点:
 Junit 是一个开放的资源框架,用于编写和运行测试。
 提供注解来识别测试方法。
 提供断言来测试预期结果。
 Junit 测试允许你编写代码更快,并能提高质量。
 Junit 优雅简洁。没那么复杂,花费时间较少。
 Junit 测试可以被组织为测试套件,包含测试用例,甚至其他的测试套件。
 Junit 在一个条中显示进度。如果运行良好则是绿色;如果运行失败,则变成红色。

2 Junit 注解

执行优先级:@BeforeClass > @Before > @Test > @After > @AfterClass

@AfterClass , @BeforeClass只能附着在静态方法中

在这里插入图片描述

3 Junit 断言

JUnit4.4 引入了 Hamcrest 框架,它们用来帮助我们确定被测试的方法是否按照预期的效
果正常工作,通常把这些辅助方法称为断言。

在这里插入图片描述

4 Junit 的使用

4.1Jar 包依赖

junit-4.13.jar
hamcrest-core-1.3.jar

4.2创建项目

在这里插入图片描述

测试类包名的命名规则,以test命名

在这里插入图片描述

测试类命名规则以被测试的类名后面加Test

在这里插入图片描述

测试方法命名规则以test加方法名

在这里插入图片描述

4.3Junit 注解的使用

在这里插入图片描述

在这里插入图片描述

如果选中测试类点右键执行,会执行整个测试类下的所有方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package test.com.bjsxt.service;

import org.junit.*;

public class UsersServiceTest {

    @Test//标记该方法为测试方法
    public void testAddUsers(){
        System.out.println("Hello Junit");
    }

    @Before//会在@Test注解标准的方法执行之前执行
    public void testBefore(){
        System.out.println("Before...");
    }

    @BeforeClass//该注解所标注的方法会在@Before标注的方法之前运行   只能标注在静态方法上否则报错
    public static void testBeforeClass(){
        System.out.println("BeforeClass...");
    }

    @After//该注解标注的方法在@Test标注的方法之后执行
    public void testAfter(){
        System.out.println("After...");
    }

    @AfterClass//该注解标注的方法会在@After之后执行   只能标注在静态方法上否则报错
    public static void testAfterClass(){
        System.out.println("AfterClass...");
    }

}
4.4Junit 断言的使用
package com.bjsxt.service;

public interface UsersService {
    int addUsers();
}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;

public class UsersServiceImpl implements UsersService {
    @Override
    public int addUsers() {
        System.out.println("insert into users...");
        return 1;
    }
}
package test.com.bjsxt.service;

import com.bjsxt.service.impl.UsersServiceImpl;
import org.junit.*;

public class UsersServiceTest {

    @Test//标记该方法为测试方法
    public void testAddUsers() {
        UsersServiceImpl usersService = new UsersServiceImpl();
        UsersServiceImpl usersService2 = usersService;
        //判断两个对象的引用是否相同
        /*Assert.assertSame(usersService,usersService2);*/

        /*int flag = usersService.addUsers();
        //param1:期望的数   param2:实际的值
        Assert.assertEquals(2,flag);*/

        //判断条件是否为真      param1可以忽略不计,错误信息会输出
        Assert.assertTrue("msg",2>1);

    }

    /*@Before//会在@Test注解标准的方法执行之前执行
    public void testBefore(){
        System.out.println("Before...");
    }

    @BeforeClass//该注解所标注的方法会在@Before标注的方法之前运行   只能标注在静态方法上否则报错
    public static void testBeforeClass(){
        System.out.println("BeforeClass...");
    }

    @After//该注解标注的方法在@Test标注的方法之后执行
    public void testAfter(){
        System.out.println("After...");
    }

    @AfterClass//该注解标注的方法会在@After之后执行   只能标注在静态方法上否则报错
    public static void testAfterClass(){
        System.out.println("AfterClass...");
    }*/

}

期望的和实际返回的是一样的出现一闪而过的绿色线条

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

与期望的返回值不同,出现一条红色的一闪而过的线条

在这里插入图片描述

判断两个对象是否相同

在这里插入图片描述

在这里插入图片描述

判断条件是否为真 也可以使用目标方法返回的布尔类型值做判断

在这里插入图片描述

在这里插入图片描述

package test.com.bjsxt.service;

import com.bjsxt.service.impl.UsersServiceImpl;
import org.junit.*;

public class UsersServiceTest {

    @Test//标记该方法为测试方法
    public void testAddUsers() {
        UsersServiceImpl usersService = new UsersServiceImpl();
        UsersServiceImpl usersService2 = usersService;
        //判断两个对象的引用是否相同
        /*Assert.assertSame(usersService,usersService2);*/

        /*int flag = usersService.addUsers();
        //param1:期望的数   param2:实际的值
        Assert.assertEquals(2,flag);*/

        //判断条件是否为真      param1可以忽略不计,错误信息会输出
        Assert.assertTrue("msg",2>1);

    }

}

十、 Spring 整合 Junit

在这里插入图片描述

在这里插入图片描述

如果不配置测试引擎默认使用Junit测试引擎,但是Junit引擎不能启动Spring框架,那么bean对象无法注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--开启注解扫描-->
    <context:component-scan base-package="com.bjsxt"/>
    
</beans>
package com.bjsxt.service;

public interface UsersService {
    void addUsers();
}
package com.bjsxt.service.impl;

import com.bjsxt.service.UsersService;
import org.springframework.stereotype.Service;

@Service
public class UsersServiceImpl implements UsersService {

    @Override
    public void addUsers() {
        System.out.println("addUsers...");
    }
}
package test.com.bjsxt.service;

import com.bjsxt.service.UsersService;
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;

/**
 * @RunWith(SpringJUnit4ClassRunner.class):更换Junit测试引擎
 * 如果不配置测试引擎默认使用Junit测试引擎,但是Junit测试引擎不能启动Spring框架,那么bean对象无法注入
 * SpringJUnit4ClassRunner引擎会启动Spring框架
 * classpath:在java项目中指的就是SRC目录下   指定配置文件的位置
 * @ContextConfiguration(locations = {"classpath:applicationContext.xml"}):指定Spring配置文件
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class UsersServiceImplTest {

    //直接通过注解注入,在Spring 整合Junit时就不需要启动框架获取bean对象了
    @Autowired
    private UsersService usersService;

    @Test//如果被测试方法有参数得传参
    public void testAddUsers(){
        //直接拿注入的bean对象调用被测试方法
        this.usersService.addUsers();
    }
}

在这里插入图片描述

十一、 Spring 整合 Servlet

1 Jar 包依赖

Spring 核心容器模块
spring-beans-5.2.7.RELEASE.jar
spring-context-5.2.7.RELEASE.jar
spring-core-5.2.7.RELEASE.jar
spring-expression-5.2.7.RELEASE.jar
Commons-Loggin 日志
commons-logging-1.2.jar
Spring AOP 模块
spring-aop-5.2.7.RELEASE.jar
SpringWeb 模块
spring-web-5.2.7.RELEASE.jar
Servlet
servlet-api.jar

2 搭建环境

2.1创建 Web 项目

在这里插入图片描述

2.2添加 Spring 配置文件

开启了Context命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
</beans>

3 在 Web 项目中启动 Spring 框架

在 Web 项目中需要在 web.xml 文件中配置启动 Spring 框架的监听器(由spring的web模块提供)。用于启动 Spring框架。

web.xml在项目启动时会被解析一次,一旦项目启动成功web.xml就不会再被解析了,通过这个特点可以保证Spring框架在web项目中只启动一次

org.springframework.web.context.ContextLoaderListener该监听器是监听ServletContext对象生命周期的监听器,只要ServletContext对象被实例化会立即调用监听器里的Init()方法,在该方法中通过ServletContext对象根据 contextConfigLocation 获取 value 拿到spring的配置文件然后启动spring框架

在web项目中配置文件放到SRC目录下当项目部署运行之后配置文件会被放到classes目录下,classes目录对应的就是 classpath

修改 web.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--指定Spring IOC容器配置文件的位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--配置启动Spring框架的监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

4 在 Servlet 中获取 Bean 对象

如果将一个Servlet交给Spring IOC容器管理那么这个servlet将无法处理请求,servlet的生命周期被容器管理,只有被容器管理的servlet才能处理请求,所以不能把servlet放到Spring IOC容器中完成bean对象注册否则无法处理请求

org.springframework.web.context.ContextLoaderListener该监听器的Init()方法启动完Spring框架之后会把spring 框架上下文对象放到ServletContext对象中缓存,如果不缓存随着监听器对象的Init()方法执行完毕这个对象的引用就没了,Spring框架就会被JVM回收,相当于Spring框架启动完就扔掉没有用过,如果把Spring框架的上下文对象缓存就可以让Spring框架一直工作,只要ServletContext对象不消亡那么Spring框架可以一直使用,这里Spring IOC容器的上下文对象不是applicationContext而是webApplicationContext对象,webapplicationContext也是一个接口继承了applicationContext类型接口,webApplicationContext对applicationContext做了扩展,webApplicationContext类型的IOC容器是专门为web项目服务的Spring IOC容器

4.1创建业务层
package com.bjsxt.service;

public interface UsersService {
    void addUsers();
}
package com.bjsxt.service.impl;

import com.bjsxt.dao.UsersDao;
import com.bjsxt.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service//完成bean对象的注册
public class UsersServiceImpl implements UsersService {

    //通过Spring IOC容器注入
    @Autowired
    private UsersDao usersDao;

    @Override
    public void addUsers() {
        System.out.println("addUsers...");
        this.usersDao.insertUsers();
    }
}
4.2创建 Servlet
package com.bjsxt.web.servlet;

import com.bjsxt.service.UsersService;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/addUsers.do")
public class UsersServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*获取webApplicationContext对象     方式一:
        ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个常量就是在ServletContext对象下存放webApplicationContext的key
        这里除了用WebApplicationContext类型来定义也可以用ApplicationContext类型定义
        */
        /*WebApplicationContext webApplicationContext = (WebApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        //获取业务层对象
        UsersService usersService = (UsersService) webApplicationContext.getBean("usersServiceImpl");
        usersService.addUsers();
        resp.getWriter().println("Hello Spring Servlet");*/

        /*获取webApplicationContext对象     方式二:
        * 工具类会根据给定的ServletContext获取webApplicationContext*/
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        //获取业务层对象
        UsersService usersService = (UsersService) webApplicationContext.getBean("usersServiceImpl");
        usersService.addUsers();
        resp.getWriter().println("Hello Spring Servlet");
    }
}
4.3创建持久层
package com.bjsxt.dao;

public interface UsersDao {
    void insertUsers();
}
package com.bjsxt.dao.impl;

import com.bjsxt.dao.UsersDao;
import org.springframework.stereotype.Repository;

@Repository//将持久层注入到业务层中
public class UsersDaoImpl implements UsersDao {

    @Override
    public void insertUsers() {
        System.out.println("insert into Users...");
    }
}
4.4修改 Spring 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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:component-scan base-package="com.bjsxt.service,com.bjsxt.dao"/>

</beans>

测试结果:

在这里插入图片描述

在这里插入图片描述

方式二:

在这里插入图片描述

因为是在原来浏览器页面刷新的所以控制台会有两个addUsers…

在这里插入图片描述

配置扫描多个包

在这里插入图片描述

十二、 Spring 重点知识梳理

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值