前置类的更改

src/main/java/pojo/IdCard

package pojo;

import java.io.Serializable;
import java.util.Objects;

// 身份证
public class IdCard implements Serializable {

private Integer id;
private String idNo; // 身份证号
private String address; // 地址

public IdCard() { }

@Override
public String toString() {
return "IdCard{" +
"id=" + id +
", idNo='" + idNo + '\'' +
", address='" + address + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdCard idCard = (IdCard) o;
return Objects.equals(id, idCard.id) &&
Objects.equals(idNo, idCard.idNo) &&
Objects.equals(address, idCard.address);
}

@Override
public int hashCode() { return Objects.hash(id, idNo, address); }

public Integer getId() { return id; }

public void setId(Integer id) { this.id = id; }

public String getIdNo() { return idNo; }

public void setIdNo(String idNo) { this.idNo = idNo; }

public String getAddress() { return address; }

public void setAddress(String address) {this.address = address;}
}

src/main/java/pojo/People

package pojo;

import java.io.Serializable;
import java.util.Objects;

public class People implements Serializable {
private Integer id;
private String name;
private IdCard idCard;

public People() {
System.out.println("无参数构造方法");
}

public People(IdCard idCard) {
System.out.println("(IdCard idCard)参数构造方法");
this.idCard = idCard;
}

public People(Integer id, String name, IdCard idCard) {
System.out.println("(Integer id, String name, IdCard idCard) 参数构造方法");
this.id = id;
this.name = name;
this.idCard = idCard;
}

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

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return Objects.equals(id, people.id) &&
Objects.equals(name, people.name) &&
Objects.equals(idCard, people.idCard);
}

@Override
public int hashCode() {
return Objects.hash(id, name, idCard);
}

public void init(){
System.out.println("初始化方法运行。");
}

public Integer getId() { return id; }

public void setId(Integer id) {
System.out.println("setId方法运行");
this.id = id;
}

public String getName() { return name; }

public void setName(String name) {
System.out.println("setName方法运行");
this.name = name;
}

public IdCard getIdCard() { return idCard; }

public void setIdCard(IdCard idCard) {
System.out.println("setIdCart方法运行:" + idCard);
this.idCard = idCard;
}
}

自动注入(自动装配)

被 Spring 容器管理的若干个 Bean 对象,如果对象之间有关系,可以实现自动装配。

自动装配:按照某种指定的规则,自动把容器种管理的 bean 对象,做赋值注入。

自动装配规则配置: 可以有两个位置,分别是根标签的 beans 属性和 bean 标签的属性。

根标签的属性配置是全局自动装配,bean 标签的是局部自动装配。都配置,局部优先。

default-autowire

在根标签 <beans> 中配置 default-autowire 属性。标签整个 Spring 中自动注入的策略。可取值有 5 个。

default:默认值。不自动注入。

no:不自动注入。

byName:通过名称自动注入。按照 bean 对象中的属性名,自动寻找容器中与当前属性同名的 bean 进行注入。如果同名 bean 的类型不匹配,抛出异常。

byType:通过类型自动注入。按照 bean 对象中的属性类型,自动寻找容器中与当前 bean 类型匹配的 bean 进行注入。如果有多个相同类型的 bean 注入会出现异常。

constructor:通过构造方法进行注入。

按照 bean 对象的构造方法,从容器中找构造参数同类型或同名称的 bean 对象。如果有自动注入进去。类型先 byType,如果同类型的 bean 有多个,再 byName,如果没有同名的,则不使用构造方法自动装配。

注意:构造方法类型和其他 Bean 的类型相同。

src/main/resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="people" class="pojo.People">
<property name="id" value="10"/>
<property name="name" value="张三"/>
</bean>
<bean id="idCard" class="pojo.IdCard">
<property name="id" value="1"/>
<property name="idNo" value="1"/>
<property name="address" value="监狱"/>
</bean>
</beans>
在测试类中测试效果

src/test/java/TestAutowired

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

/*
* 测试自动装配
*/
public class TestAutowired {
@Test
public void testAutowired(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
People people = context.getBean("people", People.class);
System.out.println(people);
}
}

由于 id 为 people 的 bean 中包含 IdCard 类型的 idCard 属性,类型相同,所以也会将 id 为 idCard 的 bean 也自动装填。

若是 byName ,则会去 bean 寻找 id 为 idCard 这个 bean 对象,找不到就返回 null。

输出:

无参数构造方法
setId方法运行
setName方法运行
setIdCart方法运行:IdCard{id=1, idNo='1', address='监狱'}
People{id=10, name='张三', idCard=IdCard{id=1, idNo='1', address='监狱'}}

测试一下 constructor:

