Spring教程04-自动化装配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 {
    @Override
    public void drink() {
        System.out.println("*******喝咖啡*********");
    }
}

需要说明的是这里可以不定制接口。

<?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;
    }
    @Test
    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

@Component("milkCup")
public class MilkCupServiceImpl implements CupService {
    @Override
    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;
    }
    @Test
    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就这种情况也给出了相应的解决方案:

@Configuration
@ComponentScan(basePackageClasses = {MarkBean.class})
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 {
    @Autowired
    private CupService colaCupServiceImpl;
    @Autowired
    private CupService coffeeCupServiceImpl;
    @Override
    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 {
    @Autowired
    private CupService colaCupServiceImpl;
    private CupService coffeeCupServiceImpl;
    private ShopService smallShopServiceImpl;
    @Override
    public void drink() {
        colaCupServiceImpl.drink();;
        coffeeCupServiceImpl.drink();
    }
    @Autowired
    public ManServiceImpl(CupService coffeeCupServiceImpl) {
        this.coffeeCupServiceImpl = coffeeCupServiceImpl;
    }
    @Autowired(required = false)
    public void setSmallShopServiceImpl(ShopService smallShopServiceImpl) {
        this.smallShopServiceImpl = smallShopServiceImpl;
    }
}

这三种使用方式其效果都是一样的,当然我们也可以使用到其他普通方法上进行Bean的装配,与@Component类似,Java依赖注入规范也提供了一个注解来自动装配Bean,演示一哈:

@Named
public class JavaAnnotationManServiceImpl implements PersonService {
    @Inject
    private CupService colaCupServiceImpl;
    private CupService coffeeCupServiceImpl;
    @Override
    public void drink() {
        colaCupServiceImpl.drink();;
        coffeeCupServiceImpl.drink();
    }
    @Inject
    public JavaAnnotationManServiceImpl(CupService coffeeCupServiceImpl) {
        this.coffeeCupServiceImpl = coffeeCupServiceImpl;
    }
}

以上基本上涵盖了Spring自动化装配Bean的所有套路,下次我们来探讨使用Java代码装配Bean。

更多最新技术文章,请关注“冰点IT”公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值