Spring如何自动装配?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring如何自动装配?相关的知识,希望对你有一定的参考价值。

###### 问题遇到的现象和发生背景
Spring是怎么自动装配的,我该怎么用,需要注意的点有哪些
###### Main类

```
package yinyong;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main
public static Bean bean;
public static void main(String[] args)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Bean.xml");
bean = (Bean)applicationContext.getBean("zhuang_pei");
bean.getUser().Out();
bean = (Bean)applicationContext.getBean("name_zhuangpei");
System.out.println(bean.getUser());



```

###### User类

```
package yinyong;

public class User
private String name;
private Integer age;
private char sex;

public String getName()
return name;


public void setName(String name)
this.name = name;


public Integer getAge()
return age;


public void setAge(Integer age)
this.age = age;


public char getSex()
return sex;


public void setSex(char sex)
this.sex = sex;


public User(String name, Integer age, char sex)
this.name = name;
this.age = age;
this.sex = sex;


public void Out()
System.out.println("name: " + name);
System.out.println("age: " + age);
System.out.println("sex: " + sex);


public User()




```

###### Bean类

```
package yinyong;

public class Bean
public User getUser()
return user;


public void setUser(User user)
this.user = user;


public Bean(User user)
this.user = user;


public Bean()



private User user;


```
###### xml

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx" 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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<import resource="classpath:zhuru.xml"/>
<!-- 导入其它文件的配置 -->
<bean id="zhuang_pei" class="yinyong.Bean">
<property name="user" ref="zhuru_gouzao"></property>
<!-- 引用其它文件的bean -->
</bean>
<bean autowire="byName" id="name_zhuangpei" class="yinyong.Bean"></bean>
</beans>

