美文网首页Java 杂谈JAVA学习记录
《Spring实战》-第二章:Bean的装配(3)-自动化装配

《Spring实战》-第二章:Bean的装配(3)-自动化装配

作者: 廖小明的赖胖子 | 来源:发表于2019-03-06 00:12 被阅读6次

慢慢来比较快,虚心学技术

前言:创建应用对象之间协作关系的行为通常称为装配( wiring ),这也是依赖注入( DI )的本质

Spring提供三种Bean装配机制:

  1. 在 XML 中进行显式配置。
  2. 在 Java 中进行显式配置
  3. 隐式的 bean 发现机制和自动装配

一、什么是自动化装配?

很显然,通过前两篇文章的描述,我们可以发现,Spring的配置方式可以很简洁,也可以也很复杂,JavaConfig配置方式使用大量的注解替代了XML中的配置,那么,基于JavaConfig的基础之上,是否可以再次封装或简化配置呢?

Spring实现了纯注解配置的自动化隐式装配,所谓的隐式装配就是不需要像XML和JavaConfig一样去为每一个Bean创建<bean>节点或再配置类中为每个Bean做注解,而是通过一些特殊的注解实现控制

其中包含两个概念:

  • 组件扫描( component scanning ): Spring 会自动发现应用上下文中所创建的 bean 。
  • 自动装配( autowiring ): Spring 自动满足 bean 之间的依赖。【DI】

二、如何实现自动化装配

ⅰ.四个基本注解

@Autowired :标记于属性,方法等,自动装配的关键注解,依赖注入的表现,该注解可以自动寻找并从Spring容器中提取使用该注解的bean并注入到对应的属性中去

@Component :标记于类,标明当前类是一个可被扫描的组件

@ComponentScan :标记于配置类,开启组件注解扫描

@Configuration :标记于配置类,标明当前类是一个配置类

ⅱ.基本实现:

①定义基本接口CDPlayer

public interface CDPlayer {

    /**
     * 定义方法播放CD
     *  @param
     *       
     * @return void
     *       
     * @author lai.guanfu 2019/2/27
     * @version 1.0
     **/
    void playCD();

}

②定义基本类CDBean

@Data//Data是lobok的注解,自动添加setter和getter方法等
@Component//将当前类定义为可扫描的组件
public class CDBean {
    /**
     * 定义CD名
     */
    private String title="The World!";

    /**
    * 定义CD作者
    */
    private String author="Mr.D";
}

③定义实现类CDPlayerImpl,并将CDBean作为属性注入

@Component//将当前类定义为可扫描的组件
public class CDPlayerImpl implements CDPlayer {
    /**
     * 从Spring容器注入CDBean-----依赖注入
     */
    @Autowired
    private CDBean cdBean;

    @Override
    public void playCD() {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

④定义配置类CDConfig,并开启注解扫描

@ComponentScan()//开启注解扫描
@Configuration//指定当前类为配置类
public class CDConfig {}

⑤编写测试

此处使用Spring提供的测试类SpringJUnit4ClassRunner帮助测试,以便在测试开始的时候自动创建 Spring 的应用上下文

而@ContextConfiguration可以指定创建上下文的加载方式以及配置的位置等

@RunWith(SpringJUnit4ClassRunner.class)//辅助创建Spring应用上下文
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {CDConfig.class})//指定创建上下文的加载方式以及配置的位置
public class AppTest 
{
    /**
     * 从Spring容器注入CdPlayer-----依赖注入
     */
    @Autowired
    private CDPlayer cdPlayer;

    /**
     * Rigorous Test :-)
     */
    @Test
    public void play()
    {
        //调用playCD方法测试是否自动装配及依赖注入成功
        this.cdPlayer.playCD();
    }
}

⑥测试结果,自动装配成功,依赖注入成功

正在播放:The World! by Mr.D

ⅲ.关注点:

① 提供另一种测试方式:通过上一篇文章中提及的AnnotationConfigApplicationContext上下文实现进行测试

public static void main(String[] args) {
    /**
     * 注解配置实现,同时指定配置类的位置,否则无法读取配置进行扫描,默认只有Spring自带的组件
     */
     ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CDConfig.class);
     /**
      *获取应用上下文中的所有Bean名称
      */
     String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
     for (String className : beanDefinitionNames){
       System.out.println(className);
     }
     /**
      *从上下文(容器)中获取对应的Bean
      */
    CDPlayer cdPlayer = applicationContext.getBean(CDPlayer.class);
    /**
     *调用具体方法测试是否成功
     */
    cdPlayer.playCD();
}

测试结果:自动装配成功,依赖注入成功

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
CDConfig
app//当前测试类的名字
CDBean
CDPlayerImpl
正在播放:The World! by Mr.D

为装配的Bean命名

Spring 应用上下文中所有的 bean 都会给定一个 ID,默认是类名的首字母小写命名,我们可以为装配的Bean装配一个不一样的ID,在 @Component注解中有一个属性value,该值可指定bean在Spring容器中的ID,其在@Component注解的源码定义如下:

public @interface Component {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    */
   String value() default "";

}

我们可以给CDPlayerImpl指定一个别的ID:cdPlayer

