Spring5 自动化装配Bean
Posted 冰点IT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5 自动化装配Bean相关的知识,希望对你有一定的参考价值。
看过Spring官方文档我们就可以发现Spring大体上为我们提供了三种装配Bean的方式:
在XML中进行显示配置。
在Java代码中显示配置。
隐式的bean发现机制和自动装配。
1、自动装配Bean
我们先来探讨Bean的自动装配,要想实现Bean的自动装配需要先认识Spring的两个核心组件:
组件扫描(component scanning): Spring会自动的从应用上下文中发现创建的Bean。
自动装配(Autowring):装配各个Bean之间的依赖关系。
在开发中只有这两个核心组件配合使用,才能真正意义完成Bean的自动装配。
1.1、创建可被发现的Bean
1.1.1、通过@Component定义一个组件Bean
public interface CupService {
void drink();
}
@Component
public class CoffeeCupServiceImpl implements CupService {
public void drink() {
System.out.println("*******喝咖啡*********");
}
}
需要说明的是这里可以不定制接口。
1.1.2、通过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"
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">
<!-- 扫描组件Bean -->
<context:component-scan base-package="com.icypt.learn.service"/>
</beans>
1.1.3、编写测试类
public class TestCoffeeCupService {
public static ClassPathXmlApplicationContext getCtx() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
return ctx;
}
public void testDrinkCoffee() {
CupService cupService = getCtx().getBean("coffeeCupServiceImpl"
, CoffeeCupServiceImpl.class);
cupService.drink();
}
}
运行测试类
22:08:19,273 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
22:08:19,277 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
22:08:19,279 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
22:08:19,302 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'coffeeCupServiceImpl'
*******喝咖啡*********
通过运行结果来看我们已经完成了组件bean的方法调用,总结一下:当Spring上下文(application.xml)加载的时候,Bean扫描组件会自动扫描"com.icypt.learn.service"包下所有的组件Bean并完成实例化,最后通过ClassPathXmlApplicationContext提供的getBean()方法获得该组件Bean的实例完成方法的调用,对于ClassPathXmlApplicationContext的使用我会在后续的学习中详细讲解。
1.2、为组件Bean命名
Spring应用上下文中所有的Bean都有一个给定的ID,在以上定义组件Bean的时候,我们虽然没有为其定义ID,但是Spring会根据类名为其指定一个ID,指定规则就是:将该组件Bean类名称的第一个字母变为小写。
1.2.1、手动指定组件Bean的ID
"milkCup") (
public class MilkCupServiceImpl implements CupService {
public void drink() {
System.out.println("*******喝牛奶*********");
}
}
创建了一个ID为“milkCup”的组件bean。
1.2.2、编写测试方法
@Test
public void testDrinkMilk() {
CupService cupService = getCtx().getBean("milkCup", MilkCupServiceImpl.class);
cupService.drink();
}
运行测试方法
22:57:35,293 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'coffeeCupServiceImpl'
22:57:35,302 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'milkCup'
*******喝牛奶*********
通过打印的日志可以看出我们已经成功为该组件Bean指定ID为“milkCup”。
Spring也支持Java依赖注入规范中所提供的@Named注解作为@Component的替代方案,不过要使用此注解得额引入Java依赖注入规范的包依赖如下:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
1.3、使用@ComponentScan注解
@ComponentCan和XML显示配置中的<context:component-scan base-package="包名1,包名2,…"/>配置项功能相同,是Spring提供的组件扫描的两种不同体现方式。
1.3.1、简单使用@ComponentScan
@Configuration
@ComponentScan
public class BaseConfiguration {
}
对于@ Configuration注解在后续的学习中会详细介绍,其主要功能就是声明该该类为Spring的一个配置类。
1.3.2、编写测试类
public class TestCupServiceByAnnotationConfig {
public AnnotationConfigApplicationContext getAcac() {
//基于注解配置加载Spring应用上下文
AnnotationConfigApplicationContext acac = new AnnotationConfigApplication
Context(BaseConfiguration.class);
return acac;
}
public void testColaService() {
ColaCupServiceImpl ccs = getAcac().getBean("colaCupServiceImpl", ColaCupServiceImpl.class);
ccs.drink();
}
}
同比1.1.3的测试类发现,我们使用@Configuration后,加载Spring上下文的方式也发生了变化。
运行测试类
13:52:55,748 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'colaCupServiceImpl'
13:52:55,749 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'milkCup'
Disconnected from the target VM, address: '127.0.0.1:51038', transport: 'socket'
*********喝可乐***********
可以发现使用@ComponentScan也可以正常的扫描组件Bean,其效果跟在XML配置完全相同,不过需要注意的是@ComponentScan默认扫描该配置类所在包下的所有组件Bean,那么在开发中如果我们想实现配置代码与业务代码完全分离该如何配置呢?
其实我们只需要这样配置即可:
@ComponentScan(basePackages ={"com.icypt.learn.service"} )
public class BaseConfiguration {
}
根据basePackages的类型来看,我们其实可以配置多个扫描包的例如:
@ComponentScan(basePackages ={"com.icypt.learn.service",”com.icypt.repostiory”,…} )
那么通过以上的配置我们就可以实现将配置文件放在同一包下,让后通过@ComponentScan的basePackages属性去指定需要扫描的包名。
可能有些时候我们还会遇到这样一个问题?就是当我们的项目由于某种原因对包名进行了重构,这个时候我们需要将所有涉及到的配置类中的扫描路径都统统修改一遍,显得非常麻烦。其实Spring就这种情况也给出了相应的解决方案:
public class BasePackageClassesConfig {
}
我们可以创建一个标记Bean,这个Bean没有任何的属性,其作用就是告诉ComponentScan它的位置,引导它来扫描其所在目录的所有组件Bean。
1.4、使用@Autowired完成Bean的自动装配
以上所有的示例都是基于一个简单的Bean组件完成对应的功能,但是在实际情况下,由于其业务的复杂度,可能需要各个Bean之间协调配合才能完成某一个功能。
1.4.1、使用@Autowired
public interface PersonService {
void drink();
}
@Component
public class ManServiceImpl implements PersonService {
private CupService colaCupServiceImpl;
private CupService coffeeCupServiceImpl;
public void drink() {
colaCupServiceImpl.drink();;
coffeeCupServiceImpl.drink();
}
}
编写测试方法
@Test
public void testManDrinkService() {
PersonService ms = getAcac().getBean("manServiceImpl", ManServiceImpl.class);
ms.drink();
}
运行结果如下
15:06:20,175 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'manServiceImpl'
15:06:20,199 DEBUG Psupport.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'markBean'
15:06:20,202 DEBUG main support.DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'milkCup'
Disconnected from the target VM, address: '127.0.0.1:53404', transport: 'socket'
*********喝可乐***********
*******喝咖啡*********
由运行结果可以看出我们已经成功的将ColaCupServiceImpl
、CoffeeCupServiceImpl这两个Bean装配到ManServiceImpl这个Bean之中,其装配过程如下:在Spirng加载应用上下文的时候,@ Autowired默认会从Bean容器中找到与属性变量相同的BeanID进行装配,并且这种装配在默认情况下是强制执行的,如果其找不到对应的BeanID则会抛出异常,容器无法成功加载。当然可以手动设置让装配机制非强制执行,这将使用到@Autowired(required = false),默认required=true表示强制装配,如果等于required = false,Spring会尝试去装配,如果没有符合条件的Bean则放弃装配,不影响Spring容器加载。但是这种情况在调用此Bean处一定要做好null判断,否则有可能会抛出空指针异常。
1.4.2、@Autowired使用方式
常用的使用方式有三种:属性、构造方法、setter方法上使用。
@Component
public class ManServiceImpl implements PersonService {
private CupService colaCupServiceImpl;
private CupService coffeeCupServiceImpl;
private ShopService smallShopServiceImpl;
public void drink() {
colaCupServiceImpl.drink();;
coffeeCupServiceImpl.drink();
}
public ManServiceImpl(CupService coffeeCupServiceImpl) {
this.coffeeCupServiceImpl = coffeeCupServiceImpl;
}
false) (required =
public void setSmallShopServiceImpl(ShopService smallShopServiceImpl) {
this.smallShopServiceImpl = smallShopServiceImpl;
}
}
这三种使用方式其效果都是一样的,当然我们也可以使用到其他普通方法上进行Bean的装配,与@Component类似Java依赖注入规范也提供了一个注解来自动装配Bean,演示一哈:
public class JavaAnnotationManServiceImpl implements PersonService {
private CupService colaCupServiceImpl;
private CupService coffeeCupServiceImpl;
public void drink() {
colaCupServiceImpl.drink();;
coffeeCupServiceImpl.drink();
}
public JavaAnnotationManServiceImpl(CupService coffeeCupServiceImpl) {
this.coffeeCupServiceImpl = coffeeCupServiceImpl;
}
}
以上基本上涵盖了Spring自动化装配Bean的所有套路,下次我们来探讨使用Java代码装配Bean。
代码:https://github.com/icypt/spring-learn-rep,欢迎大家star,也欢迎大家提供宝贵的意见或者建议。
以上是关于Spring5 自动化装配Bean的主要内容,如果未能解决你的问题,请参考以下文章