src/main/resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="people" class="pojo.People">
<property name="id" value="10"/>
<property name="name" value="张三"/>
</bean>
<bean id="idCard" class="pojo.IdCard">
<property name="id" value="1"/>
<property name="idNo" value="1"/>
<property name="address" value="监狱"/>
</bean>
</beans>

输出:

(IdCard idCard)参数构造方法
setId方法运行
setName方法运行
People{id=10, name='张三', idCard=IdCard{id=1, idNo='1', address='监狱'}}

会发现相比较与 byName 调用无参构造方法 ,constructor 调用的是只有一个参数的构造方法。

如果将其注释掉则会调用无参构造方法并赋值为 null 而不是调用多个参数的构造方法。

autowire

<bean> 标签中配置 autowire 属性,和 default-autowire 取值相同。

当前 bean 对象的自动装配方案,局部优先。默认是 default。

唯一注意 default 表示全局 default-autowire 的值。如果 autowire 和 default-autowire 同时存在,autowire 生效。

src/main/resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="people" class="pojo.People" autowire="byName">
<property name="id" value="10"/>
<property name="name" value="张三"/>
</bean>
<bean id="idCard" class="pojo.IdCard" autowire="byName">
<property name="id" value="1"/>
<property name="idNo" value="1"/>
<property name="address" value="监狱"/>
</bean>
</beans>

注意:自动注入指的都是 bean 之间的自动注入。能够自动注入必须保证 Spring 容器中包含能被自动注入的 bean

在测试类中测试效果

测试类与上文一致

输出:

无参数构造方法
setId方法运行
setName方法运行
setIdCart方法运行:IdCard{id=1, idNo='1', address='监狱'}
People{id=10, name='张三', idCard=IdCard{id=1, idNo='1', address='监狱'}}

因为局部优先,调用 byName 所以不调用有参构造。

bean 标签的 scope 属性

Spring 中 <bean> 的 scope 控制的是 bean 的有效范围。

一共有 6 个可取值:

​ singleton:默认值。单例。一个 bean 标签一个 bean 对象,同一标签调用 getbean 方法每次获取 bean 都是同一个对象。

​ prototype:多例。每次获取 bean 都重新实例化,即同一标签调用 getbean 方法每次获取 bean 是不同对象。

​ request(Spring - web.jar):每次请求重新实例化对象,同一个请求中多次获取时是单例的。

​ session(Spring - web.jar):每个会话内 bean 是单例的。

​ application(Spring - web.jar):整个应用程序对象内 bean 是单例的。

​ websocket(Spring - websocket.jar):同一个 websocket 对象内对象是单例的。

里面的 singleton 和 prototype 在 Spring 最基本的环境中就可以使用,不需要 web 环境(需要添加依赖)。

但是里面的 request、session、application、websocket 都只有在 web 环境才能使用。

测试一下前两个:

singleton

默认情况下,在 ApplicationContext 创建结束时,bean 对象就都创建完成了。

src/main/resources/applicationContext.xml

<bean id="people" class="pojo.People" scope="singleton">

src/test/java/TestAutowired

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

public class TestAutowired {
@Test
public void testAutowired(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("Spring容器创建完毕");
System.out.println(context.getBean("people", People.class));
System.out.println(context.getBean("people", People.class));
}
}

输出:

无参数构造方法
setId方法运行
setName方法运行
Spring容器创建完毕
People{id=10, name='张三', idCard=null}
People{id=10, name='张三', idCard=null}

只回显了一次 “无参数构造方法” 说明只调用一次,只有一个对象。

可以使用 bean 标签的属性 lazy-init = “true”,让 ApplicationContext 创建时,不初始化 bean 对象。
lazy-init 默认是 default,采用全局的,就是 false。可选值有 false 和 true。
false:不延迟初始化,ApplicationContext 创建时,就初始化 bean 对象。
true:延迟初始化,getBean 方法调用时,才初始化 bean 对象。

src/main/resources/applicationContext.xml

<bean id="people" class="pojo.People" scope="singleton" lazy-init="true">

输出:

Spring容器创建完毕
无参数构造方法
setId方法运行
setName方法运行
People{id=10, name='张三', idCard=null}
People{id=10, name='张三', idCard=null}

prototype

在 ApplicationContext 创建结束时,不会创建这个 scope 有效范围的 bean 对象。

直到 getBean 方法被调用,才创建 bean 对象,getBean 方法调用多少次,创建多少对象。

极少的情况下,需要用多例。

src/main/resources/applicationContext.xml

<bean id="people" class="pojo.People" scope="prototype">

输出:

Spring容器创建完毕
无参数构造方法
setId方法运行
setName方法运行
People{id=10, name='张三', idCard=null}
无参数构造方法
setId方法运行
setName方法运行
People{id=10, name='张三', idCard=null}

只回显了两次 “无参数构造方法” 说明多次调用,有多个对象。

bean 对象初始化方法

init-method="方法名"

