
普通容器只能用来存储,没有更多功能。
政府管理我们的一生,生老病死都和政府有关。
Servlet 容器能够管理 Servlet、Filter、Listener 这样的组件的一生,所以它是一个复杂容器。我们即将要学习的 IOC 容器也是一个复杂容器。它们不仅要负责创建组件的对象、存储组件的对象,还要负责调用组件的方法让它们工作,最终在特定情况下销毁组件。
[1]Servlet生命周期
| 名称 | 时机 | 次数 |
|---|---|---|
| 创建对象 | 默认情况:接收到第一次请求 修改启动顺序后:Web应用启动过程中 | 一次 |
| 初始化操作 | 创建对象之后 | 一次 |
| 处理请求 | 接收到请求 | 多次 |
| 销毁操作 | Web应用卸载之前 | 一次 |
[2] Filter生命周期
| 生命周期阶段 | 执行时机 | 执行次数 |
|---|---|---|
| 创建对象 | Web应用启动时 | 一次 |
| 初始化 | 创建对象后 | 一次 |
| 拦截请求 | 接收到匹配的请求 | 多次 |
| 销毁 | Web应用卸载前 | 一次 |
IOC:Inversion of Control,翻译过来是反转控制。
自己做饭:买菜、洗菜、择菜、改刀、炒菜,全过程参与,费时费力,必须清楚了解资源创建整个过程中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
点外卖:下单、等、吃,省时省力,不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
DI:Dependency Injection,翻译过来是依赖注入。
DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。
Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:
这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
以后在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关。
| 类型名 | 简介 |
|---|---|
| ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
| FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
| ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。 |
| WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
1)目标
由 Spring 的 IOC 容器创建类的对象。
2)思路
略
- <dependencies>
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.3.1version>
- dependency>
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.12version>
- <scope>testscope>
- dependency>
- dependencies>
- public class HelloWorld {
- public void sayHello() {
- System.out.println("hello world");
- }
- }
- "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="hello" class="com.chenyixin.ssm.HelloWorld"/>
- beans>
- public class HelloWorldTest {
-
- @Test
- public void sayHello() {
- // 从配置文件中获取 IOC 容器对象
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
-
- // 从 IOC 容器对象中获取bean,也就是组件对象
- HelloWorld hello = (HelloWorld) context.getBean("hello");
-
- hello.sayHello();
- // hello world
- }
- }
Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要无参构造器时,没有无参构造器,则会抛出下面的异常:
所以对一个JavaBean来说,无参构造器和属性的getXxx()、setXxx()方法是必须存在的,特别是在框架中。
在Spring环境下能够享受到的所有福利,都必须通过 IOC 容器附加到组件类上,所以随着我们在 Spring 中学习的功能越来越多,IOC 容器创建的组件类的对象就会比自己 new 的对象强大的越来越多。
-
- public class Student {
- private Integer sid;
- private String sname;
- private Integer age;
- private String gender;
-
- public Student() {
- }
-
- public Student(Integer sid, String sname, Integer age, String gender) {
- this.sid = sid;
- this.sname = sname;
- this.age = age;
- this.gender = gender;
- }
-
- public Integer getSid() {
- return sid;
- }
-
- public void setSid(Integer sid) {
- this.sid = sid;
- }
-
- public String getSname() {
- return sname;
- }
-
- public void setSname(String sname) {
- this.sname = sname;
- }
-
- public Integer getAge() {
- return age;
- }
-
- public void setAge(Integer age) {
- this.age = age;
- }
-
- public String getGender() {
- return gender;
- }
-
- public void setGender(String gender) {
- this.gender = gender;
- }
-
- @Override
- public String toString() {
- return "Student{" +
- "sid=" + sid +
- ", sname='" + sname + '\'' +
- ", age=" + age +
- ", gender='" + gender + '\'' +
- '}';
- }
- }
<bean id="studentOne" class="com.chenyixin.ssm.pojo.Student"/>
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。上个实验中我们使用的就是这种方式。
- // 获取 ioc 容器
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- // 获取 bean 对象
- // 1、根据id获取bean
- Student studentOne1 = (Student) ioc.getBean("studentOne");
- System.out.println(studentOne1);
- // Student{sid=null, sname='null', age=null, gender='null'}
- // 2、根据类型获取 bean
- Student studentOne2 = ioc.getBean(Student.class);
- System.out.println(studentOne2);
- // Student{sid=null, sname='null', age=null, gender='null'}
-
"studentOne" class="com.chenyixin.ssm.pojo.Student"/> - <bean id="studentTwo" class="com.chenyixin.ssm.pojo.Student"/>
当IOC容器中一个都没有配置
根据类型获取时会抛出异常:
如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
如果组件类实现了接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一
代码示例:
前提步骤:创建接口 Person ,并使Student 实现接口
Person person1 = ioc.getBean(Person.class); System.out.println(person1); // Student{sid=null, sname='null', age=null, gender='null'} Person person2 = ioc.getBean(Student.class); System.out.println(person2); // Student{sid=null, sname='null', age=null, gender='null'}
结论
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。即通过bean的类型、bean所继承的类的类型、bean所实现的接口的类型都可以获取bean
- // 3、根据 id 和类型获取 bean
- Student studentOne3 = ioc.getBean("studentOne", Student.class);
- System.out.println(studentOne3);
- // Student{sid=null, sname='null', age=null, gender='null'}
通过property标签配置的属性值会通过setXxx()方法注入
- <bean id="studentOne" class="com.chenyixin.ssm.pojo.Student"/>
- <bean id="studentTwo" class="com.chenyixin.ssm.pojo.Student">
-
- <property name="sid" value="1001"/>
- <property name="sname" value="张三"/>
- <property name="age" value="23"/>
- <property name="gender" value="女"/>
- bean>
- @Test
- public void testDI() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentTwo = ioc.getBean("studentTwo", Student.class);
- System.out.println(studentTwo);
- // Student{sid=1001, sname='张三', age=23, gender='女'}
- }
① 配置bean时为属性赋值
- <bean id="studentThree" class="com.chenyixin.ssm.pojo.Student">
- <constructor-arg value="1002"/>
- <constructor-arg value="李四"/>
- <constructor-arg value="22"/>
- <constructor-arg value="男"/>
- bean>
constructor-arg标签还有两个属性可以进一步描述构造器参数:
- index属性:指定参数所在位置的索引(从0开始)
- name属性:指定参数名
② 测试:
- @Test
- public void testDI2() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentThree = ioc.getBean("studentThree", Student.class);
- System.out.println(studentThree);
- // Student{sid=1002, sname='李四', age=22, gender='男'}
- }
什么是字面量?int a = 10;声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。而如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。
- <property name="sid" value="1001"/>
- <bean id="studentFour" class="com.chenyixin.ssm.pojo.Student">
-
- <property name="sid" value="1001"/>
- <property name="sname" value="张三"/>
- <property name="age">
-
- <null/>
- property>
- <property name="gender" value="女"/>
- bean>
注意:<property name="name" value="null">property>以上写法,为name所赋的值是字符串null
-
-
-
- <property name="sname" value="<张三>"/>
-
-
-
-
- <property name="gender">
- <value>>]]>value>
- property>
<: <;
>:>
CDATA节:
CDATA节的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据
XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析
所以CDATA节中写什么符号都随意
注意:CDATA 节是 xml 中一个特殊的标签,因此不能写在一个属性中
spring-ioc.xml配置文件:
- <bean id="studentFour" class="com.chenyixin.ssm.pojo.Student">
-
- <property name="sid" value="1001"/>
-
-
-
- <property name="sname" value="<张三>"/>
- <property name="age">
- <null/>
- property>
-
-
-
-
- <property name="gender">
- <value>>]]>value>
- property>
- bean>
测试方法:
- @Test
- public void testDI3() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentFour = ioc.getBean("studentFour", Student.class);
- System.out.println(studentFour);
- // Student{sid=1001, sname='<张三>', age=null, gender='<<女>>'}
- }
- public class Clazz {
- private Integer cid;
- private String cname;
-
- public Clazz() {
- }
-
- public Clazz(Integer cid, String cname) {
- this.cid = cid;
- this.cname = cname;
- }
-
- public Integer getCid() {
- return cid;
- }
-
- public void setCid(Integer cid) {
- this.cid = cid;
- }
-
- public String getCname() {
- return cname;
- }
-
- public void setCname(String cname) {
- this.cname = cname;
- }
-
- @Override
- public String toString() {
- return "Clazz{" +
- "cid=" + cid +
- ", cname='" + cname + '\'' +
- '}';
- }
- }
- private Clazz clazz;
-
- public Clazz getClazz() {
- return clazz;
- }
-
- public void setClazz(Clazz clazz) {
- this.clazz = clazz;
- }
-
- @Override
- public String toString() {
- return "Student{" +
- "sid=" + sid +
- ", sname='" + sname + '\'' +
- ", age=" + age +
- ", gender='" + gender + '\'' +
- ", clazz=" + clazz +
- '}';
- }
- <bean id="clazzOne" class="com.chenyixin.ssm.pojo.Clazz">
- <property name="cid" value="1111"/>
- <property name="cname" value="最强王者班"/>
- bean>
- <bean id="studentFive" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1003"/>
- <property name="sname" value="王五"/>
- <property name="age" value="25"/>
- <property name="gender" value="男"/>
-
- <property name="clazzOne" ref="clazz"/>
- bean>
测试:
- @Test
- public void testDI4() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentFive = ioc.getBean("studentFive", Student.class);
- System.out.println(studentFive);
- // Student{sid=1003, sname='王五', age=25, gender='男', clazz=Clazz{cid=1111, cname='最强王者班'}}
- }
-
"studentFive" class="com.chenyixin.ssm.pojo.Student"> -
"sid" value="1003"/> -
"sname" value="王五"/> -
"age" value="25"/> -
"gender" value="男"/> -
-
"clazz" value="clazzOne"/>
如果错把 ref 属性写成了 value 属性,会 抛出异常 :![]()
意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个
属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值
- <bean id="studentSix" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1004"/>
- <property name="sname" value="赵六"/>
- <property name="age" value="21"/>
- <property name="gender" value="男"/>
- <property name="clazz">
-
-
- <bean id="clazzTwo" class="com.chenyixin.ssm.pojo.Clazz">
- <property name="cid" value="2222"/>
- <property name="cname" value="远大前程班"/>
- bean>
- property>
- bean>
测试:
- @Test
- public void testDI5() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentSix = ioc.getBean("studentSix", Student.class);
- System.out.println(studentSix);
- // Student{sid=1004, sname='赵六', age=21, gender='男', clazz=Clazz{cid=2222, cname='远大前程班'}}
- }
错误演示:
- @Test
- public void testClazzTwo() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Clazz clazzTwo = ioc.getBean("clazzTwo", Clazz.class);
- System.out.println(clazzTwo);
- }
内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,否者抛出异常
- <bean id="studentSeven" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1003"/>
- <property name="sname" value="王五"/>
- <property name="age" value="25"/>
- <property name="gender" value="男"/>
-
- <property name="clazz" ref="clazzOne"/>
- <property name="clazz.cid" value="3333"/>
- <property name="clazz.cname" value="荣耀王者班"/>
- bean>
测试:
- @Test
- public void testDI6() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentSeven = ioc.getBean("studentSeven", Student.class);
- System.out.println(studentSeven);
- // Student{sid=1003, sname='王五', age=25, gender='男', clazz=Clazz{cid=3333, cname='荣耀王者班'}}
- }
错误演示:
- <bean id="studentSeven" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1003"/>
- <property name="sname" value="王五"/>
- <property name="age" value="25"/>
- <property name="gender" value="男"/>
-
- <property name="clazz.cid" value="3333"/>
- <property name="clazz.cname" value="荣耀王者班"/>
- bean>
若没有提前给 clazz 属性赋值或者实例化 ,会抛出异常:
- private String[] hobbies;
-
- public String[] getHobbies() {
- return hobbies;
- }
-
- public void setHobbies(String[] hobbies) {
- this.hobbies = hobbies;
- }
-
- @Override
- public String toString() {
- return "Student{" +
- "sid=" + sid +
- ", sname='" + sname + '\'' +
- ", age=" + age +
- ", gender='" + gender + '\'' +
- ", hobbies=" + Arrays.toString(hobbies) +
- ", clazz=" + clazz +
- '}';
- }
- <bean id="studentEight" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1003"/>
- <property name="sname" value="王五"/>
- <property name="age" value="25"/>
- <property name="gender" value="男"/>
- <property name="hobbies" >
- <array>
-
-
- <value>抽烟value>
- <value>喝酒value>
- <value>烫头value>
- array>
- property>
- <property name="clazz" ref="clazzOne"/>
- bean>
- @Test
- public void testDI7() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentEight = ioc.getBean("studentEight", Student.class);
- System.out.println(studentEight);
- // Student{sid=1003, sname='王五', age=25, gender='男',
- // hobbies=[抽烟, 喝酒, 烫头], clazz=Clazz{cid=3333, cname='荣耀王者班'}}
-
- }
在Clazz类中 添加或修改 以下代码:
- private List
students; -
- public List
getStudents() { - return students;
- }
-
- public void setStudents(List
students) { - this.students = students;
- }
-
- @Override
- public String toString() {
- return "Clazz{" +
- "cid=" + cid +
- ", cname='" + cname + '\'' +
- ", students=" + students +
- '}';
- }
配置bean:
- <bean id="clazzThree" class="com.chenyixin.ssm.pojo.Clazz">
- <property name="cid" value="4444"/>
- <property name="cname" value="非同一班"/>
- <property name="students">
- <list>
- <ref bean="studentTwo"/>
- <ref bean="studentThree"/>
- <ref bean="studentFive"/>
- list>
- property>
- bean>
测试:
- @Test
- public void testDI8() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Clazz clazzThree = ioc.getBean("clazzThree", Clazz.class);
- System.out.println(clazzThree);
- }
结果:

