现大多使用注解方式,xml方式并不简洁,本文仅记录xml用作基础学习。
0、前提
首先在父项目的pom.xml中配置好依赖们。然后子模块也可以使用这些依赖。
- 在resource目录下创建Spring的xml文件,名称无要求,本文使用bean.xml。文件最上头的几行为约束,表示可以用哪些标签等,如要使用其他标签,需先在此处添加对应的三行(xmlns:标签名=".../标签名"、xsi:schemaLocation="... ...",都把链接的末尾改为标签名)
- 在bean.xml文件中写bean标签:id为类的唯一标识,class为类的全类名(包名+类名)
- 根据bean.xml文件创建IoC容器
一、获取Bean:
有三种方式获取bean,基于id、基于class、基于id和class一起使用。
1. 根据id获取:getBean("id")
2. 根据类型获取:getBean(类名.class)
注意事项:
当根据类型获取bean时,要求IoC容器中指定类型的bean有且只能有一个。
当IOC容器中一共配置了多个,根据类型获取时会抛出异常。
期望匹配单实例的bean,但发现了两个,IoC不知道找谁:
只能使用其他两种方式。
扩展:
接口不能new,只能new接口的实现类。
使用实现类的类型可以获取到bean,所以此处试验接口类型能不能获取bean。
如果组件类实现了接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一
接口UserDao,实现类UserDaoImpl。
如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
接口UserDao,实现类UserDaoImpl、PersonDaoImpl。
原因与注意事项类似,不知道找谁。
结论:
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:[对象 instanceof指定的类型」的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
java中,instanceof运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回true,否则返回false。也就是说:用instanceof关键字做判断时,instanceof操作符的左右操作必须有继承或实现关系
3. 根据id和类型获取:getBean("id", 类名.class)
二、依赖注入
依赖注入是指在创建对象时,给属性设置值。
原始写法:通过set方法和有参构造器赋值
有两种实现方式:Setter和构造器。
1. 基于Setter注入:<property>
- 首先创建类,定义属性和set方法。
在类前使用lombok的@Data注解可以不用手动get、set、toString等。
- 在bean.xml配置文件中写bean标签,使用<property>标签调用set方法赋值。
name为属性,value为具体赋值。
2. 基于构造器注入:<constructor-arg>
- 首先创建类,定义属性和有参构造方法。
- 在bean.xml配置文件中写bean标签,使用<constructor-arg>标签调用有参构造方法赋值。
name为属性,value为具体赋值。(name也可以用index索引但不推荐,因为顺序要一致)
3. 特殊值处理
3.1 字面量
字面量没有引申含义,就是我们看到的这个数据本身。
使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量
3.2 null-空值
不能写成如下形式,此时为name属性赋值null字符串
3.3 xml实体:转义
<
3.4 CDATA节:<![CDATA[...]]>
4. 特殊类型属性注入
4.1 对象类型属性
一个类对象中有属性是另一个对象,例如在员工对象中有一个部门对象。
有三种方式:
4.1.1 引用外部bean——ref
在bean.xml中写好两个类的bean,部门bean和员工bean。
在员工bean的部门属性中用ref代替value赋值,ref="部门bean的id"
4.1.2 内部bean
在bean.xml中写员工bean,在员工bean的部门属性中声明部门bean。
4.1.3 级联属性赋值
在引用外部bean的基础上,使用对象.属性可以改变值。
4.2 数组类型属性——<array>标签
员工有多个爱好,用数组表示。
4.3 集合类型属性
4.3.1 List集合——<List>标签
一个部门有多个员工,部门对象bean中有一个员工类型的List。
因为员工是对象类型所以用ref引用外部bean,如果是字符串或其他,用value。
4.3.2 Map集合——<map><entry><key><value><ref>标签
也可以用map-key/value,一个学生有多个老师
4.3.3 引用集合类型的bean——<util:list><util:map>
由于要使用util标签,要先在bean.xml文件的最上头引入util,否则不能用:
一个学生有多门课-List,多个老师-Map,课和老师为对象类型。
两个lesson对象bean:
两个teacher对象bean:
student的bean:ref为对应util的id
4.4 p命名空间
要使用p标签,在bean.xml文件最上添加:
使用p命名空间,可以用:p:属性="值"进行赋值,而不用name、value了。
5. 引入外部属性文件
由于bean.xml文件中有很多bean,不利于修改和维护,所以把一些特定的值放在外部文件中,在引入外部文件进行注入。修改时只需修改外部文件,而不用修改配置文件。
常见的如数据库。
5.1 首先在pom.xml文件中添加数据库驱动和数据源等相关依赖。
5.2 创建外部属性文件(resource目录下),一般是.properties后缀格式,定义数据库信息,此处为jdbc.properties
=左边为名字,右边为赋值
5.3 引入外部属性文件:先在bean.xml最上头引用context名称空间,使用<context>标签引入外部属性文件,在bean中的value使用${}表达式完成注入
location为外部属性文件类路径:
class为德鲁伊连接池对象路径,value使用外部文件中的名字:
测试:
三、bean的作用域-scope
注意创建时机不同。
1. 单实例:
两个对象为同一个
2. 多实例:
两个对象不同
四、bean的生命周期
bean对象从创建到销毁的过程。
1. 生命周期过程:
bean对象创建(调用无参构造器)
给bean对象设置属性(set方法)
bean的后置处理器(初始化之前)
bean对象初始化(需在配置bean时指定初始化方法)
bean的后置处理器(初始化之后)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭
2. 后置处理器:
实现BeanPostProcessor接口,并重写两个方法:postProcessBeforeInitialization和postProcessAfterInitialization。
还需在IoC容器中配置后置处理器,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IoC容器中所有bean都会执行
class为接口实现类的全类名:
3. 测试
输出:
五、FactoryBean
FactoryBean是Spring的一种常用机制。帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来。和普通的bean不同,配置一个FactorvBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值决定创建什么对象。
将来整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
使用:
实现FactoryBean接口的类,重写getObject()方法,返回一个对象。
配置文件:
getBean("user")返回的user对象,而不是class的MyFactoryBean对象。
六、基于xml的自动装配
不使用自动装配即上述中的二、4.1部分使用ref。
在IoC容器匹配bean时,自动为该bean依赖的类类型、接口类型的属性赋值。
有两种方式:byName和byType
创建三个包,controller、Service、dao,其中controller包下有一个UserController类,Service包下有userService接口和实现该接口的userServiceImpl类,dao包下有userDao接口和实现该接口的userDaoImpl类。
1. 在bean.xml中的bean标签内使用autowired ="byType",在IoC容器中根据类型找bean自动装配。最好保证bean唯一。
如果没有找到匹配的类型,则该属性不匹配,默认赋值为null。如果找到多个,抛出异常NoUniqeBeanDefinitionException。
2. autowired ="byName",需保证id标识与类定义中的属性名称一致
不一致则报错,会赋空值。