当 bean 对象创建,且属性注入后,调用的初始化方法。

src/main/resources/applicationContext.xml

<bean id="people" class="pojo.People" scope="singleton" lazy-init="false" init-method="init">

输出:

无参数构造方法
setId方法运行
setName方法运行
初始化方法运行。
Spring容器创建完毕
People{id=10, name='张三', idCard=null}
People{id=10, name='张三', idCard=null}

destroy-method

当 bean 对象被销毁前,执行的方法。一般用于回收资源。

单例的 bean 对象。是在 spring 容器 ApplicationContext 销毁的时候,才会销毁。

单例设计模式

设计模式:根据面向对象五大设计思想衍生出的 23 中常见代码写法,每种写法可以专门解决一类问题。

单例设计模式:保证某个类在整个应用程序中只有一个对象。

单例写法分为两种:饿汉式、懒汉式。

饿汉式和懒汉式的选择:

如果不知道该如何选择,优先使用饿汉式。

​ 饿汉式:

​ 优点:没有线程安全隐患。

​ 缺点:只要类加载,不管是否需要那个单例的对象,都会创建,对内存压力相对较高。

​ 懒汉式:

​ 优点:不使用单例对象,就不创建单例对象。对内存的压力相对较低。

​ 缺点:需要考虑线程安全隐患。

饿汉式

饿汉式:当类加载的时候,就把单例对象创建好了。

src/main/java/module/Singleton1

package module;

public class Singleton1 {
// 私有构造的价值是,避免其他任何类型可以new当前类型对象。
private Singleton1(){}
// 静态变量,定义的同时做初始化赋值。保证,类加载结束,对象已创建。
private static Singleton1 OBJECT = new Singleton1();
// 提供静态的方法,获取唯一的单例对象。
public static Singleton1 getInstance(){
return OBJECT;
}
}

在测试类中测试效果

src/test/java/TestSingleton

import module.Singleton1;
import module.Singleton2;
import org.junit.Test;

public class TestSingleton {
@Test
public void testSingleton1(){
// 饿汉式
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
System.out.println(s1 == s2);
}
}

懒汉式

懒汉式: 当要获取对象的时候,再去创建唯一的单例对象。

src/main/java/module/Singleton2

package module;

public class Singleton2 {
private Singleton2(){}
private static Singleton2 OBJECT;
public static Singleton2 getInstance(){
if(OBJECT == null){
// 第一次调用方法,还未创建对象。开始创建对象。
synchronized (Singleton2.class){
// 同步逻辑,在类对象上加锁。保证只有一个线程创建对象。
if(OBJECT == null){
// 加锁前,一定是OBJECT为null。但是,获取锁后,OBJECT未必是null。 需要再次判断。
OBJECT = new Singleton2();
}
}
}
return OBJECT;
}
}

在测试类中测试效果

src/test/java/TestSingleton

import module.Singleton1;
import module.Singleton2;
import org.junit.Test;

public class TestSingleton {
@Test
public void testSingleton1(){
// 饿汉式
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
System.out.println(s1 == s2);
}
}

Spring 循环注入

循环注入即多个类相互依赖,产生了一个闭环。

当两个类都是用构造注入时,没有等当前类实例化完成就需要注入另一个类,而另一个类没有实例化完整还需要注入当前类,所以这种情况是无法解决循环注入问题的的。会出现 BeanCurrentlyInCreationException 异常。

如果两个类都使用设值注入且 scope 为 singleton 的就不会出现问题,可以正常执行。因为单例默认下有三级缓存(DefaultSingletonBeanRegistry),可以暂时缓存没有被实例化完成的 Bean。

但是如果两个类的 scope 都是 prototype 依然报 BeanCurrentlyInCreationException 异常。

测试一下:

src/main/java/pojo/A

package pojo;

import java.io.Serializable;
import java.util.Objects;

public class A implements Serializable {
private String aName;
private B b;

public A(String aName, B b) {
this.aName = aName;
this.b = b;
}

public A() {
System.out.println("A无参构造运行");
}

@Override
public String toString() {
return "A{" +
"aName='" + aName + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
A a = (A) o;
return Objects.equals(aName, a.aName) &&
Objects.equals(b, a.b);
}

@Override
public int hashCode() {
return Objects.hash(aName, b);
}

public String getaName() {
return aName;
}

public void setaName(String aName) {
this.aName = aName;
}

public B getB() {
return b;
}

public void setB(B b) {
System.out.println("a.setB运行");
this.b = b;
}
}

src/main/java/pojo/B

package pojo;

import java.io.Serializable;
import java.util.Objects;