```

先写个例子,我看下有什么区别

Spring的自动装配是指通过Spring容器自动地将一个bean与另一个bean关联起来。Spring提供了三种自动装配方式:
默认的byName自动装配:当一个bean的属性名称与其他bean的id相同时,Spring会自动将该bean引用到这些同名的bean中去。
byType自动装配:当一个bean的属性数据类型与其他bean的类型相同时,Spring会自动将该bean引用到这些同类型的bean中去。
constructor自动装配:与byType类似,但是是在构造函数参数类型匹配时自动装配。
Spring的自动装配可以使用@Autowired、@Qualifier和@Resource等注解来实现:
@Autowired注解可以用于自动装配所需的bean,它可以用在字段、setter方法、构造函数上。
@Qualifier注解可以与@Autowired一起使用,表示需要装配的具体实现。
@Resource注解也可以用于自动装配所需的bean,它支持byName和byType两种自动装配方式。
通过自动装配,Spring可以帮助我们省去手动编写配置文件的麻烦,提高开发效率。需要注意的是,在使用自动装配时,必须确保bean的命名或类型是唯一的,否则可能会出现自动装配失败或错误的情况。
参考技术A

Spring可以使用自动装配机制将bean之间的依赖关系自动化。自动装配可以减少手动配置的工作量,提高开发效率。

在Spring中,可以使用三种方式进行自动装配:

1.按名称自动装配:通过设置bean的名称,将需要自动注入的属性名称与bean的名称相同,从而实现自动装配。

<bean id="beanA" class="com.example.BeanA">

<property name="beanB" ref="beanB"/>

</bean>

<bean id="beanB" class="com.example.BeanB"/>

2.按类型自动装配:通过设置bean的类型,将需要自动注入的属性类型与bean的类型相同,从而实现自动装配。

<bean id="beanA" class="com.example.BeanA">

<property name="beanB" ref="beanB"/>

</bean>

<bean id="beanB" class="com.example.BeanB"/>

3.构造函数自动装配:通过设置bean的构造函数参数,将需要自动注入的参数类型与bean的构造函数参数类型相同,从而实现自动装配。

<bean id="beanA" class="com.example.BeanA">

<constructor-arg ref="beanB"/>

</bean>

<bean id="beanB" class="com.example.BeanB"/>

除了XML配置外,还可以使用JavaConfig或注解方式实现自动装配。例如,使用注解方式实现按名称自动装配:

@Component

public class BeanA

@Autowired

private BeanB beanB;

// ...

@Component

public class BeanB

// ...

在上面的例子中,@Autowired注解将会自动将beanB属性注入到beanA中。

Spring Boot 自动装配是 Spring Boot 最重要的功能之一,它可以根据项目中的依赖关系和配置信息,自动配置 Spring 应用程序所需的所有组件和功能。

Spring Boot 自动装配基于 Spring 框架的注解和配置机制,通过自动扫描和配置来实现自动化装配。Spring Boot 应用程序中有许多标准的注解和配置文件,比如 @SpringBootApplication 注解、application.properties 或 application.yml 配置文件,Spring Boot 就是通过这些标准的注解和配置文件来自动扫描和装配应用程序所需的组件。

举个例子,如果你在 Spring Boot 应用程序中使用了 Spring Data JPA,并且在 application.properties 中配置了相关的数据库连接信息,那么 Spring Boot 就会自动扫描并配置相关的 JPA 组件,使其可以直接在应用程序中使用。

除了使用标准的注解和配置文件来实现自动装配,Spring Boot 还提供了许多自定义的注解和配置类,可以根据自己的需要进行自定义扩展。

总的来说,Spring Boot 自动装配可以极大地提高开发效率和代码质量,减少重复工作和错误率。

追问

我看网上都是方法或变量自带值 的,像我这种从bean中传值的实现不了 么,我试了好几次,都是输出的null 

参考技术B 在Spring中实现自动组装(autowiring)主要是通过自动装配(autowiring)来实现的。自动装配将自动查找项目中可用的bean,并自动将其注入到需要的bean中。使用自动组装可以减少一些繁琐的配置,同时还可以提高应用程序的可维护性和灵活性。
在Spring中,自动组装有以下几种方式:
1. 根据类型自动装配:Spring会自动查找容器中与需要自动装配的bean属性类型匹配的其他bean,并自动注入到需要的bean属性中。
2. 根据名称自动装配:Spring会自动查找容器中与需要自动装配的bean属性名称匹配的其他bean,并自动注入到需要的bean属性中。
3. 根据构造函数自动装配:Spring会自动查找容器中与需要自动装配的bean参数类型匹配的其他bean,并自动注入到需要的bean构造函数参数中。
可以使用@Autowired、@Resource、@Inject注解的方式来实现自动组装。其中@Autowired注解默认是按照类型自动装配的,如果有多个与之匹配的bean,则使用@Qualifer注解来指定具体的bean。@Resource注解默认是按照名称自动装配的,如果需要按照类型自动装配,则需要指定其type属性。@Inject注解与@Autowired注解类似,也是默认按照类型自动装配。
自动组装是Spring框架中一个非常方便的特性,可以让我们更轻松地进行bean的注入和管理,提高应用程序的开发效率和可维护性。同时,需要注意自动组装可能存在的歧义和冲突,需要正确地设置@Autowired、@Resource、@Inject注解的属性,才能确保正确地进行自动组装。

深度剖析Spring Boot自动装配机制实现原理

在前面的分析中,Spring Framework一直在致力于解决一个问题,就是如何让bean的管理变得更简单,如何让开发者尽可能的少关注一些基础化的bean的配置,从而实现自动装配。所以,所谓的自动装配,实际上就是如何自动将bean装载到Ioc容器中来。

实际上在spring 3.x版本中,Enable模块驱动注解的出现,已经有了一定的自动装配的雏形,而真正能够实现这一机制,还是在spirng 4.x版本中,conditional条件注解的出现。ok,我们来看一下spring boot的自动装配是怎么一回事。

自动装配的演示

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> 
spring:
    redis:
      host: 127.0.0.1 
      port: 6379
 @Autowired
    private RedisTemplate<String,String>redisTemplate;

按照下面的顺序添加starter,然后添加配置,使用RedisTemplate就可以使用了? 那大家想没想过一个问题,为什么RedisTemplate可以被直接注入?它是什么时候加入到Ioc容器的呢? 这就是自动装配。自动装配可以使得classpath下依赖的包相关的bean,被自动装载到Spring Ioc容器中,怎么做到的呢?

深入分析EnableAutoConfiguration

EnableAutoConfiguration的主要作用其实就是帮助springboot应用把所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器中。

再回到EnableAutoConfiguration这个注解中,我们发现它的import是这样

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

但是从EnableAutoCOnfiguration上面的import注解来看,这里面并不是引入另外一个Configuration。而是一个ImportSelector。这个是什么东西呢?

AutoConfigurationImportSelector是什么?

Enable注解不仅仅可以像前面演示的案例一样很简单的实现多个Configuration的整合,还可以实现一些复杂的场景,比如可以根据上下文来激活不同类型的bean,@Import注解可以配置三种不同的class

  1. 第一种就是前面演示过的,基于普通bean或者带有@Configuration的bean进行诸如
  2. 实现ImportSelector接口进行动态注入

实现ImportBeanDefinitionRegistrar接口进行动态注入

CacheService

public class CacheService {
}

LoggerService

public class LoggerService {
}

EnableDefineService

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented 
@Inherited  --允许被继承
@Import({GpDefineImportSelector.class})
public @interface EnableDefineService {

    String[] packages() default "";
}

GpDefineImportSelector

public class GpDefineImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //获得指定注解的详细信息。我们可以根据注解中配置的属性来返回不同的class,
        //从而可以达到动态开启不同功能的目的
    
annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true)
            .forEach((k,v) -> {
                log.info(annotationMetadata.getClassName());
                log.info("k:{},v:{}",k,String.valueOf(v));
            });
        return new String[]{CacheService.class.getName()};
    }
}

EnableDemoTest

@SpringBootApplication
@EnableDefineService(name = "gupao",value = "gupao")
public class EnableDemoTest {
    public static void main(String[] args) {
        ConfigurableApplicationContext ca=SpringApplication.run(EnableDemoTest.class,args);
        System.out.println(ca.getBean(CacheService.class));
        System.out.println(ca.getBean(LoggerService.class));
    }
}

了解了selector的基本原理之后,后续再去分析AutoConfigurationImportSelector的原理就很简单了,它本质上也是对于bean的动态加载。

@EnableAutoConfiguration注解的实现原理

了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了

它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector

@Import(AutoConfigurationImportSelector.class)

从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都会被纳入到spring容器中。

那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的selectImports方法

selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
// 获取所有候选配置类EnableAutoConfiguration
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
         autoConfigurationMetadata, annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(
      AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
//获取元注解中的属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoriesLoader 加载classpath路径下META-INF\\spring.factories中,
//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value
   List<String> configurations = getCandidateConfigurations(annotationMetadata,
         attributes);
//去重
   configurations = removeDuplicates(configurations);
//应用exclusion属性
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
   configurations = filter(configurations, autoConfigurationMetadata);
   //广播事件
fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤

SpringFactoriesLoader

为了给大家补一下基础,我在这里简单分析一下SpringFactoriesLoader这个工具类的使用。它其实和java中的SPI机制的原理是一样的,不过它比SPI更好的点在于不会一次性加载所有的类,而是根据key进行加载。

首先,SpringFactoriesLoader的作用是从classpath/META-INF/spring.factories文件中,根据key来加载对应的类到spring IoC容器中。接下来带大家实践一下

创建外部项目jar

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.13.RELEASE</version>
</dependency>

创建bean以及config

public class GuPaoCore {
    public String study(){
        System.out.println("good good study, day day up");
        return "GuPaoEdu.com";
    }
}
@Configuration
public class GuPaoConfig {
    @Bean
    public GuPaoCore guPaoCore(){
        return new GuPaoCore();
    }
}

创建另外一个工程(spring-boot)

把前面的工程打包成jar,当前项目依赖该jar包

<dependency>
    <groupId>com.gupaoedu.practice</groupId>
    <artifactId>Gupao-Core</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

通过下面代码获取依赖包中的属性

运行结果会报错,原因是GuPaoCore并没有被Spring的IoC容器所加载,也就是没有被EnableAutoConfiguration导入

@SpringBootApplication
public class SpringBootStudyApplication {
    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext ac=SpringApplication.run(SpringBootStudyApplication.class, args);
        GuPaoCore gpc=ac.getBean(GuPaoCore.class);
        System.out.println(gpc.study());
    }
}

解决方案

在GuPao-Core项目resources下新建文件夹META-INF,在文件夹下面新建spring.factories文件,文件中配置,key为自定配置类EnableAutoConfiguration的全路径,value是配置类的全路径

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gupaoedu.practice.GuPaoConfig

重新打包,重新运行SpringBootStudyApplication这个类。

可以发现,我们编写的那个类,就被加载进来了。

@EnableAutoConfiguration注解的实现原理

了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了

它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector

@Import(AutoConfigurationImportSelector.class)

从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都会被纳入到spring容器中。

那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的selectImports方法

selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata 
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
// 获取所有候选配置类EnableAutoConfiguration
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
         autoConfigurationMetadata, annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(
      AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
//获取元注解中的属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoriesLoader 加载classpath路径下META-INF\\spring.factories中,
//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value
   List<String> configurations = getCandidateConfigurations(annotationMetadata,
         attributes);
//去重
   configurations = removeDuplicates(configurations);
//应用exclusion属性
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
   configurations = filter(configurations, autoConfigurationMetadata);
   //广播事件
fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mic带你学架构
如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注「跟着Mic学架构」公众号公众号获取更多技术干货!

以上是关于Spring如何自动装配?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 自动装配定义与自定义starter原理,及如何实现自定义装配

Spring JUnit:如何在自动装配组件中模拟自动装配组件

Spring_总结_03_装配Bean之自动装配

spring学习总结——高级装配学习一(处理自动装配的歧义性)

如何进行 Spring DAO 自动装配

深度剖析Spring Boot自动装配机制实现原理