Spring启示录

OCP开闭原则

  • 开闭原则规定:软件开发中应做到 对拓展开放,对修改关闭。即程序可以对原有功能进行拓展,但是不能因为拓展功能而修改原本可以正常运行的程序。因为因为一旦修改,会导致整个项目要进行全方位的重新测试。而导致这个问题的主要原因是:代码之间耦合度过高
  • 在这个程序里上层依赖下层。UserController依赖UserServiceImpl,而UserServiceImpl依赖UserDaoImplForMySQL,这样就会导致下面只要改动,上面必然会受牵连(跟着也会改),所谓牵一发而动全身。这样也就同时违背了另一个开发原则:依赖倒置原则

依赖倒置原则

  • 依赖倒置原则(Dependence Inversion Principle),简称DIP。主要倡导面向抽象编程,面向接口编程。(不直接手动new对象,由系统为属性分配对象),该原则包含两个关键点:
    • 高层模块不应该依赖低层模块,两者都应该依赖抽象。此处抽象通常指接口或者抽象类
    • 抽象不应该依赖细节,细节(具体实现)应该依赖抽象
  • 实现该原则的核心问题:
    1. 谁来创建对象
    2. 谁来负责为新建的对象赋值
  • Spring框架为我们解决了这两个问题。
  • 通过将对象的创建权/管理权交出去,不再使用硬编码的格式去操纵对象。像这种把对象的创建权和管理权交出去,被称为控制反转

Ioc控制反转

  • 控制反转(Inversion of Control,缩写为IOC)是一种更宽泛的设计模式或架构原则,能够用来降低代码代码之间的耦合度,符合依赖倒置原则。
  • 控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,有第三方容器来负责创建与维护

目的:

  • 解耦组件: 组件不需要自己定位或创建依赖,只需要声明需要什么(如通过构造函数参数、setter 方法、字段标注)。容器负责满足这些依赖。
  • 集中管理配置和生命周期: 对象的创建、依赖关系、作用域(单例、原型等)可以在一个地方(如 XML、注解、Java Config)集中配置和管理。
  • 提高可测试性: 依赖可以更容易地被 Mock 或 Stub 替换进行单元测试。
  • 促进模块化和可重用性: 组件更专注于自身业务逻辑,不涉及复杂的依赖管理代码。
  • 为 AOP 等高级特性提供基础: IoC 容器是实现面向切面编程(AOP)的常见基础。
  • 控制反转常见的实现方式:依赖注入
  • 通常依赖注入的方式有两种:
    • Set注入
    • 构造注入

Spring概述

Spirng Context模块

如果说核心模块中的BeanFactory使Spirng成为容器,那么上下文模块就是Spring成为框架的原因

此模块拓展类BeanFactory,增加了对国际化(I18N)消息、事件传播、验证的支持。另外提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持

Spring AOP模块

Spring在它的AOP模块中提供了对面向切面编程的丰富支持,Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中,可以自定义拦截器、切点、日志等操作。

Spring DAO模块

提供了一个JDBC的抽象层和异常层次结构,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析,用于简化JDBC。

Spring ORM模块

Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射,这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

Spring web MVC模块

Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离。

Spring webFlux模块

Spring Framework 中包含的原始 Web 框架 Spring Web MVC 是专门为 Servlet API 和 Servlet 容器构建的。反应式堆栈 Web 框架 Spring WebFlux 是在 5.0 版的后期添加的。它是完全非阻塞的,支持反应式流(Reactive Stream)背压,并在Netty,Undertow和Servlet 3.1+容器等服务器上运行。

Spring web模块

Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文,提供了Spring和其它Web框架的集成,比如Struts、WebWork。还提供了一些面向服务支持,例如:实现文件上传的multipart请求。

Spring的特点

  • 轻量级
    • 从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
    • Spring是非侵入式的:Spring应用中的对象不依赖于Spring的特定类。
  • 控制反转
    • Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
  • 面向切面
    • Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
  • 容器
    • Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
  • 框架
    • Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

