VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • Spring5 学习笔记

个人代码: GitHub

https://github.com/bpf-collector/Study-Code/tree/master/Spring5_Code

1. Spring 概述

1.1 Spring 简介

  Spring Framework 是一个使用Java开发的、轻量级的、开源框架,它的主要作用是为了解耦合。Spring 的核心技术是 IOC(控制反转) 和 AOP(面向切面编程)

  • 官方网站: https://spring.io

  Spring 框架提高了很多功能,包括IOC容器、AOP、数据访问、事务、测试功能、定时任务、缓存等等。

1.2 优点

轻量、解耦、面向切面编程、方便与其他框架集成、方便测试、减低开发难度。

2. IOC 控制反转

2.1 IOC 是什么

  IOC (Inversion of Control, 控制反转) 是一种理论,指导开发人员如何使用对象、管理对象,将对象的生命周期交给容器来管理。通过容器管理对象,开发人员只需要拿到对象,执行对象的方法即可。

  • 控制:管理对象的创建、属性赋值、生命周期的管理。
  • 正转:让开发人员掌控对象的创建、属性赋值,即整个生命周期的管理。
  • 反转:把开发人员管理对象的权限转移给容器来实现,让容器完成管理。

2.2 IOC 的技术实现

  DI (Dependency Injection, 依赖注入) 是 IOC 的一种技术实现,开发人员通过对象的名称获取已初始化的对象,而对象的创建、属性赋值、对象间的调用等都由容器内部实现。

2.3 IOC-创建对象 牛刀小试

 Source Code

2.3.1 测试步骤

  1. 创建 maven-quickstart 项目,并调整项目结构(字符编码、JDK版本等)
  2. 添加依赖
    • spring-context
    • junit
  3. 定义接口和实现类
    • 接口: SomeService
      • 方法: doSome(): void
    • 实现类: SomeServiceImpl
  4. 创建 Spring 配置文件(.xml),声明需要创建的对象
    • 通过<bean>标签声明对象,一个标签对应一个对象。
  5. 使用容器中的对象
    • 创建 ApplicationContext 对象
    • 通过 getBean() 获取容器中的对象

2.3.2 依赖文件

 
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bpf</groupId>
    <artifactId>M01-ioc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

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

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

2.3.3 接口与实现类

 

2.3.4 配置文件

 
<?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      自定义对象的名称,保持唯一。
        class   自定义对象的全限定类名,不能是接口。

        >>> Spring 根据 id 和 class 创建对象,并将对象放入一个 map 对象中。
    -->
    <bean id="someService" class="com.bpf.service.impl.SomeServiceImpl" />
    <bean id="someService1" class="com.bpf.service.impl.SomeServiceImpl" />

    <bean id="mydate" class="java.util.Date" />
</beans>

2.3.5 测试创建对象

测试创建对象: CreateBeanTest.java

2.4 Spring 的配置文件

  Spring 配置文件通常命名为ApplicationContext.xml。标准的配置文件格式如下:

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

<!-- 
    1) 根标签是 beans
    2) xxx.xsd 是当前XML文件的约束文件
    3) 在 beans 标签内声明 bean 对象。
       一个 bean 就是一个java对象。
 -->
</beans>

  Spring 支持多配置文件方式,Spring 管理多配置文件常用的是包含关系。即在主配置文件中使用import标签包含其他配置文件,在其他配置文件中定义声明各自的信息。

 
<!-- 主配置文件 -->

<!-- 路径中可以使用通配符 * 同时引入多个文件 -->
<import resource="classpath:其他配置文件路径" />

2.5 Spring IOC ☞ 创建对象

2.5.1 Spring 容器创建对象的特点

Spring 框架使用 DI 实现 IOC 思想,底层通过反射机制创建对象、初始化对象。

  1. 容器对象是ApplicationContext,它是一个接口。常用的实现类是ClassPathXmlApplicationContext,并且通过getBean()方法获取已初始化的对象。
  2. Spring 创建对象默认调用类的无参构造器
  3. Spring 在创建容器对象后,会读取配置文件,并创建文件中声明的所有java对象,然后都放在map对象(ConcurrentMap)中。

2.5.2 XML方式

  Spring 通过在配置文件中使用bean标签声明对象,使用id属性指定创建的对象名称,使用class属性指定创建的对象类型。

 
<!-- 配置文件中声明一个 bean 标签代表一个 java对象 -->
<bean id="对象名称" class="对象类型" />

2.5.3 注解方式

  使用注解代替配置文件中的bean标签,在Java类上使用注解,通过value属性指定创建的对象名称(相对于标签的id属性)。同时还需要在配置文件中开启注解扫描并指定扫描的包路径。

Spring 提供了四个注解

注解 说明
@Component 表示普通的java对象
@Repository 常用于创建DAO层的对象,持久层对象,表示可以访问数据库
@Service 常用于创建Service层的对象,业务层对象,表示拥有事务功能
@Controller 常用于创建Controller层的对象,控制器对象,表示可以接收和处理请求。

