目录
1、IOC
1.1、IOC容器
1.1.1、IOC思想
①获取资源的传统方式
②反转控制方式获取资源
③DI
1.1.2、IOC容器在Spring中的实现
①BeanFactory
②ApplicationContext
③ApplicationContext的主要实现类
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
ConfigurableApplicationContext
WebApplicationContext
1.2、基于XML管理bean
1.2.1、实验一
①创建Maven Module
②引入依赖
③创建类
④创建Spring的配置文件
⑤在Spring的配置文件中配置bean
⑥创建测试类测试
⑦思路
⑧注意
1.2.2、获取bean
①方式一:根据id获取
②方式二:根据类型获取
③方式三:根据id和类型
扩展
⑤结论
1.2.3、依赖注入之setter注入
①配置bean时为属性赋值
②测试
1.2.4、依赖注入之构造器注入
①在Student类中的有参构造
②配置bean
③测试
拓展
1.2.5、特殊值处理
①字面量赋值
②null值
③xml实体
④CDATA节
1.2.6、为类类型属性赋值
①创建班级类Clazz
②修改Student类
③方式一:引用外部已声明的bean
④方式二:级联属性赋值
⑤方式二:内部bean
1.2.7、为数组类型属性赋值
①修改Student类
②配置bean
1.2.8、为集合类型属性赋值
①为List集合类型属性赋值
②为Map集合类型属性赋值
1.2.9、p命名空间
1.2.10、引入外部属性文件
①加入依赖
②创建外部属性文件
③引入属性文件并配置bean
④测试
1.2.11、bean的作用域
①概念
单例(singleton)测试
多例(prototype )测试
1.2.12、bean的生命周期
③配置bean
④测试
⑤bean的后置处理器
1.2.13、FactoryBean
①简介
②创建类UserFactoryBean
③配置bean
④测试
1.2.14、基于xml的自动装配
①场景模拟(三层架构)
②配置bean(不用自动装配的方式)
③测试
④配置bean(自动装配的方式)
⑤测试
1.3、基于注解管理bean
1.3.1、标记(注解)与扫描
①注解
②扫描
③新建一个Maven Module并导入依赖
④创建Spring配置文件
⑤标识组件的常用注解
⑥创建组件
⑦扫描组件
⑨组件所对应的bean的id
1.3.2、实验二:基于注解的自动装配
①场景模拟
②@Autowired注解
@AutoWired注解能够标识的位置
③@Autowired工作流程
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。
快捷键双击shift
快捷键Ctrl+H
通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、 关闭和刷新上下文的能力。
专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对 象,并将对象引入存入 ServletContext 域中。
org.springframework
spring-context
5.3.24
junit
junit
4.12
public class hello {
public void sayHello(){
System.out.println("hello word!!");
}
}
public class helloTest {
@Test
public void test(){
//resources和java最终加载到同一个目录(类路径下)
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean.xml");
//获取IOC容器中的bean
hello hello = (com.pjp.spring6.bean.hello) ioc.getBean("hello");
hello.sayHello();
}
}
注释其中的无参构造方法
public class Student {
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// public Student() {
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要 无参构造器时,没有无参构造器,则会抛出下面的异常:
Caused by: java.lang.NoSuchMethodException: com.pjp.spring.Student.
Student.
在配置文件中也相应报错 在类“Student”中找不到匹配的构造函数
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。
ioc.getBean(String name)
Student studentOne = (Student) ioc.getBean("studentOne");
ioc.getBean(Class
Student student = ioc.getBean(Student.class);
注意
当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
当IOC容器中一共配置了两个:
根据类型获取时会抛出异常
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student student = ioc.getBean(Student.class);
System.out.println(student);
}
org.springframework.beans.factory.NoUniqueBeanDefinitionException(没有唯一Bean定义异常): No qualifying bean of type 'com.pjp.spring.Student' available: expected single matching bean but found 2: studentOne,studentTwo
ioc.getBean(String name,Class
Student studentOne = ioc.getBean("StudentOne", Student.class);
如果组件类实现了接口,根据接口类型可以获取 bean 前提是bean唯一
public class Student implements Person {
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Person person = ioc.getBean(Person.class);
System.out.println(person);
}
输出:Student{name='null', age=0, sex='null'}
如果一个接口有多个实现类,这些实现类都配置了 bean,不能根据接口类型获取 bean 因为bean不唯一
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
即通过bean的类型、bean所继承的类的类型、bean所实现的接口的类型都可以获取bean
就是属性复制
property标签:通过组件类的Set方法给主键对象设置属性
name属性:指定属性名(这个属性名是getter和setter方法定义的,和成员变量无关)
value属性:指定属性值
@Test
public void testSetter(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student studentTwo = ioc.getBean("studentTwo", Student.class);
System.out.println(studentTwo);
}
输出 Student{name='pjp', age=21, sex='男'}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
constructor-arg标签还有两个属性可以进一步描述构造器参数:
index属性:指定参数所在位置的索引(从0开始)
name属性:指定参数名
public void testConstructor(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student studentThree = ioc.getBean("studentThree", Student.class);
System.out.println(studentThree);
}
输出 Student{name='jack', age=24, sex='男'}
增加两个属性及其构造方法
private Integer id;
private double score;
public Student(String name, int age, String sex,double score) {
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
}
public Student(String name, int age, String sex,Integer id) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
配置bean
测试
public void testConstructor2(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student studentFour = ioc.getBean("studentFour", Student.class);
System.out.println(studentFour);
}
输出 Student{name='Tom', age=25, sex='男', id=10, score=0.0}
优先匹配了下面这个构造器
public Student(String name, int age, String sex, Integer id) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
指定sroce
输出
Student{name='Tom', age=25, sex='男', id=null, score=10.0}
则匹配下面这个构造器
public Student(String name, int age, String sex, double score) {
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
}
什么是字面量?
int a = 10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a 的时候,我们实际上拿到的值是10。
而如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。
注意
这种写法,为name赋值是字符串null
输出:Student{name='a < b', age=0, sex='null', id=null, score=0.0}
xml中转义字符
========>< 小于号转义
========>> 大于号转义
========>& &符号转义
========>' 单引号转义
========>" 双引号转义
输出:Student{name='a < b', age=0, sex='a
转义标签
快捷键 大写CD回车
public class Clazz {
private Integer cid;
private String name;
public Clazz(Integer cid, String name) {
this.cid = cid;
this.name = name;
}
public Clazz() {
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Clazz{" +
"cid=" + cid +
", name='" + name + '\'' +
'}';
}
}
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id=" + id +
", score=" + score +
", clazz=" + clazz +
'}';
}
配置Clazz类型的bean:
为Student中的clazz属性赋值:
ref属性:应用IOC容器中某个bean的id,将所对应的bean为属性赋值
测试后输出
Student{name='NoNo', age=19, sex='女', id=null, score=0.0, clazz=Clazz{cid=101, cname='java'}}
错误演示:
如果错把ref属性写成了value属性,会抛出异常: Cannot convert value of type 'java.lang.String' to required type 'com.pjp.spring.Clazz' for property 'clazz': no matching editors or conversion strategy found
意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个
属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值
在一个bean内部声明再声明一个bean
内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性
输出:Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python'}}
在Student类中添加以下代码:
private String[] hobby;
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id=" + id +
", score=" + score +
", clazz=" + clazz +
", hobby=" + Arrays.toString(hobby) +
'}';
}
游泳
健身
打球
输出:Student{name='赵六', age=18, sex='女', id=null, score=0.0, clazz=null, hobby=[游泳, 健身, 打球]}
在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:
输出:Clazz{cid=202, cname='web', students=[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null},
Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null},
Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null},
Student{name='a < b', age=0, sex='a
配置util约束
配置一个集合类型的bean,需要使用util约束
实际上创建list集合的属性的bean
输出:Clazz{cid=202, cname='web', students=[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null},
Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null},
Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null},
Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python', students=null}, hobby=null}]}
输出list集合
@Test
public void test3(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
ArrayList studentList = ioc.getBean("studentList", ArrayList.class);
System.out.println(studentList);
}
输出:[Student{name='pjp', age=21, sex='男', id=null, score=0.0, clazz=null, hobby=null}, Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null},
Student{name='Tom', age=25, sex='男', id=null, score=10.0, clazz=null, hobby=null}, Student{name='王五', age=24, sex='男', id=null, score=0.0, clazz=Clazz{cid=303, cname='python', students=null}, hobby=null}]
创建教师类Teacher:
public class Teacher {
private Integer Tid;
private String Tname;
public Teacher(Integer tid, String tname) {
Tid = tid;
Tname = tname;
}
public Teacher() {
}
public Integer getTid() {
return Tid;
}
public void setTid(Integer tid) {
Tid = tid;
}
public String getTname() {
return Tname;
}
public void setTname(String tname) {
Tname = tname;
}
@Override
public String toString() {
return "Teacher{" +
"Tid=" + Tid +
", Tname='" + Tname + '\'' +
'}';
}
}
在Student类中添加以下代码:
private Map teacherMap;
public Map getTeacherMap() {
return teacherMap;
}
public void setTeacherMap(Map teacherMap) {
this.teacherMap = teacherMap;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id=" + id +
", score=" + score +
", clazz=" + clazz +
", hobby=" + Arrays.toString(hobby) +
", teacherMap=" + teacherMap +
'}';
}
配置bean:
输出:Student{name='jack', age=24, sex='男', id=null, score=0.0, clazz=null, hobby=null, teacherMap={888=Teacher{Tid=888, Tname='LiMing'}, 777=Teacher{Tid=777, Tname='Wang'}}}
配置util约束(和List集合同理)
输出同上
引入p命名空间后,可以通过以下方式为bean的各个属性赋值
加入约束xmlns:p="http://www.springframework.org/schema/p"
输出:Student{name='田七', age=22, sex='null', id=null, score=0.0, clazz=null, hobby=null, teacherMap={888=Teacher{Tid=888, Tname='LiMing'}, 777=Teacher{Tid=777, Tname='Wang'}}}
mysql
mysql-connector-java
8.0.16
com.alibaba
druid
1.0.31
jdbc.user=root
jdbc.password=pjp
jdbc.url=jdbc:mysql://localhost:3306/db01?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
serverTimezone=UTC 避免时区问题
实际上是指出核数据库的时区为美国。因为我们的数据库的时区是美国的,而我们连接的时候用的是中国的北京时间,然后比美国早上8个小时,然后呢用的时候就必须指出我们当前用的时间是美国的时间,这样才能连接上数据库。
public void test() throws SQLException {
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
DruidDataSource dataSource = ioc.getBean(DruidDataSource.class);
System.out.println(dataSource.getConnection());
}
输出:七月 02, 2023 5:26:24 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info
信息: {dataSource-1} inited
com.mysql.cj.jdbc.ConnectionImpl@1d76aeea
连接成功!!
在Spring中可以通过配置bean标签的scope属性来指定bean的作用范围,各取值含义参考下表
取值 |
含义 |
创建对象的时机 |
singleton(默认) |
在IOC容器中,这个bean的对象始终为单实例 |
IOC容器初始化时 |
prototype |
这个bean在IOC容器中有多个实例 |
获取bean时 |
如果是在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值 |
含义 |
request |
在一个请求范围内有效 |
session |
在一个会话范围内有效 |
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student student1 = ioc.getBean(Student.class);
Student student2 = ioc.getBean(Student.class);
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
}
输出
265119009
265119009
结果返回对象的hash地址相同,说明是同一个对象
@Test
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student student1 = ioc.getBean(Student.class);
Student student2 = ioc.getBean(Student.class);
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
}
输出
1282811396
641853239
结果返回对象的hash地址不相同,说明不是同一个对象
①具体的生命周期过程
bean对象创建(调用无参构造器)
给bean对象设置属性
bean对象初始化之前操作(由bean的后置处理器负责)
bean对象初始化(需在配置bean时指定初始化方法)
bean对象初始化之后操作(由bean的后置处理器负责)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭
②创建类User并添加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、初始化");
}
public void destroyMethod(){
System.out.println("生命周期:5、销毁");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
public void test() {
//ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化
User{id=111, username='pjp', password='857857', age=21}
生命周期:4、销毁
注意:
若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行
public void test() {
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
// User user = ioc.getBean(User.class);
// System.out.println(user);
// ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化
若bean的作用域为多例时,声明周期的前三个步骤会在获取bean时执行(没有执行销毁方法)
public void test() {
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
// User user = ioc.getBean(User.class);
// System.out.println(user);
// ioc.close();
}
没有输出
public void test() {
// ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、初始化
User{id=111, username='pjp', password='857857', age=21}
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口, 且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行
创建bean的后置处理器:
public class MyBeanPostProcessor implements BeanPostProcessor {
// 快捷键Ctrl+O 重写方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 在bean的生命周期初始之前执行
System.out.println("后置处理器postProcessBeforeInitialization"+bean+beanName);
return bean;
//这个bean是此时IOC管理的bean,beanName为bean的id
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 在bean的生命周期初始化之后执行
System.out.println("后置处理器postProcessAfterInitialization"+bean+beanName);
return bean;
}
}
在IOC容器中配置后置处理器:
测试输出
public void test() {
ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
ioc.close();
}
输出
生命周期:1、创建对象
生命周期:2、依赖注入
后置处理器postProcessBeforeInitializationUser{id=111, username='pjp', password='857857', age=21}com.pjp.spring.POJO.User#0
生命周期:3、初始化
后置处理器postProcessAfterInitializationUser{id=111, username='pjp', password='857857', age=21}com.pjp.spring.POJO.User#0
User{id=111, username='pjp', password='857857', age=21}
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
其中还有一个方法 设置是否是单例
default boolean isSingleton() {
return true;
}
public class UserFactoryBean implements FactoryBean {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class> getObjectType() {
return User.class;
}
}
public void test() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml");
User user = ioc.getBean(User.class);
System.out.println(user);
}
输出
生命周期:1、创建对象
User{id=null, username='null', password='null', age=null}
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定bean中所依赖的类型或接口类型属性赋值
创建类UserController
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("保存成功!!!");
}
}
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser();
}
输出
保存成功!!!
使用bean标签的autowire属性设置自动装配效果
属性:
no,default:表示不装配,即bean中的属性不会自动匹配某个bean为属性赋值,此时属性使用认值
byType:根据要赋值的属性的类型,在IOC容器中匹配某个bean,为属性赋值
使用byType进行自动装配时,是利用Java的反射机制获取测试类中的set方法的参数类型,寻找此接口类型的实现类或子类,找到后将其首字母小写与IOC容器中的beanId进行匹配,匹配成功后则调用set方法进行依赖注入
注意:
a>若通过类型没有找到任何一个类型匹配的bean,此时不装配,属性使用默认值
b>若通过类型找到了多个类型匹配的bean,此时会抛出异常 NoUniqueBeanDefinitionException
总结:当使用byType实现自动装配时,IOC容器中有且只有一个类型匹配的bean能够为属性赋值
自动装配方式:byType
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser();
}
输出
保存成功!!!
自动装配方式:byName
byName:将自动装配的属性的属性名,作为bean的id在IOC容器中匹配相对应的bean进行赋值
当类型匹配的bean有多个时,此时可以使用byName实现自动装配
测试输出同上
匹配机制:
userController中的属性名 userService 和 配置UserServiceImpl中的bean id: userService 相匹配,如果没有自定义UserServiceImpl的bean id
其默认名为userServiceImpl相匹配,因为名不同则匹配失败。
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
举例:元旦联欢会要布置教室,蓝色的地方贴上元旦快乐四个字,红色的地方贴上拉花,黄色的地方贴上气球。
班长做了所有标记,同学们来完成具体工作。墙上的标记相当于我们在代码中使用的注解,后面同学们做的工作,相当于框架的具体操作。
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作
@Component:将类标识为普通组件 (组件:IOC管理下的一个bean)
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件
四个组件的区别
通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
创建控制层组件
@Controller
public class UserController {
}
创建接口UserService
public interface UserService {
}
创建业务层组件UserServiceImpl
@Service
public class UserServiceImpl {
}
创建接口UserDao
public interface UserDao {
}
创建持久层组件UserDaoImp
@Repository
public class UserDaoImpl {
}
情况一:最基本的扫描方式
扫描的是com.pjp.spring这个目录下全部的类
测试
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
UserController userController = ioc.getBean(UserController.class);
System.out.println(userController);
UserDaoImpl userDao = ioc.getBean(UserDaoImpl.class);
System.out.println(userDao);
UserServiceImpl userService = ioc.getBean(UserServiceImpl.class);
System.out.println(userService);
}
输出
com.pjp.spring.controller.UserController@1460a8c0
com.pjp.spring.dao.impl.UserDaoImpl@4f638935
com.pjp.spring.service.impl.UserServiceImpl@4387b79e
情况二:指定要排除的组件
context:exclude-filter标签:指定排除规则
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
测试
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
// UserController userController = ioc.getBean(UserController.class);
// System.out.println(userController);
UserDaoImpl userDao = ioc.getBean(UserDaoImpl.class);
System.out.println(userDao);
UserServiceImpl userService = ioc.getBean(UserServiceImpl.class);
System.out.println(userService);
}
输出
com.pjp.spring.dao.impl.UserDaoImpl@8909f18
com.pjp.spring.service.impl.UserServiceImpl@79ca92b9
获取不了@Controller标记的类
情况三:仅扫描指定组件
context:include-filter标签:指定在原有扫描规则的基础上追加的规则
use-default-filters属性:取值false表示关闭默认扫描规则
此时必须设置use-default-filters=false,因为默认规则扫描指定包下所有的类
测试
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
UserController userController = ioc.getBean(UserController.class);
System.out.println(userController);
}
输出
com.pjp.spring.controller.UserController@7a52f2a2
只能获取到@Controller标记的类
在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
通过注解+扫描所配置的bean的id:
默认情况 :
默认值为类的小驼峰,类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。
自定义bean的id :
可通过标识组件的注解的value属性设置自定义的bean的id。例如@Controller("controller")
默认:
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
UserController userController = ioc.getBean("userController",UserController.class);
}
输出
com.pjp.spring.controller.UserController@d83da2e
自定义:
public void test(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
UserController userController = ioc.getBean("controller",UserController.class);
System.out.println(userController);
}
输出
com.pjp.spring.controller.UserController@d83da2e
参考基于xml的自动装配
在UserController中声明UserService对象
在UserServiceImpl中声明UserDao对象
在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法
@Controller
public class UserController {
@Autowired
private 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("保存成功!!!!");
}
}
配置扫描
测试
public void testAutoWired(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser();
}
输出
保存成功!!!!
>标识在成员变量上,此时不需要设置成员变量的set方法
>标识在set方法上
>标识在为当前成员变量赋值的有参构造器上
标记在set方法或有参构造器上
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser() {
userService.saveUser();
}
}
@Controller
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void saveUser() {
userService.saveUser();
}
}
首先根据所需要的组件类型到IOC容器中查找
==>能够找到唯一的bean:直接执行装配
==>如果完全找不到匹配这个类型的bean:装配失败
==>所需类型匹配的bean不止一个
--->没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配
······>能够找到:执行装配
······>找不到:装配失败
---> 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
······> 能够找到:执行装配
······> 找不到:装配失败
此时配置多个bean并且自定义id和属性名不相同
则报错NoUniqueBeanDefinitionException
使用@Qualifier注解
通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值
@Qualifier("service")中的value值service和bean id= service相匹配
@Controller
public class UserController {
@Autowired
@Qualifier("service")
private UserService userService;
public void saveUser() {
userService.saveUser();
}
}
测试成功
若IOC容器中没有任何一个类型匹配的bean,此时抛出异常:NoSuchBeanDefinitionException
@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装配失败
将required设置为false,此时能装配则装配,无法装配则使用属性的默认值
@Controller
public class UserController {
@Autowired(required = false)
@Qualifier("service")
private UserService userService;
public void saveUser() {
userService.saveUser();
}
}
此时抛出NullPointerException(属性的默认值为Null)