Spring的这些特征能使编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

Spring 入门程序

引入依赖

  • Spring有各种Jar包,来满足不同需求,仅仅使用Spring的IOC功能,只需要引入spring-context这个Jar包即可
1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.14</version>
</dependency>

当引入Spring-Context包之后,还会关联引入一些其他的依赖:

spring aop: 面向切面编程
spring beans: IoC核心
spring core: spring的核心工具包
spring jcl: spring的日志包
spring expression: spring表达式

定义bean:User

1
2
3
4
5
package com.powernode.spring6.bean;

public class User {

}

编写Spring的配置文件

  • 创建一个Spring的配置文件,命名为beans.xml,来统一管理Bean,该文件放在类的根路径下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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">
<!--这就是Spring的配置文件-->
<!--IDEA工具为我们提供了这个文件的模板,一定要使用这个模板来创建。-->
<!--这个文件名不一定叫微spring.xml,可以是其它名字。-->
<!--这个文件最好是放在类路径当中,方便后期的移植。-->
<!--放在resources根日灵下,就相当于是放到了类的根路径下。-->
<!-- 配bean,这样spring才可以帮助我们管理这个对象。-->
<!--
bean标签的两个重要属性:
id:是这个bean的身份证号,不能重复,是唯一的标识。
cLass: 必须填写类的全路径,全限定类名。 (带包名的类名)
-->

<bean id="userBean" class="com.powernode.spring6.bean.User"/>

</beans>

编写测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.powernode.spring6.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Spring6Test {

@Test
public void testFirst(){
// 第一步:获取Spring 容器对象。
// ApplicationContext 翻译为:应用上下文。其实就是Spring容器
// ApplicationContext 是一个接口。
//ApplicationContext 接口下有很多实现类。其中有一个实现类叫做: CLassPathXmLApplicationContext
// CLassPathXmlApplicationContext 专门从类路径当中加我spring配置文件的一个Spring上下文对象。
// 这行代码只要执行:就相当于启动了Spring容器,解析spring.xml文件,并且实例化所有的ben对象,放到spring容器当中。
// 初始化Spring容器上下文(解析beans.xml文件,创建所有的bean对象)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//第二部:根据id获取bean对象
Object userBean = applicationContext.getBean("userBean");
System.out.println(userBean);
}
}
  • 需要注意的问题:
    • spring的配置文件中id不能重复
    • Spirng底层是通过反射机制调用无参数的构造方法,因此想让spring创建对象,必须保证无参数构造方法存在
    • 配置文件的名称不固定,需要我们在创建类之前手动提供配置文件
    • beans.xml这样的配置文件可以有多个,初始化Spring之前指定好即可
    • Spring配置文件配置的bean既可以是JDK中的类,也可以是自定义的,只要这个类不是抽象的,且提供了无参构造方法即可
    • 直接getBean()方法返回的类型是Object类,如果想直接返回指定类,则需要添加类的Class类型作为第二个参数
1
User user = applicationContext.getBean("userBean", User.class);

使用Bean工厂获得Bean

BeanFactory是Application的超级父接口,也可以用来加载Bean

1
2
3
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring.xml");
Object vipBean = beanFactory.getBean("vipBean");
System.out.println(vipBean);

启用Log4j2日志框架

  • 首先引入Log4j2的依赖
1
2
3
4
5
6
7
8
9
10
11
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
  • 然后在类的根路径提供一个log4j2.xml的配置文件(文件名固定,且必须放到类的根路径下)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<loggers>
<!--
level指定日志级别,从低到高的优先级:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>

<appenders>
<!--输出日志信息到控制台-->
<console name="spring6log" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>