配置文件开启注解扫描:

 
<!-- base-package 指定要扫描的包路径,Spring 会自动扫描包及其子包内表有上述注解之一的类,并创建和管理。 -->
<context:componet-scan base-package="包路径" />

<!-- 如何扫描多个包? -->
<!-- 1. 使用多个标签 -->
<context:componet-scan base-package="xx.yy.pack01" />
<context:componet-scan base-package="xx.yy.pack02" />

<!-- 2. 使用分隔符:分号(;)或逗号(,) -->
<context:componet-scan base-package="xx.yy.pack01;xx.yy.pack02" />

<!-- 3. 使用共同的父包 -->
<context:componet-scan base-package="xx.yy" />

2.6 Spring IOC ☞ 属性注入

 Source Code

2.6.1 XML方式

(1)set注入(设值注入)

set注入:通过对象的 setXxx() 方法给属性赋值。

特点

  • 注入的属性必须存在对应的 setter 方法
  • 如果属性在对象中不存在,但存在 setter 方法,依然不会报错。
  • Spring 容器只负责调用 setter 方法,与方法的具体实现无关。
 
<!-- 简单类型注入: 基本数据类型、String类型 -->
<bean id="xxx" class="yyy">
    <property name="属性名" value="xxx" />
    ...
</bean>

<!-- 引用Java对象 -->
<bean id="xxx" class="yyy">
    <property name="属性名" ref="其他bean标签的id值" />
    ...
</bean>
<!-- 或 -->
<bean id="xxx" class="yyy">
    <property name="属性名">
        <bean class="想要注入此属性的对象"></bean>
    </property>
    ...
</bean>

<!-- 注入null值 -->
<bean id="xxx" class="yyy">
    <property name="属性名">
        <null/>
    </property>
    ...
</bean>

<!-- 集合类型 -->
<bean id="xxx" class="yyy">
    <property name="属性名">
        <!-- 数组 -->
        <array>
            <value>xxx</value>
        </array>
    </property>

    <property name="属性名">
        <!-- List -->
        <list>
            <value>xxx</value>
            <ref bean="其他bean标签的id值" />
        </list>
    </property>

    <property name="属性名">
        <!-- Set -->
        <set>
            <value>xxx</value>
        </set>
    </property>

    <property name="属性名">
        <!-- Map -->
        <map>
            <entry key="xxx" value="yyy" />
        </map>
    </property>

    <property name="属性名">
        <!-- 数组 -->
        <array>
            <value>xxx</value>
        </array>
    </property>
</bean>

(2)构造注入

构造注入:通过对象的 含参构造器 方法给属性赋值。

特点

  • 不需要属性的 setter 方法
  • 需要有相对应的含参构造器
 
<!-- 
    index   对应构造器的形参索引,从0开始,可以省略
    name    对应构造器的形参名
    value   对应构造器的形参值
    ref     对应其他的Java Bean
 -->
<bean id="xxx" class="yyy">
    <constructor-arg name="构造器形参名" value="xxx" />
    <constructor-arg index="构造器形参索引" value="xxx" />
    ...
</bean>

(3)引用类型自动注入

引用类型自动注入:只针对对象中的引用类型有效,可以指定根据名称或类型自动注入属性的值。

  • byName: 根据名称注入。当配置文件中bean标签的id值与对象的属性名匹配且属于同个类型时,可以进行注入。
  • byType: 根据类型注入。当配置文件中bean标签的class值与对象的属性类型同源时,可以进行注入。
    • bean标签的class值与对象的属性类型相同时。
    • bean标签的class值与对象的属性类型存在父子关系时。
    • bean标签的class值与对象的属性类型存在接口-实现类关系时。

特点

  • byName 方式通过 bean 标签的id属性,需要保证id唯一
  • byType 方式提供 bean 标签的class属性,需要保证只能存在一个同源的bean,否则会报错。
  • 引用类型自动注入本质上使用的是setter方法进行属性赋值的。
 
<!-- 引用类型自动注入 -->
<bean id="xxx" class="yyy" autowired="byName | byType">
    ...
</bean>

(4)小作业

主要功能:模拟用户注册操作。

  • 实体类 User,保存用户数据。
  • 定义一个 UserDao 接口,提供方法 insertUser(User),同时定义接口的实现类 MySqlUserDao,方法实现输出 "通过MySQL插入用户:用户数据"。
  • 定义一个 UserService 接口,提供方法 addUser(User),同时定义接口的实现类 UserServiceImpl,并实现方法。

要求:使用 Spring 创建和管理接口的实现类对象,并通过 Spring 获取对象完成用户注册操作。

 Source Code

2.6.2 注解方式

(1)@Value

  • @Value注解只能为属性赋普通类型的值。
  • @Value注解的位置:
    • 属性声明上:无需setter方法
    • setter方法上:需要setter方法,并且会调用setter方法
  • 赋的值可以通过外部配置文件(.properties)指定。
 
