Java – Spring – 数据自动装配
简介
Spring IoC是一个管理实体类对象的思想,支持bean的成员变量数据自定义,但同时也支持数据的自动识别和自动赋值。
Spring的 IoC 基于xml配置请查看以下文章
基于xml的自动装配
如果在bean的配置文件中,存在互相依赖的引用类类型属性时,Spring 支持自动识别并自动赋值,无需我们手动添加 property 标签来定义成员变量(属性)数据
手动装配
在说明自动装配之前,需要先说明一下何为手动装配。
举例:学校-班级-学生
假如学生类中保存了班级类,班级类中保存了学校类,通过学生类获取学校类的名称
// 定义学生类
public class Student {
// 记录了该学生在那个班级
private Clazz clazz;
}
// 定义班级类
public class Clazz {
// 记录了该班级归属那个学校
private School school;
}
// 定义学校类
public class School {
// 定义这个学校的名字
private String schoolName;
}
在bean中,我们需要使用 property 标签对引用类型的数据进行赋值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="studentBean" class="cn.unsoft.mybatis.pojo.Student">
<property name="clazz" ref="clazz"></property>
</bean>
// 定义班级类,以供 studentBean 引用
<bean id="clazz" class="cn.unsoft.mybatis.pojo.Clazz">
<property name="school" ref="school"></property>
</bean>
// 定义学校类,以供 clazz 引用
<bean id="school" class="cn.unsoft.mybatis.pojo.School">
<property name="schoolName" value="大学名称"></property>
</bean>
</beans>
自动装配属性 autowire
在上一章节我们知道,手动装配则是需要指定 property 标签引用,Spring 会自动通过 bean 自动创建对应的实例对象,并把数据自动注入到对应的类成员变量中。
自动装配的属性是【autowire】
byType自动装配
我们可以通过设置自动装配的方式,省略 property 标签的声明,Spring 会通过成员变量中的类型自动在beans中寻找合适类型的bean
<bean id="studentBean" class="cn.unsoft.mybatis.pojo.Student" autowire="byType">
<!-- 通过 autowire="byType" 自动装配后,Spring 会自动寻找Beans中符合类型的bean,因此无需再手动配置 property-->
<!-- <property name="clazz" ref="clazz"></property>-->
</bean>
<bean id="clazz" class="cn.unsoft.mybatis.pojo.Clazz" autowire="byType">
<!-- 通过 autowire="byType" 自动装配后,Spring 会自动寻找Beans中符合类型的bean,因此无需再手动配置 property-->
<!-- <property name="school" ref="school"></property>-->
</bean>
<bean id="school" class="cn.unsoft.mybatis.pojo.School">
<property name="schoolName" value="大学名称"></property>
</bean>
byName自动装配
Spring 除了通过成员变量的类型来自动寻找合适的bean装配外,还支持通过识别成员变量的名称来对应bean名称自动装配
<bean id="studentBean" class="cn.unsoft.mybatis.pojo.Student" autowire="byName">
<!-- 因为成员变量中的名称为clazz,所以会自动寻找id为【clazz】的bean,并自动装配-->
<!-- <property name="clazz" ref="clazz"></property>-->
</bean>
<bean id="clazz" class="cn.unsoft.mybatis.pojo.Clazz" autowire="byName">
<!-- 因为成员变量中的名称为school,所以会自动寻找id为【school】的bean,并自动装配-->
<!-- <property name="school" ref="school"></property>-->
</bean>
<bean id="school" class="cn.unsoft.mybatis.pojo.School">
<property name="schoolName" value="大学名称"></property>
</bean>
其它属性值
autowire = "no"
: 表示不进行自动装配
autowire = "default"
: 默认配置,和 no 一致,也表示不进行自动装配
autowire = "constructor"
: 使用构造器进行自动装配
注意事项
1.在自动装配中,如果使用 ByType 进行自动装配时,不允许出现多个相同类型的bean,否则会报出异常。如果不存在对应类型的bean,则Spring不进行装配,该属性将被赋值为原始值 null
2.在自动装配中,如果使用 ByName 进行自动装配时,若成员变量名称与 beans 中找不到相匹配的 id 的 bean 的话,则Spring不进行装配,该属性将被赋值为原始值 null。如果出现相同 id 的bean 时,则会直接报错,因为beans中bean的id值是唯一的。
基于注解的自动装配
Spring 支持通过在类上添加注解来识别那些类交由IoC管理
Spring 支持在类上添加的注解有4个:
@Component
:将类标识为普通组件
@Controller
:将类标识为控制层组件
@Service
:将类标识为业务层组件
@Repository
:将类标识为持久层组件
注解本身所实现的功能是一样的,但是它们有不同的含意,其主要含意在于能使开发人员清晰看到那些实体类是属于实现那方面的功能的类,虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
扫描注解类
我们在类中添加了注解,声明这个类是可交由IoC去管理的,但Spring并不能通过注解主动记录所有被注解的类,因此在创建IoC之前,Spring 必须主动扫描包中有那些类标注了注解,从而知道那些类属于IoC管理的类。
配置扫描
<?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/context https://www.springframework.org/schema/context/spring-context.xsd">
// 配置扫描标注了注解的类
<context:component-scan base-package="cn.unsoft.mybatis"></context:component-scan>
</beans>
base-package
: 要扫描的包的位置,会自动扫描子目录,偏历所有文件寻找标注了注解的类。
注意:base-package 可以扫描子目录,但应尽可能的精确扫描目录,以避免扫描没有注解类的目录,降低性能
同时,base-package支持多目录
// 配置扫描多目录
<context:component-scan base-package="cn.unsoft.spring.controller,cn.unsoft.spring.pojo"></context:component-scan>
排除扫描目录
在项目中,SpringMVC与Spring可能会造成扫描冲突,使得SpringMVC和Spring重复扫描目录,或因其它原因使得目录重复扫描。
因此可以通过设置排除扫描目录的方式对某些目录不进行扫描:
<context:component-scan base-package="cn.unsoft.mybatis">
// 通过对注解类型排除扫描
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
type
:要设置排除的类型,常用的是 annotation
和 assignable
annotation
: 通过注解类型排除,
assignable
: 通过实体类型排除,
expression
:该类型的准确类别,比如annotation
的话应当设置注解类型的全类名,如果是assignable
的话应当设置排除类型的全类名
指定扫描目录
除了排除扫描目录的功能外,还可以扫描指定目录
<context:component-scan base-package="cn.unsoft.mybatis" use-default-filters="false">
// 设置只扫描的注解类型
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
</context:component-scan>
type
:要设置扫描的类型,常用的是 annotation
和 assignable
annotation
: 通过注解类型扫描,
assignable
: 通过实体类型扫描,
expression
:该类型的准确类别,比如annotation
的话应当设置注解类型的全类名,如果是assignable
的话应当设置扫描类型的全类名
注意:在Spring中,设置指定扫描目录并不影响它原来的策略,它原来的策略本身就是扫描全部目录的类型,所以设置指定扫描,Spring不会取消指定扫描以外的目录(也就是说,本来就全部扫描,设置指定扫描后,依然是全部扫描,不会因设置了指定扫描而自动排除其它扫描),因此指定扫描目录需要关闭Spring默认扫描策略 use-default-filters="false"
注解bean类ID名
在通过xml配置bean类时,都会有一个id值,我们可以通过getBean(name)来获取bean对象。
在注解中,bean类的默认ID名为该类名的小驼峰名,如
UserController ==> userController
UserDao ==> userDao
自定义bean类ID名
可以在注解中加入参数,该参数可自定义ID名,如
@Controller("student")
public class Student { }
注解bean类自动装配
Spring中除了可以在xml中进行自动装配,也支持在注解上自动装配。
成员变量自动装配
通过使用【@Autowired】
注解即可设置成员变量的自动装配
@Controller("student")
public class Student {
// 使用 @Autowired 声明自动装配
@Autowired
private Clazz clazz;
}
@Service
public class Clazz {
// 使用 @Autowired 自动装配
@Autowired
private School school;
}
@Repository
public class School {
// 定义这个学校的名字
private String schoolName;
}
注意:【@Autowired】
注解在成员变量中时,成员变量无需添加 get / set
方法
set方法自动装配
【@Autowired】
注解同时也能方在 set 方法中,效果相同
@Controller("student")
public class Student {
// 记录了该学生在那个班级
private Clazz clazz;
// 在set 方法中注解
@Autowired
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
}
有参构造器自动装配
【@Autowired】
注解也可放在构造器中实现自动装配,效果相同
@Controller("student")
public class Student {
// 记录了该学生在那个班级
private Clazz clazz;
// 在构造方法中注解
@Autowired
public Student(Clazz clazz) {
this.clazz = clazz;
}
}
注解自动装配原理
1.使用【@Autowired】
时,Spring 底层会优先通过 byType
的方式对 bean 进行适配(或通过接口适配该接口的实现类)
2.使用【@Autowired】
时,如果Spring通过 byType
寻找到多个合适的 bean 类时,会使用 byName
进行适配。Spring 会使用 成员变量 的变量名作为 id 对bean进行匹配。
3.使用【@Autowired】
时,如果Spring 即不能通过 byType
找到合适的 bean 且也不能通过 byName
找到合适的 bean 时,会报出异常:NoUniqueBeanDefunitionException
(没有找到一个合适的Bean异常)
可以使用【@Qualifier("Bean名称")】
来手动指定合适的 bean 类
@Qualifier
注解可以设置名称为 成员变量的小驼峰名称,如“userController”,这是因为【@Autowired】
注解Spring 会自动装配生成成员变量名的小驼峰名称,同时也可以定义成 xml 中配置的 bean ID
@Controller("student")
public class Student {
// 记录了该学生在那个班级
@Autowired
@Qualifier("clazzBean")
private Clazz clazz;
}
4.若没有找到任何合适的bean类型时(比如需要的bean类型既没有注解定义,也没有在xml中配置bean项),会报异常NoSuchBeanDefunitionException
(没有找到Bean异常)
这和xml自动装配有些区别,在xml中,如果Spring找不到合适的bean类进行装配时,Spring会选择不装配,并置默认值null
,但不会报异常。
这是因为,【@Autowired】
默认参数 【@Autowired(required = true)】
,Spring 默认使用 【@Autowired】
注解进行自动装配时,必须完成自动装配
可以设置 【@Autowired(required = false)】
关闭必须完成自动装配功能。
@Controller("student")
public class Student {
// 当Spring 找不到合适的bean时,会把 clazz 设为 null 值
@Autowired(required = false)
private Clazz clazz;
}
共有 0 条评论