</configuration>
  • 使用日志框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Spring6Test {
@Test
public void testFirst(){

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userBean = applicationContext.getBean("userBean");
System.out.println(userBean);

// 你自己怎么去使og4j2 记录日志信息呢?
// 第一步:创建日志记录器对象
// 获FirstSpringTest类的日志记录器对象,也就是说只要FirstSpringTest类中的代码执行记录日志的话,就输出相关的日志信息。
Logger logger = LoggerFactory.getLogger(Spring6Test.class);

// 第二步:记录日志,根不同的级别来输出日志
logger.info("我是一条日志消息");
logger.debug("我是一条调试消息");
logger.error("我是一条错误消息");
}
}

Spring对IoC的实现

IoC控制反转

  • Ioc控制反转是一种设计思想,而依赖注入是IoC思想的具体实现。
  • IoC控制反转的作用:降低程序耦合度,提高程序扩展力,达到OCP原则的实现。
  • Ioc控制反转中,反转的是什么?
    • 反转的是对象间依赖关系的控制权。
    • 将对象的创建权利和对象间关系的维护权交出去,交给第三方容器负责

依赖注入

  • Spring通过依赖注入来完成Bean的管理
  • 两种注入方式:
    • Set注入
    • 构造注入
  • Bean管理: Bean对象的创建,以及Bean对象中属性的赋值(Bean对象间关系的维护)

Set注入

  • Set注入是基于Set方法实现的,故需提供对应的Set方法,且Set方法应符合标准命名规范,底层会通过反射来调用Set方法给属性赋值
1
2
3
4
5
6
7
8
9
10
11
12
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>

</beans>
  • 实现原理:
    • 通过Property标签来获得属性名,ref属性来获得注入的属性类型(通过ref属性来完成bean的装配,这是bean最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)
    • 通过属性名来推断出Set方法名(故Set方法应遵循标准命名规范)
    • 利用反射机制调用相关Set方法来给属性赋值

