1. Overview
1.概述
In this tutorial, we’ll learn how to use the Spring @Import annotation while clarifying how it’s different from @ComponentScan.
在本教程中,我们将学习如何使用Spring @Import注解,同时阐明它与@ComponentScan的区别。
2. Configuration and Beans
2.配置和Bean
Before understanding the @Import annotation, we need to know what a Spring Bean is and have a basic working knowledge of the @Configuration annotation.
在理解@Import注解之前,我们需要知道什么是Spring Bean,并对@Configuration注解有基本的工作知识。
Both topics are out of this tutorial’s scope. Still, we can learn about them in our Spring Bean article and in the Spring documentation.
这两个主题都不在本教程的范围内。但是,我们仍然可以在我们的Spring Bean文章和Spring文档中了解到它们。
Let’s assume that we already have prepared three beans – Bird, Cat, and Dog – each with its own configuration class.
让我们假设我们已经准备了三个bean – Bird, Cat, 和 Dog – 每个bean都有自己的配置类。
Then, we can provide our context with these Config classes:
然后,我们可以用这些Config类提供我们的上下文。
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { BirdConfig.class, CatConfig.class, DogConfig.class })
class ConfigUnitTest {
@Autowired
ApplicationContext context;
@Test
void givenImportedBeans_whenGettingEach_shallFindIt() {
assertThatBeanExists("dog", Dog.class);
assertThatBeanExists("cat", Cat.class);
assertThatBeanExists("bird", Bird.class);
}
private void assertThatBeanExists(String beanName, Class<?> beanClass) {
Assertions.assertTrue(context.containsBean(beanName));
Assertions.assertNotNull(context.getBean(beanClass));
}
}
3. Grouping Configurations with @Import
3.用@Import将配置分组
There’s no problem in declaring all the configurations. But imagine the trouble to control dozens of configuration classes within different sources. There should be a better way.
声明所有的配置是没有问题的。但是想象一下在不同的资源中控制几十个配置类的麻烦吧。应该有一个更好的方法。
The @Import annotation has a solution, by its capability to group Configuration classes:
@Import 注解有一个解决方案,它能够对Configuration类进行分组。
@Configuration
@Import({ DogConfig.class, CatConfig.class })
class MammalConfiguration {
}
Now, we just need to remember the mammals:
现在,我们只需要记住那些哺乳动物。
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { MammalConfiguration.class })
class ConfigUnitTest {
@Autowired
ApplicationContext context;
@Test
void givenImportedBeans_whenGettingEach_shallFindOnlyTheImportedBeans() {
assertThatBeanExists("dog", Dog.class);
assertThatBeanExists("cat", Cat.class);
Assertions.assertFalse(context.containsBean("bird"));
}
private void assertThatBeanExists(String beanName, Class<?> beanClass) {
Assertions.assertTrue(context.containsBean(beanName));
Assertions.assertNotNull(context.getBean(beanClass));
}
}
Well, probably we’ll forget our Bird soon, so let’s do one more group to include all the animal configuration classes:
好吧,可能我们很快就会忘记我们的Bird,所以让我们再做一个组,包括所有的动物配置类。
@Configuration
@Import({ MammalConfiguration.class, BirdConfig.class })
class AnimalConfiguration {
}
Finally, no one was left behind, and we just need to remember one class:
最后,没有人被落下,我们只需要记住一个班级。
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { AnimalConfiguration.class })
class AnimalConfigUnitTest {
// same test validating that all beans are available in the context
}
4. @Import vs @ComponentScan
4.@Import与@ComponentScan对比
Before proceeding with @Import examples, let’s have a quick stop and compare it to @ComponentScan.
在进行@Import的例子之前,让我们先停一下,把它和@ComponentScan进行比较。
4.1. Similarities
4.1.相似性
Both annotations can accept any @Component or @Configuration class.
两个注解都可以接受任何@Component或@Configuration 类。
Let’s add a new @Component using @Import:
让我们使用@Import添加一个新的@Component。
@Configuration
@Import(Bug.class)
class BugConfig {
}
@Component(value = "bug")
class Bug {
}
Now, the Bug bean is available just like any other bean.
现在,BugBean就像其他Bean一样可用。
4.2. Conceptual Difference
4.2.概念上的差异
Simply put, we can reach the same result with both annotations. So, is there any difference between them?
简单地说,我们可以通过两种注释达到相同的结果。那么,它们之间有什么区别吗?
To answer this question, let’s remember that Spring generally promotes the convention-over-configuration approach.
要回答这个问题,让我们记住,Spring通常提倡约定俗成的配置方法。
Making an analogy with our annotations, @ComponentScan is more like convention, while @Import looks like configuration.
与我们的注释进行类比,@ComponentScan更像是惯例,而@Import看起来像配置。
4.3. What Happens in Real Applications
4.3.在实际应用中发生了什么
Typically, we start our applications using @ComponentScan in a root package so it can find all components for us. If we’re using Spring Boot, then @SpringBootApplication already includes @ComponentScan, and we’re good to go. This shows the power of convention.
通常情况下,我们在启动应用程序时使用根包中的@ComponentScan,这样它就能为我们找到所有组件。如果我们使用的是Spring Boot,那么@SpringBootApplication已经包含了@ComponentScan,我们就可以开始了。这显示了惯例的力量。
Now, let’s imagine that our application is growing a lot. Now we need to deal with beans from all different places, like components, different package structures, and modules built by ourselves and third parties.
现在,让我们想象一下,我们的应用程序正在大量增长。现在我们需要处理来自各个不同地方的Bean,比如组件、不同的包结构以及由我们自己和第三方构建的模块。
In this case, adding everything into the context risks starting conflicts about which bean to use. Besides that, we may get a slow start-up time.
在这种情况下,把所有的东西都加入到上下文中,就有可能在使用哪个Bean的问题上开始冲突。除此之外,我们可能会得到一个缓慢的启动时间。
On the other hand, we don’t want to write an @Import for each new component because doing so is counterproductive.
另一方面,我们不想为每个新的组件写一个@Import,因为这样做会适得其反。
Take our animals, for instance. We could indeed hide the imports from the context declaration, but we still need to remember the @Import for each Config class.
以我们的动物为例。我们确实可以从上下文声明中隐藏导入,但我们仍然需要记住每个Config类的@Import。
4.4. Working Together
4.4.共同工作
We can aim for the best of both worlds. Let’s picture that we have a package only for our animals. It could also be a component or module and keep the same idea.
我们可以争取做到两全其美。让我们想象一下,我们有一个只针对我们的动物的包。它也可以是一个组件或模块,并保持同样的想法。
Then we can have one @ComponentScan just for our animal package:
然后我们可以有一个@ComponentScan,只为我们的animal包。
package com.baeldung.importannotation.animal;
// imports...
@Configuration
@ComponentScan
public class AnimalScanConfiguration {
}
And an @Import to keep control over what we’ll add to the context:
还有一个@Import,以保持对我们将添加到上下文的内容的控制。
package com.baeldung.importannotation.zoo;
// imports...
@Configuration
@Import(AnimalScanConfiguration.class)
class ZooApplication {
}
Finally, any new bean added to the animal package will be automatically found by our context. And we still have explicit control over the configurations we are using.
最后,任何添加到动物包的新Bean都会被我们的上下文自动找到。而且我们仍然可以明确控制我们所使用的配置。
5. Conclusion
5.总结
In this quick tutorial, we learned how to use @Import to organize our configurations.
在这个快速教程中,我们学习了如何使用@Import来组织我们的配置。
We also learned that @Import is very similar to @ComponentScan, except for the fact that @Import has an explicit approach while @ComponentScan uses an implicit one.
我们还了解到,@Import与@ComponentScan非常相似。除了@Import有一个显式的方法,而@ComponentScan使用一个隐式方法。
Also, we looked at possible difficulties controlling our configurations in real applications and how to deal with these by combining both annotations.
此外,我们还研究了在实际应用中控制我们的配置可能遇到的困难,以及如何通过结合两种注释来处理这些问题。
As usual, the complete code is available over on GitHub.
像往常一样,完整的代码可以在GitHub上找到,。