<!-- 配置文件中引入外部配置文件 -->
<context:property-placeholder location="classpath:properties文件的路径" />
 

(2)@Autowired

  • @Autowired注解可以为属性赋引用类型的值,默认方式是byType
  • @Autowired注解的位置:
    • 属性声明上:无需setter方法
    • setter方法上:需要setter方法,并且会调用setter方法
 
/**
 * Autowired 注解源码
 *   包含了 required 属性,默认值为true。表示当赋值的属性必须有值且赋值成功,当赋值的对象为null时,会抛出异常。
 */
public @interface Autowired {
    boolean required() default true;
}
 

(3)@Qualifer

  当使用@Autowired注解进行引用类型注入时,由于默认方式为byType,当存在多个同源的bean时,会抛出异常:org.springframework.beans.factory.NoUniqueBeanDefinitionException。这时候就需要使用byName方式了。

  • @Qualifer注解结合@Autowired注解使用可以实现byName方式的引用类型自动注入。
  • 注解位置同上。
 
/**
 * Qualifer 注解中只有一个属性 value, 用来指定 bean 的名称即 id。
 */
public @interface Qualifier {
    String value() default "";
}

(4)@Resource

  • @Resource注解是JDK自带的注解,但 Spring 支持这样的注解使用。
  • @Resource注解只能为属性赋引用类型的值,默认方式是byName
    • 当使用byName无法匹配到任何bean时,会使用byType方式。
    • 通过指定name属性让注解只通过byName方式注入bean。
  • 在 JDK8 及之前是自带此注解的,更高的版本需要手动导入依赖。
 
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

2.7 Spring IOC 总结

  IOC 就是用来管理对象、管理依赖关系的。通过 IOC 可以实现解决处理业务逻辑对象之间的耦合关系,即 Service 和 DAO 之间的解耦合。

  • 不适合交给Spring管理的对象:
    • 实体类
    • servlet、listener、filter 等 WEB 中的对象,因为它们是由 Tomcat 创建和管理的对象。

补充

> 完全注解开发

> Spring Bean 的生命周期

3. AOP 面向切面编程

3.1 AOP 是什么

  AOP (Aspect Orient Programming, 面向切面编程) 是一种编程思想。它可以在不改变源代码的基础上,给业务方法新增功能。

  AOP 是一种动态的思想,它是在程序运行期间,为特定的业务创建代理,通过代理来增加切面功能,而这个代理是存在于内存中的。

什么是切面

  • 给业务功能新增的功能就是切面。
  • 切面一般是非业务功能,而且一般都是可复用的。
  • 比如:日志功能、事务功能、权限检查、参数检查、信息统计等等。

AOP的作用

  • 给业务功能新增方法不需改变源代码。
  • 让开发人员专注业务逻辑,提高开发效率。
  • 实现业务功能与非业务功能解耦合。
  • 切面复用。

3.2 AOP 中的重要术语

术语 翻译 解释
Aspect 切面 给业务方法新增的功能
JoinPoint 连接点 即业务方法
Pointcut 切入点 切面的执行位置。一个或多个连接点的集合,即增加切面的所有业务方法。
Target 目标对象 业务方法的执行者
Advice 通知 切面的执行时间

  AOP 中重要的三个要素:AspectPointcutAdvice,表示在 Advice时间、在 Pointcut位置 执行 Aspect切面

3.3 AOP 的使用时机

  • 当某些方法需要增加相同功能,而源代码又不方便修改时
  • 当给业务方法增加非业务功能时

3.4 AOP 的技术实现

  常用的 AOP 实现技术是 Spring 和 AspectJ

  • Spring:Spring 框架实现了 AOP 思想中的部分功能。但其操作比较繁琐和笨重。
  • AspectJ:独立的框架,专门负责 AOP,属于 Eclipse 基金会。
    • 官网: https://www.eclipse.org/aspectj/

3.5 AspectJ 框架

  AspectJ 框架中可以使用 注解 和 XML配置文件 的方式实现 AOP。

 
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.12</version>
</dependency>

3.5.1 注解方式

 Source Code

(1)Advice 通知注解

 AspectJ 框架中表示切面执行的时间是五种通知注解,分别代表不同的执行时间。

注解 通知类型 执行时间
@Before 前置通知 业务方法前执行
@AfterReturning 后置通知 业务方法后执行
@Around 环绕通知 业务方法前和后都执行
@AfterThrowing 异常通知 业务方法过程中出现异常时执行
@After 最终通知 业务方法后执行

(2)Pointcut 切入点表达式

 AspectJ 框架中表示切面执行的位置是切入点表达式,本质上可以看作是业务方法的定位标志。

 
execution(访问权限? 返回值类型 全限定类名?方法名(参数列表) 异常类型?)
  • ? 代表可选。
    • 最简形式:execution(返回值类型 方法名(参数列表))
  • 四个部分之间通过空格分开,并且都可以使用通配符

相关教程