构造注入

  • 核心原理:调用构造方法为属性赋值
    1
    2
    3
    4
    5
    <bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
    <!--index="0"表示构造方法的第一个参数,将orderDaoBean对象传递给构造方法的第一个参数。-->
    <constructor-arg index="0" ref="orderDaoBean"/>
    </bean>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class OrderService {
    private OrderDao orderDao;

    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao) {
    this.orderDao = orderDao;
    }

    public void delete(){
    orderDao.deleteById();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class OrderService {
    private OrderDao orderDao;
    private UserDao userDao;

    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao, UserDao userDao) {
    this.orderDao = orderDao;
    this.userDao = userDao;
    }

    public void delete(){
    orderDao.deleteById();
    userDao.insert();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
    <!--第一个参数下标是0-->
    <constructor-arg index="0" ref="orderDaoBean"/>
    <!--第二个参数下标是1-->
    <constructor-arg index="1" ref="userDaoBean"/>
    </bean>

    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
  • 也可以不使用参数的下标,直接使用参数的名字实现构造注入,建议最好顺序不要乱,这样会更清晰
1
2
3
4
5
6
7
8
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--没有指定下标,也没有指定参数名字-->
<constructor-arg ref="orderDaoBean"/>
<constructor-arg ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
  • 构造注入:
    • 可以通过下标来指定参数的位置
    • 可以通过参数名字来指定参数的位置
    • 也可以不指定下标和参数名,根据类型自动推断

Set注入专题

注入外部Bean

  • 前面演示的Set注入就是注入外部Bean的方式
  • 注入外部Bean的特点:Bean定义在外面,直接使用ref标签注入即可
1
2
3
4
5
6
7
8
9
10
11
12
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>

</beans>

注入内部Bean

  • 注入内部Bean的特点:Bean定义在里面,需要在Bean标签中嵌套Bean标签,用的较少
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">

<bean class="com.powernode.spring6.dao.UserDao"/>

</property>
</bean>

</beans>

注入简单类型

  • 简单类型包括:
    • 基本数据类型
    • 基本数据类型的包装型
    • String和Class
    • Number子类
    • Date可以通过PropertyEditor 或 Converter 将字符串转换为日期类型
    • Enum子类
    • URL等扩展类型均可以
  • 注入简单类型的特点:直接使用value标签注入即可
1
<property name="file" value="classpath:config.properties"/>

级联属性赋值(了解)

  • 级联属性赋值:在Spring的Set注入中,集合中存放的是对象类型,可以直接在集合定义时为内部对象的属性赋值,简单来说就是可以在Set中给对象的私有属性赋值,而不需要单独创建对象再Set
  • 要点:
    • 必须注意配置顺序正确
    • 在Spring的配置文件中,clazz属性必须提供Getter方法,因为在复制过程中,Spring需要通过Getter方法获取clazz,再为clazz的属性赋值
1
2
3
4
5
6
7
8
9
10
11
@Data
public class Clazz {
private String name;

public Clazz() {
}

public Clazz(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class Student {
private String name;
private Clazz clazz;

public Student() {
}

public Student(String name, Clazz clazz) {
this.name = name;
this.clazz = clazz;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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="clazzBean" class="com.powernode.spring6.beans.Clazz"/>

<bean id="student" class="com.powernode.spring6.beans.Student">
<property name="name" value="张三"/>

<!--要点1:以下两行配置的顺序不能颠倒-->
<property name="clazz" ref="clazzBean"/>
<!--要点2:clazz属性必须有getter方法-->
<property name="clazz.name" value="高三一班"/>
</bean>
</beans>

注入数组

  • 当数组中元素是简单类型时直接使用数组标签即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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="person" class="com.powernode.spring6.beans.Person">
<property name="favariteFoods">
<array>
<value>鸡排</value>
<value>汉堡</value>
<value>鹅肝</value>
</array>
</property>
</bean>
</beans>
  • 当数组中的元素是非简单类型时,需要使用array标签的ref元素来引用其他Bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Data
    public class Goods {
    private String name;

    public Goods() {
    }

    public Goods(String name) {
    this.name = name;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    @Data
    public class Order {
    // 一个订单中有多个商品
    private Goods[] goods;
    public Order() {
    }
    public Order(Goods[] goods) {
    this.goods = goods;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <?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代表不同种类的水果
    <bean id="goods1" class="com.powernode.spring6.beans.Goods">
    <property name="name" value="西瓜"/>
    </bean>

    <bean id="goods2" class="com.powernode.spring6.beans.Goods">
    <property name="name" value="苹果"/>
    </bean>

    <bean id="order" class="com.powernode.spring6.beans.Order">
    <property name="goods">
    <array>
    <!--这里使用ref标签即可-->
    <ref bean="goods1"/>
    <ref bean="goods2"/>
    </array>
    </property>
    </bean>

    </beans>

要点:

  • 数组是简单类型,使用value标签
  • 数组是飞简单类型,使用ref标签

注入List集合

  • 与注入数组类似,使用List标签即可,如果是简单类型,使用value标签,如果是非简单类型,使用ref标签
1
2
3
4
5
@Data
public class People {
// 一个人有多个名字
private List<String> names;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="names">
<list>
<value>铁锤</value>
<value>张三</value>
<value>张三</value>
<value>张三</value>
<value></value>
</list>
</property>
</bean>
</beans>

注入Set集合

  • 与注入其他类型一致,使用标签,简单类型使用value,非简单类型可以使用ref
1
2
3
4
5
@Data
public class People {
// 一个人有多个电话
private Set<String> phones;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="phones">
<set>
<!--非简单类型可以使用ref,简单类型使用value-->
<value>110</value>
<value>110</value>
<value>120</value>
<value>120</value>
<value>119</value>
<value>119</value>
</set>
</property>
</bean>
</beans>

注入Map集合

要点:

  • 使用标签
  • 如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
  • 如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
1
2
3
4
5
@Data
public class People {
// 一个人有多个住址
private Map<Integer, String> addrs;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="addrs">
<map>
<!--如果key不是简单类型,使用 key-ref 属性-->
<!--如果value不是简单类型,使用 value-ref 属性-->
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
</map>
</property>
</bean>
</beans>

注入Properties

  • java.util.Properties继承java.util.Hashtable,所以Properties本质上也是一个Map集合,故注入方式类似

要点:

  • 使用标签
  • 如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
  • 如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
1
2
3
4
@Data
public class People {
private Properties properties;
}

注入null和空字符串

  • 注入空字符串使用:或者 value=””
  • 注入null使用:或者 不为该属性赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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="vipBean" class="com.powernode.spring6.beans.Vip">
<!--空串的第一种方式-->
<!--<property name="email" value=""/>-->
<!--空串的第二种方式-->
<property name="email">
<value/>
</property>
</bean>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">
//注入Null的第一种
<bean id="vipBean" class="com.powernode.spring6.beans.Vip" />
//注入Null的第二种
<bean id="vipBean" class="com.powernode.spring6.beans.Vip">
<property name="email">
<null/>
</property>
</bean>
</beans>

注入的值含义特殊符号

  • XML中有5个特殊字符,分别是:<、>、’、”、&
  • 以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中就会报错。

解决方案包括两种:

  • 第一种:特殊符号使用转义字符代替。
  • 第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在CDATA区中的数据不会被XML文件解析器解析。

5个特殊字符对应的转义字符分别是:

特殊字符转义字符
>>
<<
'
"
&&
1
2
3
4
5
6
7
8
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result" value="2 &lt; 3"/>
</bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>

</beans>

P命名空间注入

  • 目的:简化Set注入。
  • 前提条件:
    • 在XML文件中引入P命名空间:xmlns:p="http://www.springframework.org/schema/p"
    • P命名空间是基于Setter方法的,所以需要对应属性提供Setter方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.powernode.spring6.beans;
@Data
public class Customer {
private String name;
private int age;

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

public void setAge(int age) {
this.age = age;
}
}
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="customerBean" class="com.powernode.spring6.beans.Customer" p:name="zhangsan" p:age="20"/>

</beans>

C命名空间注入

  • 目的:简化构造注入。
  • 前提条件:
    • 在XML文件中引入C命名空间:xmlns:c="http://www.springframework.org/schema/c"
    • C命名空间是基于构造方法的,所以需要对应属性提供构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.powernode.spring6.beans;
@Data
public class MyTime {
private int year;
private int month;
private int day;

public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:year="1970" c:month="1" c:day="1"/>-->

<bean id="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:_0="2008" c:_1="8" c:_2="8"/>

</beans>

不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型。

Util命名空间

  • 目的:让配置复用
  • 前提条件:在XML文件中引入Util命名空间:xmlns:util="http://www.springframework.org/schema/util"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<util:properties id="prop">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>

<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>

<bean id="dataSource2" class="com.powernode.spring6.beans.MyDataSource2">
<property name="properties" ref="prop"/>
</bean>
</beans>

基于XML的自动装配

  • Spring的自动化注入又被称为自动装配,能根据名字进行自动装配,也可根据类型进行自动装配。
  • 无论是通过名字还是类型进行自动装配,都是基于Set方法进行自动装配的,所以Set方法必须提供。(不考虑使用注解)

基于名字进行自动装配

  • 底层调用Set方法进行注入。主要是根据属性名称所对应的Set方法与Bean的ID之间的关联进行自动装配
1
2
3
4
5
6
public class UserDao {

public void insert(){
System.out.println("正在保存用户数据。");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class UserService {
private UserDao aaa;
// 这个set方法非常关键
public void setAaa(UserDao aaa) {
this.aaa = aaa;
}

public void save(){
aaa.insert();
}
}
1
2
3
4
5
6
7
8
9
10
<?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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>

<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>

</beans>
  • 配置文件中的autowire=”byName”起到关键作用,表示通过名称进行自动装配
  • UserService类中有一个UserDao属性,而UserDao属性的名字是aaa,对应的set方法是setAaa(),正好和UserDao Bean的id是一样的。这就是根据名称自动装配。

基于类型进行自动装配