@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {

    @Autowired
    private CDBean cdBean;

    @Override
    public void playCD() {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

此时执行上一测试代码结果中,CDPlayerImpl在容器中的ID类名将变成cdPlayer

另:可以使用@Name代替@Component注解为Bean命名,但是为了避免概念模糊,建议使用@Component注解

设置组件扫描的基础包

上述代码中,配置类CDConfig仅能扫描自身所在包及其子包的组件,能否指定扫描某个或多个包及其子包中的组件呢?

我们看到@ComponentScan注解的源码中定义的几个属性:

public @interface ComponentScan {

   /**
    * Alias for {@link #basePackages}.
    * <p>Allows for more concise annotation declarations if no other attributes
    * are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
    * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
    */
   @AliasFor("basePackages")
   String[] value() default {};

   /**
    * Base packages to scan for annotated components.
    * <p>{@link #value} is an alias for (and mutually exclusive with) this
    * attribute.
    * <p>Use {@link #basePackageClasses} for a type-safe alternative to
    * String-based package names.
    */
    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages} for specifying the packages
     * to scan for annotated components. The package of each class specified will be scanned.
     * <p>Consider creating a special no-op marker class or interface in each package
     * that serves no purpose other than being referenced by this attribute.
     */
    Class<?>[] basePackageClasses() default {};
} 

从源码得知,我们可以通过配置basePackages和basePackageClasses属性来指定配置扫描的范围,其中:

basePackages指定组件扫描的包路径(使用{}包含的数组)

@ComponentScan(basePackages = {"com.my.spring","com.my.test"})//指定扫描com.my.spring包和com.my.test包及其子包下的组件
@Configuration
public class CDConfig {}

basePackageClasses:指定以该类数组所在包及其子包为组件扫描范围

@ComponentScan(basePackageClasses = {App.class})//指定扫描App.class所在包及其子包下的组件
@Configuration
public class CDConfig {}

注:通常,为了防止因包路径更改以及业务实体类更改等因耦合而产生的问题,我们通常会使用一个不具备任何业务意义的空接口作为扫描包的类

自动注入注解@Autowired的使用

可以用在属性,构造函数,setter以及任何一个普通方法中

@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
    //1.在属性注入
    @Autowired
    private CDBean cdBean;

    public CDPlayerImpl(){super()};

    //2.在构造函数中注入
    @Autowired
    public CDPlayerImpl(CDBean cdBean){super()};

    //3.在setter方法中注入
     @Autowired
    public void setCDBean(CDBean cdBean){this.cdBean = cdBean};

    //4.在普通方法中使用
    @Autowired
    @Override
    public void playCD(CDBean cdBean) {
        System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
    }
}

总结

1.自动化装配使用全注解方式替代XML和JavaConfig显式装配方式,方便简洁

2.自动化装配依赖于几个特殊注解:@Autowired,@Component,@ComponentScan和@Configuration

3.@Component注解可以将类定义为装配的组件,同时可以为改组件另起别名(ID)

4.@Configuration将使用该注解的当前类标注为配置类,@ComponentScan开启自动扫描,默认扫描当前配置类所在的包及其子包中含有或使用了@Component注解的类,可通过属性指定扫描范围

5.@Autowired注解可以用在属性,构造函数,setter以及任何一个普通方法中,是Spring依赖注入的核心注解

参考文档

【1】《Spring 实战(第 4 版)》·Craig Walls

相关文章

  • Spring装配Bean

    spring三种方式装配bean: 自动化装配bean 借助java来进行bean装配 使用xml进行bean装配...

  • spring

    IOC Bean的装配 自动化装配 自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,...

  • Spring IOC学习(01)Bean的装配

    内容概览 简化Java开发 Spring Bean Spring Bean生命周期 装配Bean的可选方案 自动化...

  • Spring装配bean的三种方式

    所有内容参考《spring 实战》,作为学习笔记使用。 spring装配bean主要有三种方式,(1) 自动装配 ...

  • Spring -- 装配Bean(Part 1/3)

    《Spring 实战》 读书笔记 第二章 Spring配置的可选方案 隐式的bean发现机制和自动装配(本章) 在...

  • Spring -- 装配Bean(Part 2 /3)

    《Spring 实战》 读书笔记 第二章 Spring配置的可选方案 隐式的bean发现机制和自动装配 在Java...

  • Spring之旅(三):Spring 装配

    Bean装配 bean装配有哪几种方式 如何使用javaConfig进行显示装配 如何使用自动化装配 什么是装配 ...

  • 05 装配bean

    三种方式:1 自动化装配 bean2 通过java代码装配3 通过xml装配

  • Spring 与 IoC(第二讲)

    Bean 的装配 举例:Spring_Bean_Assemble 项目。Bean 的装配,即 Bean 对象的创建...

  • 第二章 装配bean

    装配Bean [TOC] Spring装配bean的可选方案 装配:创建应用对象之间协作关系的行为通常称为装配,这...

网友评论

    本文标题:《Spring实战》-第二章:Bean的装配(3)-自动化装配

    本文链接:https://www.haomeiwen.com/subject/nthzuqtx.html