6.bean的自动装配
-
装配:Spring 在 Bean 与 Bean 之间建立依赖关系的行为
-
IOC 容器虽然功能强大,但只是一个容器而已,自己并不能独自完成装配工作。需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。
-
3种装配方式:
6.1 **方式1:**在xml中显式配置
在 XML 配置中通过 和 中的 ref 属性,手动维护 Bean 与 Bean 之间的依赖关系的。
-
问题:对于只有少量 Bean 的应用来说,在xml这种方式已经足够满足我们的需求了。但随着应用的功能不断发展,容器中包含的 Bean 会越来越多,Bean 和 Bean 之间的依赖关系也越来越复杂,这就使得我们所编写的 XML 配置也越来越复杂,越来越繁琐。
-
而过于复杂的 XML 配置可读性差,而且编写起来极易出错,严重的降低了开发人员的开发效率。为了解决这一问题,Spring 框架还为我们提供了“自动装配”功能。
6.2 方式2:Spring 自动装配(隐式配置)
自动装配功能:让 Spring 容器依据自动装配的规则,(五种),为**指定的 Bean 从应用的上下文(AppplicationContext 容器)**中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。这一过程不需要使用 和 元素 ref 属性来实现。
- 优点:能够简化 Spring 应用的 XML 配置,降低工作量。
注意:Spring 框架默认不支持自动装配的,要使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。
- Spring 框架 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应
属性值 | 说明 |
---|---|
byName | 按名称自动装配。 Spring 会根据的 Java 类中对象属性的名称,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 id 或 name 属性值与这个对象属性的名称相同,则获取这个 Bean,并与当前的 Java 类 Bean 建立关联关系。 |
byType | 按类型自动装配。 Spring 会根据 Java 类中的对象属性的类型,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 class 属性值与这个对象属性的类型相匹配,则获取这个 Bean,并与当前的 Java 类的 Bean 建立关联关系。 |
constructor | 与 byType 模式相似,不同之处在与它应用于构造器参数(依赖项),如果在容器中没有找到与构造器参数类型一致的 Bean,那么将抛出异常。其实就是根据构造器参数的数据类型,进行 byType 模式的自动装配。 |
default | 表示默认采用上一级元素 设置的自动装配规则(default-autowire)进行装配。 |
no | 默认值,表示不使用自动装配,Bean 的依赖关系必须通过 和 元素的 ref 属性来定义。 |
- 注意:
byName自动装配规则:要求XML 文件中 Bean 的 id 或 name 必须与类中的属性名称相同。否则返回null对象,并且 Bean 的 id或 name 区分大小写和Bean 的 id要全局唯一
byType自动装配规则:XML 文件中 Bean 的 id 或 name 与类中的属性名可以不同或省略,只要 Bean 的 class 属性值与类中的对象属性的类型相同,就可以完成自动装配。
- 如果同时存在多个相同类型的 Bean,则注入失败,并且引发异常
default自动装配规则:要求上一级元素 需要设置default-autowire自动装配规则
-
举例理解:一家公司有职员和电脑
-
实体类创建
public class Person {
private String name;
...get/set/toString/constructor
}
public class Computer {
private String name;
...get/set/toString/constructor
}
public class Company {
private Person person;
private Computer computer;
...get/set/toString/constructor
}
- xml中设置自动装配方式
<bean id="computer" class="com.zk.pojo.Computer">
<property name="name" value="电脑"/>
</bean>
<bean id="person" class="com.zk.pojo.Person">
<property name="name" value="职员"/>
</bean>
<bean id="company" class="com.zk.pojo.Company" autowire="byName"/>
- 测试
@Test
public void test01(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Company company = context.getBean("company", Company.class);
System.out.println(company.toString());
}
//Company{person=person{name='职员'}, computer=computer{name='电脑'}}
6.3 方式3:在java中显示配置
-
不用配置Spring的xml配置了,用java来做
-
JavaConfig时spring的一个子项目,在spring4之后成为了一个核心功能。在springBoot中使用非常频繁
-
有两种方式,配置类+@bean 或者 配置类+实体(@Component)+扫描包
6.3.1 方式1:配置类+@bean
public class User {
@Value("张三")
private String name;
@Value("18")
private int age;
}
public class Shop {
@Value("电脑")
private String name;
}
- 不开起组件扫描的MyConfig组件配置类
- @Bean跳转是跳转到使用的地方,不是类定义
/**
* @Configuration是一个@Component同样会被注册到容器中,交给容器托管
* @Configuration代表是一个配置类,和之前看到的beans.xml*/
@Configuration
public class MyConfig {
/**
* @Bean相当于beans中的单个的bean标签,注册到容器中
* 方法名字相当于bean标签的id
* 方法返回值相当于bean标签的Class属性
* 返回要注入的bean对象
* */
@Bean
public static User user(){
return new User();
}
@Bean
public static Shop shop(){
return new Shop();
}
}
- 测试结果
public class TestConfig {
@Test
public void test01(){
//若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User)context.getBean("user");
System.out.println(user.toString());
Shop shop = (Shop)context.getBean("shop");
System.out.println(shop.toString());
}
}
//User{name='张三', age=18}
//Shop{name='电脑'}
6.3.2 方式2:配置类+实体(@Component)+扫描包
- 实体类定义+@Component
@Component
public class User {
@Value("张三")
private String name;
@Value("18")
private int age;
}
@Component
public class Shop {
@Value("电脑")
private String name;
}
- MyConfig组件配置类
/**
* @Configuration是一个@Component同样会被注册到容器中,交给容器托管
* @Configuration代表是一个配置类,和之前看到的beans.xml*/
@Configuration
@ComponentScan("com.zk")
public class MyConfig {
}
- 测试结果
public class TestConfig {
@Test
public void test01(){
//若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User)context.getBean("user");
System.out.println(user.toString());
Shop shop = (Shop)context.getBean("shop");
System.out.println(shop.toString());
}
}
//User{name='张三', age=18}
//Shop{name='电脑'}
6.3.3 配置类整合
- 可以通过@Import(OtherConfig.class)注解将其他的配置类整合进来
@Import({ CustomerConfig.class, SchedulerConfig.class })
@Import注解在4.2之前只支持导入配置类
在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean
@Import(DemoService.class) // 在spring 4.2之前是不不支持的
import注解主要用在基于java代码显式创建bean的过程中,用于将多个分散的java config配置类融合成一个更大的config类。其实除了 import注解外,还有 importResource注解,其作用都类似。配置类的组合主要发生在跨模块或跨包的配置类引用过程中。
- 这里还可以使用@ImportResource(String [ ] )注解来引入其他xml配置文件
@ImportResource(locations = {"classpath:beans.xml"}) @ImportResource(value = {"classpath:file1.xml","classpath:file2.xml"})
与@Import一样,此注解提供类似于 Spring XML 中的元素的功能。它通常在设计@Configuration类以由AnnotationConfigApplicationContext引导时使用,但仍然需要一些 XML 功能,例如命名空间。
-
举例:利用@ImportResource 和 @Value 注解进行资源文件读取
-
编写db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/dbname?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
user=root
password=123456
- 编写applicationContext.xml,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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/context/spring-aop.xsd">
<!--context:property-placeholder 指定资源文件的位置-->
<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="com.zk"/>
<context:annotation-config/>
</beans>
- 编写配置类DBConfig
/*applicationContext.xml引入了db.properties
* 配置类引入applicationContext.xml*/
@Configuration
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class DBConfig {
@Value("${driver}")
private String driver;
@Value("${url}")
private String url;
@Value("${user}")
private String username;
@Value("${password}")
private String password;
@Bean
public MyDriverManager myDriverManager(){
return new MyDriverManager(driver,url,username,password);
}
}
- 编写普通类
public class MyDriverManager {
private String driver;
private String url;
private String username;
private String password;
public MyDriverManager(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
System.out.println("driver : " + driver);
System.out.println("url : " + url);
System.out.println("username : " + username);
System.out.println("password : " + password);
}
@Override
public String toString() {
return "MyDriverManager{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
- 测试
@Test
public void test02(){
//若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DBConfig.class);
/*ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");*/
MyDriverManager myDriverManager = (MyDriverManager)context.getBean("myDriverManager");
System.out.println(myDriverManager.toString());
}
- 结果
driver : com.mysql.cj.jdbc.Driver
url : jdbc:mysql://localhost:3306/dbname?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username : root
password : 123456
MyDriverManager{driver='com.mysql.cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/dbname?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC', username='root', password='123456'}
6.4 方式4:基于注解自动装配
- 从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
- Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。
Spring 注解实现自动装配的步骤如下:
- 引入依赖:spring-aop 的 Jar 包也会用到
- 开启组件扫描
- 使用注解定义 Bean
- 基于注解依赖注入
- 倒入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.19</version>
</dependency>
- 导入context约束,开启组件扫描applicationContext.xml
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/context/spring-aop.xsd">
<!--配置注解的支持-->
<context:annotation-config/>
<!--新版本配置扫描注解就可以运行,开启组件扫描功能-->
<context:component-scan base-package="net.biancheng.c"></context:component-scan>
</beans>
注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。
- 基于注解依赖注入,可以通过以下注解将定义好 Bean 装配到其它的 Bean 中。
注解 | 说明 |
---|---|
@Autowired | 可以应用到 Bean 的属性变量、setter 方法、非 setter 方法及构造函数等,默认按照 Bean 的类型进行装配。 @Autowired 注解默认按照 Bean 的类型进行装配,默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合 @Qualifier 注解一起使用 。如果在属性变量加@Autowired那么setter 方法都可以不需要了,前提式这个依赖的bean在容器中存在,进行类型装配 |
@Resource | javax.annotation.Resource时java的注解;作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 的名称进行装配。 @Resource 中有两个重要属性:name 和 type。 Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配;**如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;**如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。 |
@Qualifier | 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier (value=“beanname”)注解的参数指定。进行唯一装配属性 |
- @Autowired 和 @Resource 的区别是什么?
- @Autowired 是 Spring 提供的注解, @Resource 是 JDK 提供的注解
- @Autowired 默认的注入方式为 byType (根据类型进行匹配),@Resource 默认注入方式为 byName (根据名称进行匹配)。
- 当一个接口存在多个实现类的情况下, @Autowired 和 @Resource 都需要通过名称才能正确匹配到对应的 Bean。
- Autowired 可以通过 @Qualifier 注解来显示指定名称,
- Resource 可以通过 name 属性来显示指定名称。
@Nullable 字段加了这个注解,说明这个字段验证时可以为null,Nullable 是给编译器看的
public Company(@Nullable Person person, Computer computer) {
this.person = person;
this.computer = computer;
}
注解@Autowired的属性required,此属性用于控制如果找不到要依赖注入的对象时是否报错,默认true即默认找不到要注入的对象时会报错,required在spring security中配置动态权限,前端不需要权限,就需要配置
@Autowired使用多的原因:大多数bean时单例的,不会出现多个同类型的bean
-
举例理解
-
只在需要依赖的类中的属性上加@Autowired注解
XML和注解都是反射! 注解是反射直接给属性赋值,xml的是反射获得getset方法赋值.原因:注解是通过类对象获取类的所有信息,包括私有属性,所以能直接赋值
public class Company {
//显示定义@Autowired的属性required为false,说明依赖的对象可以为null,否则不许为空
@Autowired(required = false)
private Person person;
@Autowired
private Computer computer;
}
- xml配置文件中开启注解支持,加context约束见上
<bean id="computer" class="com.zk.pojo.Computer">
<constructor-arg name="name" value="电脑"/>
</bean>
<bean id="Person" class="com.zk.pojo.Person" name="person">
<constructor-arg name="name" value="职员"/>
</bean>
<bean id="company" class="com.zk.pojo.Company"></bean>
<!--配置注解的支持-->
<context:annotation-config/>
- 测试
@Test
public void test01(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Company company = context.getBean("company", Company.class);
System.out.println(company.toString());
}
//Company{person=person{name='职员'}, computer=computer{name='电脑'}}
@Autowired是侵入式的,已经违背了Spring的理念,这里建议用@Resources 可以无缝切换IOC框架
@Resource已经在JDK11中被移出了
多个bean对象,@Resource会自动装配第一个bean对象
@Autowired和@Resource都可以用在属性上
@Autowired和@Resource执行顺序不同
本专栏下一篇:spring注解开发
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123911.html