若为Set集合类型属性赋值,只需要将其中的list标签改为set标签即可
配置bean:
- <bean id="clazzFour" class="com.chenyixin.ssm.pojo.Clazz">
- <property name="cid" value="5555"/>
- <property name="cname" value="非同二班"/>
- <property name="students" ref="studentList"/>
- bean>
-
-
- <util:list id="studentList">
- <ref bean="studentTwo"/>
- <ref bean="studentThree"/>
- util:list>
测试:
- @Test
- public void testDI9() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Clazz clazzFour = ioc.getBean("clazzFour", Clazz.class);
- System.out.println(clazzFour);
- // Clazz{cid=5555, cname='非同二班', students=
- // [Student{sid=1001, sname='张三', age=23, gender='女', hobbies=null, clazz=null},
- // Student{sid=1002, sname='李四', age=22, gender='男', hobbies=null, clazz=null}]}
- }
- public class Teacher {
- private Integer tid;
- private String tname;
-
- public Teacher() {
- }
-
- public Teacher(Integer tid, String tname) {
- this.tid = tid;
- this.tname = tname;
- }
-
- public Integer getTid() {
- return tid;
- }
-
- public void setTid(Integer tid) {
- this.tid = tid;
- }
-
- public String getTname() {
- return tname;
- }
-
- public void setTname(String tname) {
- this.tname = tname;
- }
-
- @Override
- public String toString() {
- return "Teacher{" +
- "tid=" + tid +
- ", tname='" + tname + '\'' +
- '}';
- }
- }
- private Map
teacherMap; -
- public Map
getTeacherMap() { - return teacherMap;
- }
-
- public void setTeacherMap(Map
teacherMap) { - this.teacherMap = teacherMap;
- }
-
- @Override
- public String toString() {
- return "Student{" +
- "sid=" + sid +
- ", sname='" + sname + '\'' +
- ", age=" + age +
- ", gender='" + gender + '\'' +
- ", hobbies=" + Arrays.toString(hobbies) +
- ", teacherMap=" + teacherMap +
- ", clazz=" + clazz +
- '}';
- }
- <bean id="teacherOne" class="com.chenyixin.ssm.pojo.Teacher">
- <property name="tid" value="101"/>
- <property name="tname" value="王老"/>
- bean>
- <bean id="teacherTwo" class="com.chenyixin.ssm.pojo.Teacher">
- <property name="tid" value="102"/>
- <property name="tname" value="刘老"/>
- bean>
-
- <bean id="studentNine" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1003"/>
- <property name="sname" value="王五"/>
- <property name="age" value="25"/>
- <property name="gender" value="男"/>
- <property name="hobbies" >
- <array>
- <value>篮球value>
- <value>足球value>
- <value>乒乓球value>
- array>
- property>
- <property name="teacherMap">
- <map>
- <entry key="101" value-ref="teacherOne"/>
- <entry key="102" value-ref="teacherTwo"/>
- map>
- property>
- <property name="clazz" ref="clazzOne"/>
- bean>
测试:
- @Test
- public void testDI10() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentNine = ioc.getBean("studentNine", Student.class);
- System.out.println(studentNine);
- // Student{sid=1003, sname='王五', age=25, gender='男',
- // hobbies=[篮球, 足球, 乒乓球],
- // teacherMap={101=Teacher{tid=101, tname='王老'}, 102=Teacher{tid=102, tname='刘老'}},
- // clazz=Clazz{cid=3333, cname='荣耀王者班', students=null}}
- }
配置bean:
- <bean id="studentTen" class="com.chenyixin.ssm.pojo.Student">
- <property name="sid" value="1005"/>
- <property name="sname" value="田七"/>
- <property name="age" value="35"/>
- <property name="gender" value="男"/>
- <property name="hobbies" >
- <array>
- <value>篮球value>
- <value>足球value>
- <value>乒乓球value>
- array>
- property>
- <property name="teacherMap" ref="studentMap"/>
- <property name="clazz" ref="clazzOne"/>
- bean>
-
- <util:map id="studentMap">
- <entry key="101" value-ref="teacherOne"/>
- <entry key="102" value-ref="teacherTwo"/>
- util:map>
测试:
- @Test
- public void testDI11() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentTen = ioc.getBean("studentTen", Student.class);
- System.out.println(studentTen);
- // Student{sid=1005, sname='田七', age=35, gender='男', hobbies=[篮球, 足球, 乒乓球],
- // teacherMap={101=Teacher{tid=101, tname='王老'}, 102=Teacher{tid=102, tname='刘老'}},
- // clazz=Clazz{cid=3333, cname='荣耀王者班', students=null}}
- }
引入p命名空间:在标签中写入p: + Alt + Enter ,IDEA会提示所需导入名称空间 + Enter
- <bean id="studentEleven" class="com.chenyixin.ssm.pojo.Student"
- p:sid="1006" p:sname="小明" p:age="22" p:gender="未知" p:teacherMap-ref="studentMap"/>
测试:
- @Test
- public void testDI12() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
- Student studentEleven = ioc.getBean("studentEleven", Student.class);
- System.out.println(studentEleven);
- // Student{sid=1006, sname='小明', age=22, gender='未知', hobbies=null,
- // teacherMap={101=Teacher{tid=101, tname='王老'}, 102=Teacher{tid=102, tname='刘老'}},
- // clazz=null}
- }
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <version>8.0.16version>
- dependency>
-
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>druidartifactId>
- <version>1.0.31version>
- dependency>
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
- <property name="username" value="root"/>
- <property name="password" value="root"/>
- bean>
- @Test
- public void test() throws SQLException {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("dataSource.xml");
- DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
- System.out.println(dataSource.getConnection());
- // com.mysql.cj.jdbc.ConnectionImpl@1f1c7bf6
- }
- jdbc.driver=com.mysql.cj.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
- jdbc.username=root
- jdbc.password=root
-
- <context:property-placeholder location="jdbc.properties"/>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <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>
- @Test
- public void test() throws SQLException {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("dataSource.xml");
- DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
- System.out.println(dataSource.getConnection());
- // com.mysql.cj.jdbc.ConnectionImpl@1f1c7bf6
- }
在 Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围,各取值含义参加下表:
| 取值 | 含义 | 创建对象的时机 | 默认值 |
|---|---|---|---|
| singleton | 在 IOC 容器中,这个 bean 的对象始终为单实例(默认) | IOC 容器初始化时 | 是 |
| prototype | 这个 bean 在 IOC 容器中有多个实例 | 获取 bean 时 | 否 |
如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
| 取值 | 含义 |
|---|---|
| request | 在一个请求范围内有效 |
| session | 在一个会话范围内有效 |
- public class User {
- private Integer id;
- private String username;
- private String password;
- private Integer age;
-
- public User(Integer id, String username, String password, Integer age) {
- this.id = id;
- this.username = username;
- this.password = password;
- this.age = age;
- }
-
- public User() {
- }
-
- public Integer getId() {
- return id;
- }
-
- public void setId(Integer id) {
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public Integer getAge() {
- return age;
- }
-
- public void setAge(Integer age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", age=" + age +
- '}';
- }
- }
-
-
-
- <bean id="user1" class="com.chenyixin.ssm.pojo.User">
- <property name="id" value="1"/>
- <property name="username" value="张三"/>
- <property name="password" value="zhangsan"/>
- <property name="age" value="19"/>
- bean>
-
- <bean id="user2" class="com.chenyixin.ssm.pojo.User" scope="prototype">
- <property name="id" value="2"/>
- <property name="username" value="李四"/>
- <property name="password" value="lisi"/>
- <property name="age" value="21"/>
- bean>
- @Test
- public void test() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("User.xml");
- User user11 = ioc.getBean("user1", User.class);
- User user12 = ioc.getBean("user1", User.class);
- System.out.println(user11 == user12); // true
-
- User user21 = ioc.getBean("user2", User.class);
- User user22 = ioc.getBean("user2", User.class);
- System.out.println(user21 == user22); //false
- }
1、实例化 (无参数构造)2、依赖注入 (调用 set 方法)
3、初始化,需要通过 bean 的init-method 属性指定初始化的方法
4、调用 bean 对象
5、IOC容器关闭时销毁,需要通过 bean 的 deestroy-method 属性指定销毁的方法
- public class User {
- private Integer id;
- private String username;
- private String password;
- private Integer age;
-
- public User(Integer id, String username, String password, Integer age) {
- this.id = id;
- this.username = username;
- this.password = password;
- this.age = age;
- }
-
- public User() {
- System.out.println("生命周期1 实例化");
- }
-
- public Integer getId() {
- return id;
- }
-
- public void setId(Integer id) {
- System.out.println("生命周期2 依赖注入");
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public Integer getAge() {
- return age;
- }
-
- public void setAge(Integer age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", age=" + age +
- '}';
- }
-
- public void initMethod() {
- System.out.println("生命周期3 初始化");
- }
-
- public void destroyMethod() {
- System.out.println("生命周期5 销毁");
- }
- }
-
- <bean id="user" class="com.chenyixin.ssm.pojo.User"
- init-method="initMethod" destroy-method="destroyMethod">
- <property name="id" value="3"/>
- <property name="username" value="admin"/>
- <property name="password" value="123456"/>
- <property name="age" value="23"/>
- bean>
- public class LiftTest {
-
- @Test
- public void testLift() {
- // CofigurableApplicationContext 是 ApplicationContext 的子接口,其中扩展了刷新和关闭容器的方法
- ConfigurableApplicationContext ioc =
- new ClassPathXmlApplicationContext("spring-lift.xml");
- User user = ioc.getBean(User.class);
- System.out.println("生命周期4:" + user.toString());
- ioc.close();
- }
- }
结果:

① 若bean 的作用域 为单例是,生命周期的前三个步骤会在获取IOC容器时执行(饿汉式)
② 若 bean 的作用域为多例时,生命周期的前三个步骤会在获取 bean 时执行(懒汉式),并IOC容器的关闭不由 bean 对象操作
1、实例化 (无参数构造)2、依赖注入 (调用 set 方法)
3、bean对象初始化之前操作(由bean的后置处理器负责)
4、初始化,需要通过 bean 的init-method 属性指定初始化的方法
5、bean对象初始化之后操作(由bean的后置处理器负责)
6、调用 bean 对象
7、IOC容器关闭时销毁,需要通过 bean 的 deestroy-method 属性指定销毁的方法
1. 创建类,实现接口 BeanPostProcessor,创建后置处理器
2. 实现 BeanPostProcessor 中的 postProcessBeforeInitialization 方法 与 postProcessAfterInitialization 方法
3. 在配置文件中 创建该类的 bean标签,因为该类实现接口 BeanPostProcessor,使该类成为了后置处理器,该类的 bean标签会对其所在的xml配置文件中的所有bean标签的类添加后置处理器
1. 创建 后置处理器 类
- public class MyBeanProcessor implements BeanPostProcessor {
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("生命周期: MyBeanProcessor 中的 postProcessBeforeInitialization");
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("生命周期: MyBeanProcessor 中的 postProcessAfterInitialization方法");
- return bean;
- }
- }
2. 在xml配置文件中添加后置处理器
-
- <bean id="user" class="com.chenyixin.ssm.pojo.User"
- init-method="initMethod" destroy-method="destroyMethod">
- <property name="id" value="3"/>
- <property name="username" value="admin"/>
- <property name="password" value="123456"/>
- <property name="age" value="23"/>
- bean>
-
-
- <bean id="myBeanProcessor" class="com.chenyixin.ssm.MyBeanProcessor"/>
3、测试
- public void testLift() {
- // CofigurableApplicationContext 是 ApplicationContext 的子接口,其中扩展了刷新和关闭容器的方法
- ConfigurableApplicationContext ioc =
- new ClassPathXmlApplicationContext("spring-lift.xml");
- User user = ioc.getBean(User.class);
- System.out.println("生命周期4:" + user.toString());
- ioc.close();
- }
4、结果
FactoryBean是Spring 提供的一种整合第三方框架的常用机制。和普通的 bean 不同,配置一个 FactoryBean类型的 bean ,在获取 bean 的时候得到的并不是 class 属性中配置的这个类的对象,而是 getObject()方法的返回值。通过这种机制, Spring 可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
FactoryBean 是一个接口,需要创建一个类实现接口
其中有三个方法:
getObject():通过一个对象交给IOC容器
getOBjectType():设置所提供对象的类型
isSingleton():所提供的对象是否为单例
当 FactoryBean 的实现类配置为bean时,会将当前类中getObject()所返回的对象交给IOC容器管理
- public class UserFactoryBean implements FactoryBean
{ - @Override
- public User getObject() throws Exception {
- return new User(4,"王五","wangwu",20);
- }
-
- @Override
- public Class> getObjectType() {
- return User.class;
- }
-
- @Override
- public boolean isSingleton() {
- return FactoryBean.super.isSingleton();
- }
- }
<bean id="userFactoryBean" class="com.chenyixin.ssm.factory.UserFactoryBean"/>
- public class UserFactoryBeanTest {
-
- @Test
- public void test() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("FactoryBean.xml");
- User user = ioc.getBean(User.class);
- System.out.println(user);
- // User{id=4, username='王五', password='wangwu', age=20}
- }
-
- }
- public class UserController {
- private UserService userService;
-
- public UserService getUserService() {
- return userService;
- }
-
- public void setUserService(UserService userService) {
- this.userService = userService;
- }
-
- public void saveUser() {
- userService.saveUser();
- }
- }
创建接口UserService
- public interface UserService {
- void saveUser();
- }
创建类UserServiceImpl实现接口UserService
- public class UserServiceImpl implements UserService {
- private UserDao userDao;
-
- public UserDao getUserDao() {
- return userDao;
- }
-
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
-
- @Override
- public void saveUser() {
- userDao.saveUser();
- }
- }
创建接口UserDao
- public interface UserDao {
- void saveUser();
- }
创建类UserDaoImpl实现接口UserDao
- public class UserDaoImpl implements UserDao {
- @Override
- public void saveUser() {
- System.out.println("保存成功");
- }
- }
- <bean id="controller" class="com.chenyixin.ssm.controller.UserController">
- <property name="userService" ref="service"/>
- bean>
- <bean id="service" class="com.chenyixin.ssm.service.impl.UserServiceImpl">
- <property name="userDao" ref="dao"/>
- bean>
- <bean id="dao" class="com.chenyixin.ssm.dao.impl.UserDaoImpl"/>
自动装配:
根据指定的策略,在IOC容器中匹配某个bean,自动为bean中的类类型的属性或接口类型的属性赋值
可以通过bean 标签中的autowire属性设置自动装配的策略
1、no,default:表示不装配,即bean中的属性不会自动匹配某个bean为其属性赋值,此时属性使用默认值
2、byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值
注意:
a>若通过类型没有找到任何一个类型匹配的bean,此时不装配,属性使用默认值
b>若通过类型找到了多个类型匹配的bean,此时会抛出异常:NoUnqueBeanDefinitionException
总结:当使用 byType 实现自动装配时,IOC容器有且只有一个类型匹配的bean能够为属性赋值。
3、byName:将要赋值的属性的属性名作为bean 的 id 在IOC容器中匹配某个bean,为属性赋值
总结:当类类型的bean有点多个时,此时可以使用byName实现自动装配
- <bean id="userController" class="com.chenyixin.ssm.controller.UserController" autowire="byType"/>
- <bean id="userService" class="com.chenyixin.ssm.service.impl.UserServiceImpl" autowire="byName"/>
- <bean id="userDao" class="com.chenyixin.ssm.dao.impl.UserDaoImpl"/>
- @Test
- public void test() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
- UserController userController = ioc.getBean(UserController.class);
- userController.saveUser(); //保存成功
- }
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是 Java 代码来完成的,XML 和注解只是告诉框架中的 Java 代码如何执行。
举例:元旦联欢会要布置教室,蓝色的地方贴上元旦快乐四个字,红色的地方贴上拉花,黄色的地方贴上气球。 班长做了所有标记,同学们来完成具体工作。墙上的标记相当于我们在代码中使用的注解,后面同学们做的工作,相当于框架的具体操作。
![]()
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.3.1version>
- dependency>
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.12version>
- <scope>testscope>
@Component:将类标识为普通组件@Controller:将类标识为控制层组件@Service:将类标识为业务层组件@Repository:将类标识为持久层组件
四个典型注解没有本质区别

