装配:Spring 在 Bean 与 Bean 之间建立依赖关系的行为
IOC 容器虽然功能强大,但只是一个容器而已,自己并不能独自完成装配工作。需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。
3种装配方式:
在 XML 配置中通过 和 中的 ref 属性,手动维护 Bean 与 Bean 之间的依赖关系的。
问题:对于只有少量 Bean 的应用来说,在xml这种方式已经足够满足我们的需求了。但随着应用的功能不断发展,容器中包含的 Bean 会越来越多,Bean 和 Bean 之间的依赖关系也越来越复杂,这就使得我们所编写的 XML 配置也越来越复杂,越来越繁琐。
而过于复杂的 XML 配置可读性差,而且编写起来极易出错,严重的降低了开发人员的开发效率。为了解决这一问题,Spring 框架还为我们提供了“自动装配”功能。
自动装配功能:让 Spring 容器依据自动装配的规则,(五种),为**指定的 Bean 从应用的上下文(AppplicationContext 容器)**中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。这一过程不需要使用 和 元素 ref 属性来实现。
注意:Spring 框架默认不支持自动装配的,要使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。
属性值 | 说明 |
---|---|
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
}
<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='电脑'}}
不用配置Spring的xml配置了,用java来做
JavaConfig时spring的一个子项目,在spring4之后成为了一个核心功能。在springBoot中使用非常频繁
有两种方式,配置类+@bean 或者 配置类+实体(@Component)+扫描包
public class User {
@Value("张三")
private String name;
@Value("18")
private int age;
}
public class Shop {
@Value("电脑")
private String name;
}
/**
* @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='电脑'}
@Component
public class User {
@Value("张三")
private String name;
@Value("18")
private int age;
}
@Component
public class Shop {
@Value("电脑")
private String name;
}
/**
* @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='电脑'}
@Import({ CustomerConfig.class, SchedulerConfig.class })
- 1
@Import注解在4.2之前只支持导入配置类
在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean
@Import(DemoService.class) // 在spring 4.2之前是不不支持的
- 1
import注解主要用在基于java代码显式创建bean的过程中,用于将多个分散的java config配置类融合成一个更大的config类。其实除了 import注解外,还有 importResource注解,其作用都类似。配置类的组合主要发生在跨模块或跨包的配置类引用过程中。
@ImportResource(locations = {"classpath:beans.xml"}) @ImportResource(value = {"classpath:file1.xml","classpath:file2.xml"})
- 1
- 2
与@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
<?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>
/*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'}
- 从 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>
<?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 相关的约束。
注解 | 说明 |
---|---|
@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”)注解的参数指定。进行唯一装配属性 |
@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;
}
<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执行顺序不同