翻代码的时候,发现一段@Autowired注解写在Map上,遂点过去又翻了下@Autowired注解的源码,有如下这么一段说明

<h3>Autowiring Arrays, Collections, and Maps</h3>
<p>In case of an array, {@link java.util.Collection}, or {@link java.util.Map}
dependency type, the container autowires all beans matching the declared value
type. For such purposes, the map keys must be declared as type {@code String}
which will be resolved to the corresponding bean names. Such a container-provided
collection will be ordered, taking into account
{@link org.springframework.core.Ordered Ordered} and
{@link org.springframework.core.annotation.Order @Order} values of the target
components, otherwise following their registration order in the container.
Alternatively, a single matching target bean may also be a generally typed
{@code Collection} or {@code Map} itself, getting injected as such.

简单翻一下,大意是 @Autowired注解可以用来注入 Array、Collection和 Map这三种类型的变量,选择类型对应的type来注入的。

其中Map的key必须被定义为String类型,Map的key会被用来保存Bean的name。

Collection类型的在注入的时候会根据@Order注解来对内容进行排序,如果没有@Order注解,则会根据注册顺序决定。

即使只有一个匹配的类型的bean,也可以这么操作。


根据上面说明的内容,简单写一段代码尝试下,代码如下

定义接口

package com.cheungq.demo.service.autowiredTest.simpleServices;

/**
* SimpleInterface<br>
* @author CheungQ
*/
public interface SimpleInterface {

/**
* 实现类自行做点什么
*/
void doSomeThing();
}

写3个实现类

package com.cheungq.demo.service.autowiredTest.simpleServices;

import org.springframework.stereotype.Component;

/**
* FirstSimpleServiceImpl<br>
* @author CheungQ
*/
@Component
public class FirstSimpleServiceImpl implements SimpleInterface{
@Override
public void doSomeThing() {
System.out.println("This is the FIRST implement class");
}
}
package com.cheungq.demo.service.autowiredTest.simpleServices;

import org.springframework.stereotype.Component;

/**
* SecondSimpleServiceImpl<br>
* @author CheungQ
*/
@Component
public class SecondSimpleServiceImpl implements SimpleInterface{
@Override
public void doSomeThing() {
System.out.println("This is the SECOND implement class");
}
}
package com.cheungq.demo.service.autowiredTest.simpleServices;

import org.springframework.stereotype.Component;

/**
* ThirdSimpleServiceImpl<br>
* @author CheungQ
*/
@Component
public class ThirdSimpleServiceImpl implements SimpleInterface{
@Override
public void doSomeThing() {
System.out.println("This is the THIRD implement class");
}
}

写一个Service类,并用@Autowired分别修饰Array、Collection和Map类型

package com.cheungq.demo.service.autowiredTest;

import com.cheungq.demo.service.autowiredTest.simpleServices.SimpleInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * AutowiredTestService<br>
 * @author CheungQ
 */
@Service
public class AutowiredTestService {

    @Autowired
    private List<SimpleInterface> simpleServiceList;

    @Autowired
    private SimpleInterface[] simpleServiceArray;

    @Autowired
    private Map<String,SimpleInterface> simpleServiceMap;

    public SimpleInterface getByName(String name){
        System.out.println(Arrays.toString(simpleServiceArray));
        System.out.println(simpleServiceList);
        System.out.println(simpleServiceMap);
        return simpleServiceMap.getOrDefault(name,null);
    }


}

最后写个Controller调用下看下

package com.cheungq.demo.controller;

import com.cheungq.demo.service.autowiredTest.AutowiredTestService;
import com.cheungq.demo.service.autowiredTest.simpleServices.SimpleInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotEmpty;

/**
 * SimpleController<br>
 * @author CheungQ
 */

@RestController
@RequestMapping(value = "simple/")
public class SimpleController {

    @Autowired
    private AutowiredTestService autowiredTestService;

    @RequestMapping(value = "test")
    public void test(@RequestParam @NotEmpty String name) {
        SimpleInterface service = autowiredTestService.getByName(name);
        if (null != service){
            service.doSomeThing();
        }
    }
}

执行代码,并在Service中打个断点,我们看下情况,如下图

可以看到,Spring对Array、Collection和Map类型进行了注入,下一步修改代码,给ThirdSimpleServiceImpl实现类添加@Order注解

package com.cheungq.demo.service.autowiredTest.simpleServices;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
* ThirdSimpleServiceImpl<br>
* @author CheungQ
*/
@Component
@Order(1)
public class ThirdSimpleServiceImpl implements SimpleInterface{
@Override
public void doSomeThing() {
System.out.println("This is the THIRD implement class");
}
}

重启应用,并测试下

Array和Collection类型中的顺序都发生了改变,ThirdSimpleServiceImpl实现类正确的排到了第一位。


简单说下之前做业务开发的时候曾经的一些应用场景。

一个是在批量拉取订单数据系统中,有一个拉取任务配置模块,可以配置任务执行时间片划分参数、分页数、执行线程数、执行的BeanName。

在主要的业务流程中,根据执行的BeanName获取需要执行的bean实例、这些bean实例都要按照约定实现之前约定的接口。接口约定了两个主要的方法,一个是根据给定的参数获取总数据条数,另一个是根据给定的参数按页码拉取同步远端的订单数据。

那么再开发过程中,开发人员需要做的是根据业务需求,实现这个接口,而整个拉取的任务调度则由系统配置,根据指定的bean实例执行。

另一个曾经使用过的场景,在平台活动商户报名系统中。这个系统做得比较灵活点,根据我在这个系统上的开发和使用感受来看,这个系统最初的设计构想可能是想打造成一个低代码平台的模式。为什么会这么说呢?从已经实现的基础框架上来看,各个模块都做成了灵活配置的形式。活动类型可配置,创建活动的时候需要填写的字段可配置,商户报名填写字段可配置,可报名商户资质和商户类型可配置,报名商户、商品相关校验逻辑可配置,报名数据下发下游系统相关逻辑也是可配置的。一整个流程几乎都可以通过配置信息来调整,唯一的问题是,统统都是JSON对象配置,以及部分可在线编辑的代码配置,在可视化方面做的很少,这也导致这一整个的配置工作还是只能是开发人员来操作。稍微有点偏题了。

在这个系统中有一个模块是专门做报名信息校验的,通过配置对应的校验的BeanName,以及对应validate实例的执行顺序、相关校验参数(如字段最大最小值、字符串校验正则等等)这些。这样在执行的时候获取对应bean实例也是这么处理的。