通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别,也就是语法层面没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
- @Controller
- public class UserController {
-
- }
- public interface UserSerice {
- }
创建业务层组件UserServiceImpl
- @Service
- public class UserServiceImpl implements UserSerice {
- }

<context:component-scan base-package="com.chenyixin.ssm"/>
- <context:component-scan base-package="com.chenyixin.ssm">
-
-
- <context:exclude-filter type="annotation"
- expression="org.springframework.stereotype.Controller"/>
-
- context:component-scan>
- <context:component-scan base-package="com.chenyixin.ssm" use-default-filters="false">
-
-
-
-
- <context:include-filter type="annotation"
- expression="org.springframework.stereotype.Controller"/>
-
- context:component-scan>
总结:
扫描组件
1、context:exclude-filter:排除扫描
type=" annotation | assignable "
annotation :根据注解的类型进行排除,expression需要设置排除的注解的全类名
assignable :根据类的类型进行排除,expression需要设置排除的类的全类名
2、context:include-filter:包含扫描
注意:需要在context: component-scan标签中设置use-default-filters 属性
use-default-filters="true”(默认),所设置的包下所有的类都需要扫描,此时可以使用排除扫描
use-default-filters="false",所设置的包下所有的类都不需要扫描,此时可以使用包含扫描
- public class IOCAnnotationTest {
-
- @Test
- public void test() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-annotation.xml");
- UserController userController = ioc.getBean(UserController.class);
- System.out.println(userController);
-
- UserSerice userSerice = ioc.getBean(UserSerice.class);
- System.out.println(userSerice);
-
- UserDao userDao = ioc.getBean(UserDao.class);
- System.out.println(userDao);
-
- }
- }
结果:

默认情况 : 类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。自定义bean的id可通过标识组件的注解的value属性设置自定义的bean的id例如:@Service(value="userService")//默认为userServiceImpl (value= 可以省略)public class UserServiceImpl implements UserService {}
参考基于xml的自动装配在UserController中声明UserService对象在UserServiceImpl中声明UserDao对象
@Autowired:实现自动装配功能的注解
1、@Autowired注解能够标识的位置
a>标识在成员变量上,此时不需要设置成员变量的set方法
b>标识在set方法上
c>标识在为当前成员变量赋值的有参构造上
2、@Autowired注解的原理
a> 默认通过byType的方式,在IOC容器中通过类型匹配某个bean为属性赋值
b> 若有多个类型匹配的bean,此时会自动转换为byName的方式实现自动装配的效果即将要赋值的属性的属性名作为bean 的id匹配某个bean为属性赋值
c> 若byType和byName的方式都无妨实现自动装配,即Ioc容器中有多个类型匹配的bean
且这些bean的id和要赋值的属性的属性名都不一致,此时抛异常:NouniqueBeanDefinitionException
d> 此时可以在要赋值的属性上,添加一个注解@Qualifier 通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值
注意:荐Toc容器中没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException
在@Autowired注解中有个属性required,默认值为true,要求必须完成自动装配
可以将required设置为false,此时能装配则装配,无法装配则使用属性的默认值
- @Controller
- public class UserController {
-
- @Autowired
- private UserService userService;
-
- // @Autowired
- // public UserController(UserService userService) {
- // this.userService = userService;
- // }
-
- // @Autowired
- // public void setUserService(UserService userService) {
- // this.userService = userService;
- // }
-
- public void saveUser() {
- userService.saveUser();
- }
- }
- public interface UserService {
- void saveUser();
- }
- @Service
- public class UserServiceImpl implements UserService {
- @Autowired
- private UserDao userDao;
- @Override
- public void saveUser() {
- userDao.saveUser();
- }
- }
- public interface UserDao {
- void saveUser();
- }
- @Repository
- public class UserDaoImpl implements UserDao {
- @Override
- public void saveUser() {
- System.out.println("保存成功");
- }
- }
- @Test
- public void test2() {
- ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-annotation.xml");
- UserController userController = ioc.getBean(UserController.class);
- userController.saveUser(); //保存成功
- }
- @Autowired
- @Qualifier(value="userServiceImpl")
- private UserService userService;
给 @Autowired 注解设置 required = false 属性表示:能装就装,装不上就不装。但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性。
- @Autowired(required = false)
- private UserDao userDao;
WARNING
如果类中同时存在装配属性的 setXxx() 方法会使 required = false 设定失效。
- @Configuration // 声明该类为 配置类
- @ComponentScan(basePackages = "com.chenyixin.ssm") // 注解扫描
- public class SpringConfig {
- }
- @Test
- public void test3() {
- // ClassPathXmlApplicationContext 根据 XM L配置文件创建 IOC 容器对象
- // AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象
- ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
- UserController userController = ioc.getBean(UserController.class);
- userController.saveUser(); //保存成功
- }
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-testartifactId>
- <version>5.3.1version>
- dependency>
- // junit的@RunWith注解:指定Spring为Junit提供的运行器
- @RunWith(SpringJUnit4ClassRunner.class)
-
- // Spring的@ContextConfiguration的value指定Spring配置文件的位置,需要以classpath:+文件路径
- // 如:value = {"classpath:spring-annotation.xml"}
- // Spring的@ContextConfiguration的classes指定Spring配置类的位置,如:classes = SpringConfig.class
- // @ContextConfiguration(value = {"classpath:spring-annotation.xml"})
- @ContextConfiguration(classes = SpringConfig.class)
- public class JunitIntegrationSpring {
-
- @Autowired
- private UserController userController;
- @Test
- public void testIntegration() {
- userController.saveUser();
- }
-
- }