<dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
public class HelloWorld {
public void sayHello(){
System.out.println("hello,spring");
}
}
<!--
bean:配置一个bean对象,将对象交给IOC容器管理
属性:
id:bean的唯一标识,不能重复
class:设置bean对象所对应的类型
-->
<bean id="helloworld" class="com.gao.spring.pojo.HelloWorld"></bean>
测试:
@Test
public void test(){
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applcationContext.xml");
//获取IOC容器中的bean
HelloWorld helloworld = (HelloWorld) ioc.getBean("helloworld");
helloworld.sayHello();
}
Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要无参构造器时,没有无参构造器,则会抛出下面的异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'helloworld' defined in class path resource [applicationContext.xml]: Instantiation of bean
failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed
to instantiate [com.atguigu.spring.bean.HelloWorld]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: com.atguigu.spring.bean.HelloWorld.<init>
()
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。上个实验中我们使用的就是这种方式。
@Test
public void testIOC(){
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
//1.获取bean
// Student studentOne = (Student) ioc.getBean("studentOne");
// System.out.println(studentOne);
// 2.获取bean
Student bean = ioc.getBean(Student.class);
System.out.println(bean);
}
如果有两个及以上会报错:
<bean id="studentOne" class="com.gao.spring.pojo.Student"></bean>
<bean id="studentTwo" class="com.gao.spring.pojo.Student"></bean>
报错:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.gao.spring.pojo.Student' available: expected single matching bean but found 2: studentOne,studentTwo
@Test
public void testIOC(){
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
//1.获取bean
// Student studentOne = (Student) ioc.getBean("studentOne");
// System.out.println(studentOne);
// 2.获取bean
// Student bean = ioc.getBean(Student.class);
// System.out.println(bean);
// 3.根据id和bean
Student studentOne = ioc.getBean("studentOne", Student.class);
System.out.println(studentOne);
}
当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
当IOC容器中一共配置了两个:
<bean id="studentOne" class="com.gao.spring.pojo.Student"></bean>
<bean id="studentTwo" class="com.gao.spring.pojo.Student"></bean>
根据类型获取时会抛出异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean
of type 'com.atguigu.spring.bean.HelloWorld' available: expected single matching bean but
found 2: helloworldOne,helloworldTwo
如果组件类实现了接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一
如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
//获取bean的三种方式
// 1.根据id获取
// 2.根据bean类型获取(常用ss)
// 注意:根据类型获取bean时候,要求IOC容器中有且只有一个类型匹配的bean
// 若有多个类型匹配:NoUniqueBeanDefinitionException
// 若没有匹配的类型:NoSuchBeanDefinitionException
// 3.根据bean的id和类型
public class Student implements Person{
private Integer sid;
private String sname;
private Integer age;
private String gender;
public Student(Integer sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public Student() {
}
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="studentTwo" class="com.gao.spring.pojo.Student">
<!--
property:通过成员变量的set方法进行赋值
name:设置需要赋值的属性名(和set方法有关)
value:设置为属性赋值
-->
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
</bean>
@Test
public void testDI(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student studentTwo = ioc.getBean("studentTwo", Student.class);
System.out.println(studentTwo);
}
public Student(Integer sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
<bean id="studentThree" class="com.gao.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="24"></constructor-arg>
<constructor-arg value="男"></constructor-arg>
</bean>
注意:
constructor-arg标签还有两个属性可以进一步描述构造器参数:
index属性:指定参数所在位置的索引(从0开始)
name属性:指定参数名
@Test
public void testDI(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
// Student studentTwo = ioc.getBean("studentTwo", Student.class);
// System.out.println(studentTwo);
Student bean = ioc.getBean("studentThree", Student.class);
System.out.println(bean);
}
如果有两个构造器:
public Student(Integer sid, String sname, String gender,Integer age) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public Student(Integer sid, String sname, String gender, Double score) {
this.sid = sid;
this.sname = sname;
this.gender = gender;
this.score = score;
}
<bean id="studentThree" class="com.gao.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="男"></constructor-arg>
<constructor-arg value="24"></constructor-arg>
</bean>
输结果:
Student{sid=1002, sname='李四', age=null, gender='男', score=24.0}
修改:
<bean id="studentThree" class="com.gao.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="男"></constructor-arg>
<constructor-arg value="24" name="age"></constructor-arg>
</bean>
输出结果:
Student{sid=1002, sname='李四', age=24, gender='男', score=null}
什么是字面量?
int a = 10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。
而如果a是带引号的:‘a’,那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
<property name="name">
<null />
</property>
注意:
以上写法,为name所赋的值是字符串null
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a < b"/>
<property name="expression">
<!-- 解决方案二:使用CDATA节 -->
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>
完整代码:
<bean id="studentFour" class="com.gao.spring.pojo.Student">
<property name="sid" value="1003"></property>
<!--
<: <
>: >
CDATA节其中的内容会原样解析<![CDATA[····]]>
CDATA节是xml其中的一个是特殊的标签,不能写在一个属性中
-->
<!-- <property name="sname" value="<王五>"></property>-->
<property name="sname">
<!-- 快捷键:CD(大写) + 回车 -->
<value><![CDATA[<王五>]]></value>
</property>
<property name="gender">
<null></null>
</property>
</bean>
public class Clazz {
private Integer cid;
private String cname;
public Clazz(Integer cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public Clazz() {
}
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 + '\'' +
'}';
}
}
在Student类中添加以下代码:
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
配置Clazz类型的bean:
<bean id="clazzOne" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
</bean>
为Student中的clazz属性赋值:
<bean id="studentFive" class="com.gao.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵四"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<!--ref:引用IOC容器中的某个bean的id-->
<property name="clazz" ref="clazzOne"></property>
</bean>
错误演示:
<bean id="studentFive" class="com.gao.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵四"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<!--ref:引用IOC容器中的某个bean的id-->
<property name="clazz" value="clazzOne"></property>
</bean>
如果错把ref属性写成了value属性,会抛出异常: Caused by: java.lang.IllegalStateException:
Cannot convert value of type ‘java.lang.String’ to required type
‘com.atguigu.spring.bean.Clazz’ for property ‘clazz’: no matching editors or conversionstrategy found
意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值
<bean id="studentFive" class="com.gao.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵四"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<!--ref:引用IOC容器中的某个bean的id-->
<!-- <property name="clazz" ref="clazzOne"></property>-->
<!--级联的方式,要保证前为clazz属性赋值或者实例化-->
<!-- <property name="clazz.cid" value="1111"></property>-->
<!-- <property name="clazz.cname" value="远大前程班"></property>-->
<property name="clazz">
<!--内部bean,只能当前bean内部使用,不能直接通过IOC容器获取-->
<bean id="clazzInner" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
</bean>
</property>
</bean>
测试
public void testDI(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
// Student studentTwo = ioc.getBean("studentTwo", Student.class);
// System.out.println(studentTwo);
// Student bean = ioc.getBean("studentFive", Student.class);
// System.out.println(bean);
Clazz clazzInner = ioc.getBean("clazzInner", Clazz.class);
System.out.println(clazzInner);
}
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'clazzInner' available
<bean id="studentFive" class="com.gao.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵四"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<!--ref:引用IOC容器中的某个bean的id-->
<property name="clazz" ref="clazzOne"></property>
<property name="clazz.cid" value="1111"></property>
<property name="clazz.cname" value="远大前程班"></property>
</bean>
student类中添加属性:
private String[] hobby;
<bean id="studentFive" class="com.gao.spring.pojo.Student">
<property name="sid" value="1004"></property>
<property name="sname" value="赵四"></property>
<property name="age" value="18"></property>
<property name="gender" value="男"></property>
<!--ref:引用IOC容器中的某个bean的id-->
<!-- <property name="clazz" ref="clazzOne"></property>-->
<!--级联的方式,要保证前为clazz属性赋值或者实例化-->
<!-- <property name="clazz.cid" value="1111"></property>-->
<!-- <property name="clazz.cname" value="远大前程班"></property>-->
<property name="clazz">
<!--内部bean,只能当前bean内部使用,不能直接通过IOC容器获取-->
<bean id="clazzInner" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
</bean>
</property>
<property name="hobby">
<array>
<!--<ref bean="````"></ref>-->
<value>羽毛球</value>
<value>篮球</value>
<value>足球</value>
</array>
</property>
</bean>
在Clazz类中添加以下代码:重写tostring
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
配置bean:
<bean id="clazzOne" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</list>
</property>
</bean>
改进:
<bean id="clazzOne" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
<property name="students" ref="studentList"></property>
<!-- <property name="students">-->
<!-- <list>-->
<!-- <ref bean="studentOne"></ref>-->
<!-- <ref bean="studentTwo"></ref>-->
<!-- <ref bean="studentThree"></ref>-->
<!-- </list>-->
<!-- </property>-->
</bean>
<!--配置一个集合类型的bean,使用util约束-->
<util:list id="studentList">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:util=“http://www.springframework.org/schema/util”
若为Set集合类型属性赋值,只需要将其中的list标签改为set标签即可
创建教师类Teacher:
public class Teacher {
private Integer tid;
private String tname;
public Teacher(Integer tid, String tname) {
this.tid = tid;
this.tname = tname;
}
public Teacher() {
}
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 + '\'' +
'}';
}
}
student增加属性:重写toString
private Map<String,Teacher> teachermap;
public Map<String, Teacher> getTeachermap() {
return teachermap;
}
public void setTeachermap(Map<String, Teacher> teachermap) {
this.teachermap = teachermap;
}
配置bean:
<property name="teachermap">
<map>
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10089" value-ref="teacherTwo"></entry>
</map>
</property>
配置teacherbean:
<bean id="teacherOne" class="com.gao.spring.pojo.Teacher">
<property name="tid" value="10086"></property>
<property name="tname" value="高老师"></property>
</bean>
<bean id="teacherTwo" class="com.gao.spring.pojo.Teacher">
<property name="tid" value="10089"></property>
<property name="tname" value="王老师"></property>
</bean>
改进:
<util:map id="studentMap">
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10089" value-ref="teacherTwo"></entry>
</util:map>
<property name="teachermap" ref="studentMap"></property>
<bean id="clazzOne" class="com.gao.spring.pojo.Clazz">
<property name="cid" value="1111"></property>
<property name="cname" value="终极一班"></property>
<property name="students" ref="studentList"></property>
<!-- <property name="students">-->
<!-- <list>-->
<!-- <ref bean="studentOne"></ref>-->
<!-- <ref bean="studentTwo"></ref>-->
<!-- <ref bean="studentThree"></ref>-->
<!-- </list>-->
<!-- </property>-->
</bean>
<!--配置一个集合类型的bean,使用util约束-->
<util:list id="studentList">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
<property name="teachermap" ref="studentMap"></property>
<util:map id="studentMap">
<entry key="10086" value-ref="teacherOne"></entry>
<entry key="10089" value-ref="teacherTwo"></entry>
</util:map>
使用
util:list
、util:map
标签必须引入相应的命名空间,可以通过idea的提示功能选择
引入p命名空间后,可以通过以下方式为bean的各个属性赋值
xmlns:p="http://www.springframework.org/schema/p"
<bean id="studentSix" class="com.gao.spring.pojo.Student"
p:sid="1005" p:sname="小明" p:teachermap-ref="studentMap">
</bean>
不引用外部文件:(加入依赖)
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<!-- <property name="maxActive" value="8"></property>-->
</bean>
测试:
@Test
public void testDataSouerce() throws SQLException {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
System.out.println(dataSource.getConnection());
}
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
jdbc.properties
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"></context:property-placeholder>
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入jdbc.properties,之后可以通过${}的方式访问value-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
@Test
public void testDataSouerce() throws SQLException {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
System.out.println(dataSource.getConnection());
}
在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:
取值 | 含义 | ** 创建对象的时机** |
---|---|---|
singleton(默认) | 在IOC容器中,这个bean的对象始终为单实例 | IOC容器初始化时 |
prototype | 这个bean在IOC容器中有多个实例 | 获取bean时 |
如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值 | 含义 |
---|---|
request | 在一个请求范围内有效 |
session | 在一个会话范围内有效 |
<bean id="student" class="com.gao.spring.pojo.Student">
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
</bean>
@Test
public void testScope(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student bean = ioc.getBean(Student.class);
Student bean2 = ioc.getBean(Student.class);
System.out.println(bean == bean2);//true
}
单例改多例:
<bean id="student" class="com.gao.spring.pojo.Student" scope="prototype">
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
</bean>
<!--
scope:设置bean的作用域
scope="singleton|prototype"
singleton(单例):表示获取该bean所对应的对象是同一个
prototype(多例);表示获取该bean所对应的对象都不是同一个
-->
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
System.out.println("生命周期:1、创建对象(实例化)");
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
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;
}
public void initMethod(){
System.out.println("生命周期:3、初始化");
}
//生命周期:4、使用
public void destroyMethod(){
System.out.println("生命周期:5、销毁");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法
<bean id="user" class="com.gao.spring.pojo.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="id" value="4"></property>
<property name="username" value="admin"></property>
<property name="password" value="11111"></property>
<property name="age" value="22"></property>
</bean>
@Test
public void test(){
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ioc.getBean(User.class);
System.out.println(bean);
System.out.println("生命周期:4、通过IOC容器获取bean并使用");
ioc.close();
}
输出结果:
生命周期:1、创建对象(实例化)
生命周期:2、依赖注入
生命周期:3、初始化
User{id=4, username='admin', password='11111', age=22}
生命周期:4、通过IOC容器获取bean并使用
生命周期:5、销毁
@Test
public void test1(){
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
// User bean = ioc.getBean(User.class);
// System.out.println(bean);
// System.out.println("生命周期:4、通过IOC容器获取bean并使用");
// ioc.close();
}
输出:
生命周期:1、创建对象(实例化)
生命周期:2、依赖注入
生命周期:3、初始化
修改:
scope="prototype"
输出结果为空吗,没有执行操作。
@Test
public void test1(){
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ioc.getBean(User.class);
System.out.println(bean);
System.out.println("生命周期:4、通过IOC容器获取bean并使用");
ioc.close();
}
输出:(没有销毁)
生命周期:1、创建对象(实例化)
生命周期:2、依赖注入
生命周期:3、初始化
User{id=4, username='admin', password='11111', age=22}
生命周期:4、通过IOC容器获取bean并使用
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要
实现BeanPostProcessor接口
,且配置到IOC容器中
,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行
。
创建bean的后置处理器:
public class MyBeanPostProcesssor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之前执行
System.out.println("MyBeanPostProcesssor--->后置处理器postProcessBeforeInitialization");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//此方法在bean的生命周期初始化之后执行
System.out.println("MyBeanPostProcesssor--->后置处理器postProcessAfterInitialization");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
配置bean:
<bean id="mybeanpostprocessor" class="com.gao.spring.process.MyBeanPostProcesssor">
</bean>
测试
@Test
public void test1(){
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ioc.getBean(User.class);
System.out.println(bean);
System.out.println("生命周期:4、通过IOC容器获取bean并使用");
ioc.close();
}
输出结果:
生命周期:1、创建对象(实例化)
生命周期:2、依赖注入
MyBeanPostProcesssor--->后置处理器postProcessBeforeInitialization
生命周期:3、初始化
MyBeanPostProcesssor--->后置处理器postProcessAfterInitialization
User{id=4, username='admin', password='11111', age=22}
生命周期:4、通过IOC容器获取bean并使用
生命周期:5、销毁
/*
* 1.实例化
* 2.依赖注入
* 3.后置处理器的postProcessBeforeInitialization方法
* 4.初始化,需要通过bean的init-method属性来制定初始化的方法
* 5.后置处理器的postProcessAfterInitialization方法
* 6.使用
* 7.IOC容器关闭时销毁,需要通过beean的destory-method属性指定销毁的方法
*
*
* bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,
且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容
器中所有bean都会执行
* 注意:
* 若bean的作用域为单例的时候,生命周期的前三个步骤会在获取IOC容器时执行
* 若bean的作用域为多例的时候,生命周期的前三个步骤会在获取bean时执行
* */
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
/**
* Interface to be implemented by objects used within a {@link BeanFactory}
which
* are themselves factories for individual objects. If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
*
* NB: A bean that implements this interface cannot be used as a normal
bean.
* A FactoryBean is defined in a bean style, but the object exposed for bean
* references ({@link #getObject()}) is always the object that it creates.
*
*
FactoryBeans can support singletons and prototypes, and can either create
* objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
* interface allows for exposing more fine-grained behavioral metadata.
*
*
This interface is heavily used within the framework itself, for example
for
* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
* custom components as well; however, this is only common for infrastructure
code.
*
*
{@code FactoryBean} is a programmatic contract. Implementations are not
* supposed to rely on annotation-driven injection or other reflective
facilities.
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
the
* bootstrap process, even ahead of any post-processor setup. If you need access
to
* other beans, implement {@link BeanFactoryAware} and obtain them
programmatically.
*
*
The container is only responsible for managing the lifecycle of the
FactoryBean
* instance, not the lifecycle of the objects created by the FactoryBean.
Therefore,
* a destroy method on an exposed bean object (such as {@link
java.io.Closeable#close()}
* will not be called automatically. Instead, a FactoryBean should
implement
* {@link DisposableBean} and delegate any such close call to the underlying
object.
*
*
Finally, FactoryBean objects participate in the containing BeanFactory's
* synchronization of bean creation. There is usually no need for internal
* synchronization other than for purposes of lazy initialization within the
* FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 08.03.2003
* @param the bean type
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public interface FactoryBean<T> {
/**
* The name of an attribute that can be
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
* factory beans can signal their object type when it can't be deduced from
* the factory bean class.
* @since 5.2
*/
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
*
If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
*
As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
@Nullable
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
* This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
*
In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* * For prototypes, returning a meaningful type here is advisable too.
*
This method can be called before this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
*
NOTE: Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
@Nullable
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* NOTE: If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
*
The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
*
NOTE: This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
*
The default implementation returns {@code true}, since a
* {@code FactoryBean} typically manages a singleton instance.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
default boolean isSingleton() {
return true;
}
}
/*
* FactoryBean 是一个接口,需要创建一个类实现接口
* 其中有三个方法:
* getObject() :提供一个对象交个ioc管理
* getObjectType():设置所提供的对象的类型
* isSingleon() :所提供的的对象是否单例
*
* 当把FactoryBean的实现类裴志伟bean的时候,会将当前类中的getObject()返回的对象交给ioc容器管理
* */
public class UserFActoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
<bean id="factory" class="com.gao.spring.factory.UserFActoryBean">
</bean>
@Test
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
User bean = ioc.getBean(User.class);
System.out.println(bean);
}
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值
创建类UserController
public class UserController {
private UserService 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 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
<bean id="userController" class="com.gao.spring.controller.UserController">
<property name="userService" ref="userService"></property>
</bean>
<bean id="userService" class="com.gao.spring.service.impl.UserServiceImpl">
<property name="userDao" ref="userDap"></property>
</bean>
<bean id="userDap" class="com.gao.spring.dao.impl.UserDaoImpl"></bean>
测试
@Test
public void testAutowire(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire-xml.xml");
UserController bean = ioc.getBean(UserController.class);
bean.saveUser();
}
<bean id="userController" class="com.gao.spring.controller.UserController" autowire="byType">
<!-- <property name="userService" ref="userService"></property>-->
</bean>
<bean id="userService" class="com.gao.spring.service.impl.UserServiceImpl" autowire="byType">
<!-- <property name="userDao" ref="userDap"></property>-->
</bean>
<bean id="userDap" class="com.gao.spring.dao.impl.UserDaoImpl"></bean>
总结:
/*
* 自动装配:
* 根据指定的策略,在ioc容器中匹配某个bean,自动为bean中的类类型的属性或接口类型的属性赋值
* 可以通过bean标签中的autowire属性设置自动转配的策略
* 自动装配的策略:
* 1、no,default:表示不装配,即bean中的属性不会自动匹配某个bean为属性赋值,此时属性使用默认值
* 2、byType:根据需要赋值的属性的类型,在ioc容器中去匹配某个bean,为属性赋值
*
* 注意:
* 1.若通过类型没有找到任何类型匹配的bean,此时不装配,属性使用默认值
* 2.若通过类型找到多个类型匹配的bean,异常NoUniqueBeanDefinitionException
* 总结:当使用byType实现自动装配的时候,ioc容器有且只有一个类型匹配的bean能够为属性赋值
*
*
* 3、byName:将要赋值的属性的属性名作为bean的id在ioc容器中匹配某个bean,为属性赋值
* 总结:当类型匹配的bean有多个时候,此时可以使用byName实现自动装配
* */