public class B implements Serializable {
private String bName;
private A a;

public B(String bName, A a) {
this.bName = bName;
this.a = a;
}

public B() {
System.out.println("B无参构造运行");
}

@Override
public String toString() {
return "B{" +
"bName='" + bName + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
B b = (B) o;
return Objects.equals(bName, b.bName) &&
Objects.equals(a, b.a);
}

@Override
public int hashCode() {
return Objects.hash(bName, a);
}

public String getbName() {
return bName;
}

public void setbName(String bName) {
this.bName = bName;
}

public A getA() {
return a;
}

public void setA(A a) {
System.out.println("b.setA运行");
this.a = a;
}
}

src/main/resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="default">

<bean id="a" class="pojo.A" scope="singleton">
<property name="aName" value="aaa"></property>
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="pojo.B" scope="singleton">
<property name="bName" value="bbb"></property>
<property name="a" ref="a"></property>
</bean>

</beans>

src/test/java/TestCyc

import pojo.A;
import pojo.B;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/*
* 测试循环注入
* 不能使用构造注入,因为循环依赖,对方都未创建,无法作为参数,调用当前的有参构造方法。
* 可以使用设置注入,因为可以先用无参构造创建对象,后调用setter做对象关系的设置注入。
* 在Spring容器中,是使用三级缓存实现的。
* 要求:循环注入的多个bean,至少有一个是单例的,就可以实现循环注入。都是多例的,抛出异常。
*/
public class TestCyc {
@Test
public void testCyc(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("ApplicationContext创建结束");
A a = context.getBean("a", A.class);
System.out.println(a);
System.out.println(a.getB());

System.out.println("=========================");

B b = context.getBean("b", B.class);
System.out.println(b);
System.out.println(b.getA());
}
}

输出:

A无参构造运行
B无参构造运行
b.setA运行
a.setB运行
ApplicationContext创建结束
A{aName='aaa'}
B{bName='bbb'}
=========================
B{bName='bbb'}
A{aName='aaa'}

Spring 整合 web

Spring 项目不需要依赖 Web 环境,但是 Java 项目大多数是 Web 项目,所以 Spring 也支持集成到 Web 环境中。

Spring 集成 Web 环境是通过 Listener 实现的,在 ServletContext 对象创建时加载 Spring 容器。Spring 已经在 spring-web.jar 包中提供了 ContextLoaderListener 实现加载 Spring 配置文件的代码。我们只需要在 web.xml 配置 <listener> 标签让 ContextLoaderListener 生效,并且告诉 ContextLoaderListener 加载 Spring 配置文件的路径即可。

创建项目,并添加依赖

创建 Maven 的 web 项目(添加上 webapp 目录等,并配置 web 模块),并在 pom.xml 配置依赖。

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

<parent>
<artifactId>MySpring</artifactId>
<groupId>MySpring</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<!-- 将项目改为 war 项目 -->
<packaging>war</packaging>

<artifactId>MySpring_3</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<!-- 添加 spring-web 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.20</version>
</dependency>
<!-- 添加 jsp 依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- 添加 servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- 添加 Tomcat 插件 -->
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 发布到 Tomcat 的名称,即 URL 访问名称 -->
<path>/</path>
<!-- 8080 端口 -->
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
</project>

2. 编写类

随意创建一个类,为了测试是否能从容器中获取到 bean

创建 src/main/java/People

public class People {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
'}';
}
}

3.编写 Spring 配置文件

建议:建立上 applicationContext.xml 的模板。

里面创建 People 的 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="people" class="People">
<property name="id" value="123"></property>
</bean>
</beans>

4. 配置 web.xml

在 web.xml 中配置监听器,让 web 项目启动时自动创建 Spring 容器对象( WebApplicationContext )。

上下文参数名 param-name 的值是固定的 contextConfigLocation。这个值是监听器需要获取的值。

上下文参数值需要带有 classpath,表示加载类路径内容。target/classes 目录内容。如果写成 classpath*: 表示当前项目依赖的 jar 中资源也会寻找。classpath: 后面的值支持星号。例如 applicationContext-*.xml 表示加载所有以 applicationContext- 开头的 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">

<!-- 配置listener可读取解析的上下文初始化参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

</web-app>

5.编写Servlet

创建 src/main/java/DemoServlet

WebApplicationContextUtils 是工具类,可以快速获取到容器对象。

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import service.UserService;

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("/demo")
public class DemoServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从ServletContext中获取WebApplicationContext对象。
// 使用Spring提供的工具去获取WebApplicationContext对象
// 相当于getWebApplicationContext,增加了查找逻辑,如果attributeName不是WebApplicationContext.ROOT,
// 会查找其他名字的application中的attribute。
WebApplicationContext wac = WebApplicationContextUtils.findWebApplicationContext(req.getServletContext());
People peo = wac.getBean("peo", People.class);
System.out.println(peo);
}
}

6.访问测试

访问:http://localhost:8080/demo 观察 IDEA 控制台是否输出 people 对象的值。