Spring
内容管理
Javaweb —Spring
Spring框架技术
Mybatis框架相对来说使用比较简单,封装的就是JDBC,避免了原生JDBC的复杂的操作,让数据库代码不与业务代码混杂;mybatis是工作在数据持久层的,就是代替的dao的实现类的操作;但是还是需要创建sqlSession对象【injection 注入】
Spring框架则是为了进行方便创建对象的,接下来就会给大家分析这个框架的使用;毕竟是主流框架
Spring是一个广泛的名词,Spring全家桶中有很多单独的框架:spring、springboot、springmvc、springCloud
Spring
这个框架出现目的就是解决企业开发的难度,减轻项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,同时管理对象之间的关系。 Spring中有两个关键的技术就是LOC— 控制版本和AOP----面向切面编程;能够实现模块之间、类之间的解耦合
依赖
: 类A使用类B的属性和方法,就叫做类A依赖类B; 在maven中就解释过,就是dependencies和dependency; Maven是管理项目所使用的jar包也就是外部的依赖;而Spring是管理项目内部的类之间的依赖;通过UML就可以知道手工管理依赖是非常麻烦的事情
可以访问Spring的官网Spring | Home; 进去之后可以看到Spring全家桶中的所有的框架;里面有官方的文档和对于类的使用的介绍,这里就不详细说明了
Spring特点
-
轻量 ----- Spirng框架使用的jar都比较小,一般都是1M以下,Spring的所有的核心功能的jar就几M;Spring框架运行占用的资源少,运行效率高,不依赖其他的jar
-
面向接口编写,解耦合 ----- Spring提供了ioc控制反转,由容器管理对象、对象的依赖关系,传统的创建对象由容器完成,对象之间的依赖解耦合
-
多模块 : Spring框架由很多的模块构成,差不多20多个,可以分为数据的访问继承(data access/integration)、web、面向切面编程(AOP、Aspects)、提供JVM的代理(instrumentation)、消息发送(Messaging)、核心容器(core container)、测试(Test)
这样就很清楚了,Spring能够配合ORM中间件进行数据库的持久化操作,这里就解释以下什么是ORM
ORM
: object relational Mapping 对象映射关系;其实这个概念以前就提过了,ORM模式是为了解决面向对象域关系数据库的不匹配现象的技术; 也就是说是为了将表/记录和类/对象进行映射的模式。ORM通过映射关系将对象自动持久化到数据库中; 域模型或者关系模型发生变化,都需要修改持久化层得代码(三层架构)- 实现持久化的操作最简单的方式就是硬件编码: 也即是为每一种可能得数据库操作提供单独方法【不使用框架】; 但是这样子会出现庞大得重复,并且出现了很多业务不相关得代码,并且由于没有映射关系,所以需要自己实现Dao接口,这样数据访问层就会十分庞大
- ORM也是实现持久化层得另外得方式,采用映射的方式来描述对象关系的映射,ORM中间件能够在业务逻辑层和数据库层之间充当桥梁; 典型的ORM中间件就是hibernate、mybatis等,一个持久化类和一个表对应,一个对象对应一个记录,一个属性对应一个字段; — 这样通过动态代理【反射】的方式就可以通过标签、名称来获得操作;简化持久化层的操作 ----- 但是确定就是牺牲了执行的效率和固化了思维;使用mybatis就只需要配置mapper文件和主配置文件,还有就是编写sql语句,其他的操作都是mybatis动态生成的,这样使得优化性能方面就没有可以优化的点;并且操作就没有了创新的地方;因为是固定的操作;在进行对象持久化的时候,ORM一般持久化所有的属性,但是有时不需要; —>mybatis进行对象传参的时候会将对象的所有的属性进行操作,但是其实不需要持久化所有的属性
所以框架确实解决了很多问题,但是框架为了尽可能满足”用户“的需求,让编码简单,封装了很多的操作,比如传参,之后分析Mybatis源码的时候会分享其反射的过程;就可以看到mybatis处理对象传参的过程😿;所以framework还是不能过于依靠了
关系模型
: 用二维表的形式来表示实体和实体间联系的数据模型;简单来说就是二维表格模型;关系型数据库就是由二维表及其之间的关系组成的数据组织域模型
: 软件领域,模型就是代指的真实事件的实体,设计阶段需要域模型;域模型就是融合了行为和数据的对象模型;其初步分为失血模型、贫血模型、充血模型、涨血模型
IOC控制反转
Spring框架就是一个软件,框架要完成一个功能,就必须要遵循框架的语法。Spring的重要部分就是IOC,IOC – inversion of control — 控制反转
控制反转是一种思想 ----- 将传统上由程序代码直接操作的对象调用权力交给容器【programmer弱化new 对象】,通过容器来实现对象的装配和管理、控制反转就是对对象的控制权的转义,从代码本身交给外部的容器;通过容器实现对象的创建、对象的复制、依赖的管理; Spring时使用DI来实现的IOC
控制 : 创建对象,对象的属性赋值,对象之间的关系管理
反转 : 由外部容器管理对象的操作,权限转移给容器【Tomcat创建Servlet对象也不需要programmer参与】
容器: 容器可以是服务器软件【tomcat】 ,也可以是一个框架【spring】
正转 : 由开发人员在代码中,使用new 的方式创建对象,主动管理对象
IOC模式的实现方式比较流行的就是 ---- 依赖注入 dependency injection: 程序代码不做定位查询,这些工作由容器完成;DI就是程序运行时,如果需要调用另外一个对象,比如new student();无需在代码中进行操作,而是使用外部的容器,容器创建对象后交给代码执行
Spring容器就是一个巨大的工厂:负责创建、管理所有的java对象;这些对象就称为Bean,Spring容器管理容器中Bean的依赖关系,使用DI的方式来管理Bean的依赖,使用IOC类实现对象间的解耦合
使用IOC的作用
使用IOC可以减少对于代码的改动,也可以实现不同的功能;【解耦合】 如果在代码中直接new Student();那么如果之后要实现不同的功能,就要做大量的修改,这样就耦合了
【Tomcat】Servlet就使用过IOC
创建一个类Servlet会自动继承HttpServlet;这个HttpServlet是继承的genericServlet—>servlet;然后绑定;在servlet的doGet/Post写访问执行的代码; 触发后,是Tomcat自动帮助创建的servlet的实例对象调用构造方法;这里就是使用了IOC的模式; Tomcat也被称为容器---->存放着servlet对象、监听器、过滤器对象等; IOC就让修改非常简单,直接在注解中修改即可
java创建对象的方式
在java中创建对象的方式由很多种
- 构造方法 : new 对象
- 反射 class.getDeclaredConstructor.newInstance() 【记不清了,好像是】class是类的字节码,类的字节码的获取也有多种方式
- 序列化
- 克隆
- 动态代理 — mybatis创建Dao接口的对象
- IOC方式— 容器创建对象
DI
IOC的技术实现就是依赖注入dependency injection;只需要在程序中提供要使用对象的名称,而对象如何在容器中创建赋值、查找都是容器内部进行实现;通过名称使用对象即可; Spring就使用DI技术实现IOC,底层使用的反射机制,Spring就是一个容器
Spring-IOC示例
首先先展示以下Spring的大概的用法,首先就是IOC了,实现的步骤
maven项目,加入依赖
这里就先加入spring-context的依赖,标签就是dependency injection;
Spring下面的依赖spring mvc是代替servlet执行功能的;而spring context 就是实现IOC功能的— 控制反转,使用容器创建对象
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
这里使用最新版的5.3.14会出现问题,所以最终导入的是5.3.13的版本
创建类【接口和其实现类】–test
就像servlet一样,这里我们要首先创建一个要交给Spring管理的类
package cfeng.service;
public interface ManagerService {
//简单的测试方法
public void doSome();
}
//下面编写其实现类
package cfeng.service.impl;
import cfeng.service.ManagerService;
public class SomeService implements ManagerService {
@Override
public void doSome() {
System.out.println("执行some方法");
}
}
创建Spring配置文件,声明类信息
Tomcat容器实现创建对象就是将我们创建的Servlet类等绑定到web.xml文件,这样Tomcat容器才会创建这些类的对象; Spring中也一样,要在spring的配置文件中声明这些类的信息,这些类就用Spring进行创建和管理
Spring的配置文件和mybatis的主配置文件以及数据库配置文件相同,都是直接放在resource目录下面;
创建Spring配置文件,IDEA提供了快捷的方式,就是使用新建—>XML—>spring configuration
<?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>
这其中,beans 是根标签----> 在spring中,将java的对象称为bean
Spring spring-beans.xsd也是一种约束文件,xsd是比dtd更强的约束文件,所以很多都使用这种类型的约束文件;
使用bean标签就可以声明bean --- 告诉Spring要声明某个类的对象 ; id是对象的自定义名称,唯一性;class是类的全限定名称【不能是接口,因为是使用反射机制创建的对象,接口不行】
<bean id="someService" class="cfeng.service.impl.SomeService"/>
这里就类似于Tomcat容器中的web.xml中的servlet-name和servlet-class; 这个name就是表明要创建这个类型的对象访问之后
Tomcat中有两张Map;一张map就是路径和对象;当没有找到的时候,会访问第二张Map创建对应Servlet的对象存入到第一张Map中供使用; Spring的底层也是使用的Map来存储对象
相当于是 new 对象之后,然后放到Map中 put(“id的值”,“对象”); — 所以通过id的值就可以拿到对象;Servlet中通过一个路径就可以拿到servlet对象 一个bean标签声明一个对象
test
如果不使用Spring容器来管理对象,就需要programmer来手工创建对象
package cfeng;
import cfeng.service.ManagerService;
import cfeng.service.impl.SomeService;
import org.junit.Test;
public class testSomeService {
@Test
public void testDoSome(){
//不使用Spring,就需要new 对象
ManagerService service = new SomeService();
service.doSome();
}
}
这里就是创建了一个Service对象,然后使用这个对象来执行实例方法;这就相当于是正转的方式
那要如何使用Spring创建对象呢?
首先和Mybatis最初相同,要制定Spring配置文件的名称;
然后创建表示Spring容器的对象,也就是ApplicationContext对象,ApplicationContext就是表示Spring容器;通过容器对象就能够使用Spring创建对象
//applicationContext是一个接口
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
//最常用的实现类
//从磁盘中读取配置文件
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
//从类路径中加载配置文件target/classes 然后创建一个容器
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
这样正转创建一个容器之后,就可以通过容器的getBean方法来获取对象,传入的参数就是bean的id
package cfeng;
import cfeng.service.ManagerService;
import cfeng.service.impl.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testSomeService {
@Test
public void testDoSome(){
//不使用Spring,就需要new 对象
// ManagerService service = new SomeService();
// service.doSome();
//首先获得Spring配置文件的名称; 这里直接放在resource下面的,所以就直接是名称
String config = "beans.xml";
//获得Spring容器的对象,使用ApplicationContext就可以表示容器
ApplicationContext springContainer = new ClassPathXmlApplicationContext(config);
//从容器中获取对象;调用对象的方法getBean 【可以通过id获取】;获得对象之后要进行强制类型转换
ManagerService service = (ManagerService) springContainer.getBean("someService");
service.doSome();
}
}
这样就成功创建了对象;和Tomcat中类似,tomcat使用的是路径,通过路径就可以绑定一个对象
Spring创建对象时机
代码是从前向后依次执行,所以这里的过程就是先制定配置文件,然后使用ClassPathXmlAplicationContext的构造方法解析spring配置文件创建一个容器; — 这里创建对象就是在解析过程中发生的【反射机制调用构造器然后创建对象】
进入配置文件,遇到了bean标签;然后Spring通过bean的class类的全限定名称,利用反射机制创建一个实例对象;然后和id一起放到Map中
之后通过getBean就相当于从这张Map中取出对象就可以了;读取文件的同时就已经创建好对象了
在创建Spring容器时,会创建Spring配置文件中的所有的对象;一个bean标签对应的一个对象
获取Spring文件中的java对象的信息
- 使用Spring容器的方法,getBeanDefinitionCount 就可以获得容器中定义的对象的数量
- 使用getBeanDefinitionNames 数组类型 获得容器中定义的所有对象的名称
package cfeng;
import cfeng.service.ManagerService;
import cfeng.service.impl.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
public class testSomeService {
@Test
public void testSpring(){
String config = "beans.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(config);
//通过Spring提供的方法getBeanDefinitionCount就可以获取容器中对象的数量
int num = container.getBeanDefinitionCount();
System.out.println(num);
//通过方法getBeanDefintionNames可以获得容器中定义的所有对象的id构成的数组
String[] names = container.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);//利用流stream来操作数组中的每一个对象,就可以不用for循环了
}
}
这里使用这两个方法就可以知道容器中的对象的信息
非自定义对象创建 【default — 无参构造】
上面演示的时创建的自定义的类的对象,那么非自定义的类型的对象是否可以通过这种绑定的方式创建呢?这是肯定的,就直接写类的全限定名称就可创建了,这个和是否自定义没有关系
<bean id="mydate" class="java.util.Date"/>
String config = "beans.xml";
//获得Spring容器的对象,使用ApplicationContext就可以表示容器
ApplicationContext springContainer = new ClassPathXmlApplicationContext(config);
//从容器中获取对象;调用对象的方法getBean 【可以通过id获取】;获得对象之后要进行强制类型转换
Date date = (Date) springContainer.getBean("mydate"); System.out.println(date.toString());
这里输出的结果就是一个日期的类型 Sat Jan 08 14:55:14 CST 2022 — 这是标准的时间
Spring创建对象,默认调用的时无参数的构造方法
DI dependency injection
DI: 依赖注入 D是dependency依赖,表示的是模块所要依赖的所有的内部的类,而I表示injection,注入,代表注入依赖的类的属性值【因为不可能都是调用无参构造的对象】,表示创建对象,给属性赋值;DI的实现方式有两种:一种是在Spring的配置文件中,使用标签和属性完成,类似于之前在web.xml配置servlet ; 另外一种是使用Spring中的注解,完成属性的赋值;类似于之前servlet中的@webServlet等注解的方式
DI的语法分类
- Set注入(设置注入) : Spring调用类的set方法,在set方法可以实现属性的赋值(应用广泛)构造方法太长
- 构造注入 : Spring调用类的有参的构造方法,创建对象,在构造方法中实现赋值
基于配置文件的DI
这个过程还是类似,要在Spring的配置文件除了简单使用bean的class和id之外,还要使用spring的语法,完成属性的赋值,也就是在文件中进行注入
- 首先先创建一个实体类,这里创建一个student
package cfeng.entity;
public class Student {
private int stuno;
private String stuname;
private String stuclass;
public void setStuno(int stuno) {
this.stuno = stuno;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public void setStuclass(String stuclass) {
this.stuclass = stuclass;
}
@Override
public String toString() {
return "Student{" +
"stuno=" + stuno +
", stuname='" + stuname + '\'' +
", stuclass='" + stuclass + '\'' +
'}';
}
}
因为是演示Spring的注入,所以就不用getter
- 之后在Spring的配置文件中进行依赖的绑定
<bean id="mystudent" class="cfeng.entity.Student"/>
- 之后可以简单测试; 注意使用快捷键ALT + 回车;可以对报错进行解决办法的选择,不用鼠标去点击;CTRL + ALT + O可以删除多余的包
Student student = (Student) springContainer.getBean("mystudent");
System.out.println(student);
输出的就是这个对象: Student{stuno=0, stuname=‘null’, stuclass=‘null’}
- 那么现在就演示以下setter注入的方式赋值属性【注入 — 赋值】
Spring和Mybatis相同,都规定java的基本类型和String类型是简单类型
相当于是Spring调用类的set方法,可以在set方法中完成属性的赋值
简单类型的set注入
直接在bean中,不是使用单个的标签,而是使用成对的标签,有开始和结束标签,然后在标签域中,使用property标签完成属性的赋值,一个property标签只能完成一个属性的赋值
<bean id="自定义名称" class="类的全限定名称">
<property name="属性名" value="属性值"/>
<property name="属性名" value="属性值"/>
………………
</bean>
这里可以演示,给Student进行赋值
<bean id="mystudent" class="cfeng.entity.Student">
<property name="stuno" value="1"/>
<property name="stuname" value="李四"/>
<property name="stuclass" value="HC2001"/>
</bean>
IDEA在property会自动提示类Student的属性~~
测试还是和上面相同,简单输出即可
Student{stuno=1, stuname=‘李四’, stuclass=‘HC2001’} ---- > 输出的就是一个已经有属性值的对象,非常的方便;不再需要手工进行赋值【但是必须要有set方法】这样想要修改属性值就直接修改配置文件即可,不需要修改代码
符合开闭原则
Spring的底层就是调用了类的set方法
可以操作康康
public void setStuno(int stuno) {
System.out.println("设置了学生的学号");
this.stuno = stuno;
}
public void setStuname(String stuname) {
System.out.println("设置了学生的姓名");
this.stuname = stuname;
}
public void setStuclass(String stuclass) {
System.out.println("设置了学生的班级");
this.stuclass = stuclass;
}
在类的set方法中都加入输出语句,一旦执行就会输出
设置了学生的学号
设置了学生的姓名
设置了学生的班级
Student{stuno=1, stuname='李四', stuclass='HC2001'}
发现在配置文件中使用property标签就相当于调用类的set方法,可以看到是按照定义标签的顺序进行执行的,执行开始标签的时候就创建了一个空的对象,使用property进行注入【先进行了对象的创建,之后再调用set方法】
set注入要求bean类必须有set方法; 如果没有set方法就会报错; 在property标签中设置name;就会自动执行set{name}的方法; spring就是执行set方法【如果set方法中不进行赋值,或者进行其他的操作,都会执行】;
只要在property中书写name,那么spring就会利用机制进行调用对应类的set{name}方法
这里的意思就是: 主要类中有这样一个方法,但是就算这个方法不是设置类的属性值的,也会正常执行
这也是框架的局限的地方,就是说只要放入property标签,就会执行set{标签name}方法,之后将value的值作为参数传入赋值或者执行其他的方法,如果没有这样的方法就会报错,如果有同名的方法就会执行方法【和属性没有关系】
//并没有这个属性,设置了这样一个方法
public void setStuqq(String stuqq){
System.out.println("执行了没有这个属性的方法");
System.out.println("传入的参数是 =" + stuqq);
}
<property name="stuqq" value="1378789"/>
Student student = (Student) springContainer.getBean("mystudent");
System.out.println(student);
执行之后就可以得到
执行了没有这个属性的方法
传入的参数是 =1378789
Student{stuno=1, stuname='李四', stuclass='HC2001'}
这里就可以推测property或者说set注入的方式: 直接执行set + Name 的方法,传入的参数就是value的值
---- 因为这是简单的检测【不会检测是否有属性】 xml要求所有的属性的值必须放到引号之中;只要有set方法就可以进行赋值
引用类型set注入
类和类之间还可以存在关联关系,当一个类的属性是引用类型,应该怎样进行set注入?
package cfeng.entity;
public class School {
private String address;
private int buildTime;
public void setAddress(String address) {
this.address = address;
}
public void setBuildTime(int buildTime) {
this.buildTime = buildTime;
}
}
----------------------------------------------------------
public class Student {
private int stuno;
private String stuname;
private String stuclass;
private School school;
引用类型的注入的配置不同,因为普通的property只能实现简单类型的对象的赋值,一个bean代表的就是一个对象,那么就先用bean创建被引用类型的对象,之后再在引用的类型中使用ref来调用这个对象进行赋值
//如果使用java代码就是这样
Student student = new Student(); ---> <bean id="mystudent" class="cfeng.entity.Student">
student.setStuno("1"); ----> <property name="stuno" value="1"/>
student.setStuname("张三"); <property name="stuname" value="张三"/>
student.setStuclass("HC2001"); <property name="stuclass" value="HC2001"/>
School school = new School(); <bean id="myschool" class="cfeng.entity.School">
school.setAddress("北京"); <property name="address" value="北京"/>
school.setBuildTime("1998"); <property name="buildTime" value="1998"/> <bean/>
student.setSchool(school); <property name="school" ref="myschool"/> <bean/>
也就是说,就是定义好一个对象,然后通过热引用的方式给传值
<bean id="myschool" class="cfeng.entity.School">
<property name="address" value="北京"/>
<property name="buildTime" value="1998"/>
</bean>
<bean id="mystudent" class="cfeng.entity.Student">
<property name="stuno" value="1"/>
<property name="stuname" value="李四"/>
<property name="stuclass" value="HC2001"/>
<property name="stuqq" value="1378789"/>
<property name="school" ref="myschool"/>
</bean>
Student{stuno=1, stuname='李四', stuclass='HC2001', school=School{address='北京', buildTime=1998}}
//必须要重写toString方法,分则就不会打印内容,而是一个地址类似的形式
也就是说:简单类型就是property标签使用value直接赋值; 引用类型就是property标签使用ref热引用其他的对象【通过id】
构造注入
构造注入就是指,在构造调用实例的时,完成调用者的实例化,也就是使用构造器设置依赖关系;
对于普通的java方法
public Student(int stuno, String stuname, String stuclass, School school) {
System.out.println("有参构造方法");
this.stuno = stuno;
this.stuname = stuname;
this.stuclass = stuclass;
this.school = school;
}
Spring调用类的有参构造方法,在创建对象的同时,在构造方法中给属性赋值,构造注入使用的是< constructor- arg>标签;
<constructor-arg/>标签,一个这个标签可以构造方法的一个参数;
属性name表示标签的属性; index表示构造方法的参数的位置【局限性】参数位置从左到右就是0、1、2
value是简单类型赋值 ref是引用类型赋值
- 第一种构造注入:使用name标签和value标签/ref 通过名称和参数绑定
- 第二种构造注入 : 使用index 和value/ref 将位置和参数进行绑定
- 省略index构造注入 : 就是按照参数原本的顺序,那么就可以省略index;直接value/ref
这里可以演示构造注入
<bean id="myschool" class="cfeng.entity.School">
<property name="address" value="北京"/>
<property name="buildTime" value="1998"/>
</bean>
<bean id="mystudent1" class="cfeng.entity.Student"> 按照位置进行赋值很不灵活,位置发生变化就得修改
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="张三"/>
<constructor-arg index="2" value="HC2003"/>
<constructor-arg index="3" ref="myschool"/>
</bean>
<bean id="mystudent1" class="cfeng.entity.Student">
<constructor-arg name="stuno" value="1"/> 识别是constructor-arg执行构造方法,个数就会调用对应个数的构造方法
<constructor-arg name="stuname" value="张三"/>
<constructor-arg name="stuclass" value="HC2003"/>
<constructor-arg name="school" ref="myschool"/>
</bean>
这种方式就必须要创建对应得参数的构造方法,如果没有就会报错,Spring识别标签的个数和值,执行对应的构造方法,将vlaue或者ref的值传入
有参构造方法
Student{stuno=1, stuname='张三', stuclass='HC2003', school=School{address='北京', buildTime=1998}}
所以底层就是去调用对应的有参构造方法,而ser注入就是调用无参构造和set方法
Spring的配置文件的对象不需要关心顺序,如果第一遍没有找到ref的所在,会进行二次扫描
Spring中一个bean标签只对应一个对象;一一对应的;容器对象ApplicationContext — 一个配置文件— 一个项目;所以就是Application;容器完成对象之间关系的管理【属性赋值】注入
Spring应该管理哪些类型的对象?
- 常用的对象应该放在容器中: dao类、service类、controller类、工具类 ---- Spring默认是单例的对象,一个id对应一个对象
- 实体类对象 【实体类对象的数据来自数据库】 、servlet、listener、filter类【Tomcat容器负责】 不应该放到Spring容器中
所以非持久化类型的对象,不被Tomcat管理的对象都可以放到Spring容器中🚜