Spring--更好地使用IoC

Spring IoC在绝大部分的业务系统中都有所使用,但是往往使用方式只是类似@Resource这样的注解注入Bean,这个是IoC最基本的功能。那么IoC作为容器自然还有批量获取,泛型注入等更为强大的功能,如何使用这些功能是本文所要探讨的内容。

IoC容器访问接口

Spring IoC所提供的access接口有以下几种,作为使用方只需要引用最底层的ApplicationContext,即可获取到IoC容器所提供的所有访问方式。这里可以多思考一下软件设计中接口的设计,我的理解ApplicationEventPublisher,EnvironmentCapable则属于功能接口,是某一功能对外的入口,比如ApplicationEventPublisher为事件发布的入口。BeanFactory则属于类别接口,其定义了一类功能,然后在使用子接口把功能拆分具体的功能接口,比如ListableBeanFactory提供IoC枚举Bean的功能,HierarchicalBeanFactory提供IoC容器父子关系查询功能。最后ApplicationContext是一个复合接口,其面向用户提供了不同纬度的功能的聚合。

BeanFactory

BeanFactory是Spring IoC的顶层接口,其提供了IoC容器获取单个bean的功能,可以根据名称,也可以根据类型。如果一个类型有多个实现类则会抛出NoUniqueBeanDefinitionException异常。

1
2
3
4
BeanFactory factory = (BeanFactory) applicationContext;
UserService userService1 = (UserService) factory.getBean("userService");
UserService userService2 = factory.getBean(UserService.class);
System.out.println(userService1==userService2);

HierarchicalBeanFactory

该接口提供Spring IoC获取上层IoC的能力,该功能在业务系统中不是很常见。

1
2
3
4
5
/**
* Return the parent bean factory, or {@code null} if there is none.
*/
@Nullable
BeanFactory getParentBeanFactory();

ListableBeanFactory

针对IoC容器,一个接口往往有着多个实现类,业务系统中时常需要获取到某个接口的全部实现,ListableBeanFactory就提供了这样的枚举功能。

1
2
3
4
5
// 根据类型获取到全部的实现类
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException
// 根据注解获取到全部被标识的类
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;

ApplicationContext

ApplicationContext是用户访问所使用的接口,其除了上述功能外,提供了一个获取工厂写入能力的方法,在Spring中bean往往是单例存在的,当遇到非单例的需求时就需要依赖这个接口手动创建需要的bean对象。

1
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException

使用场景

IoC最本质的目的是解耦接口与实现的强关联,那么在写业务的时候也是需要考虑到这一点的,比如我要为某一个重要的业务入口写校验,简单的做法是直接在业务主逻辑之前一个一个的写,这样带来的问题是校验与主逻辑相互耦合,扩展的时候则需要再次修改这里。
这种时候就要想到IoC的思想,IoC的思想解决思路是校验提取成一个服务接口,然后在主业务中依赖接口,具体的每一个校验逻辑服务从IoC中获取,当新增一个校验服务时,则只需要让该服务被IoC管理即可,这样扩展性又好,逻辑也更加清晰。
下面举两个设计模式上常用到的例子。

观察者模式

观察者模式在业务系统中使用很频繁,比如在订单创建之后要调用各个子模块的监听方法,那么订单创建就是一个Subject,每一个监听策略就是Observe,达到逻辑解耦的目的。在观察者模式中Subject需要获取到全部的Observe,然后在行为产生时依次调用每一个Observe的通知方法,达到通知目的。那么Subject中如何获取到全部的Observe?
常见的做法是在Observe实例化时主动向Subject注册自己,那么Observe必须持有Subject,换句话说必须知道自己的Subject是哪个具体的实现类。有了IoC之后,两者则可以通过IoC解耦,Observe只需要往IoC里面注入,Subject在实例化时从IoC中加载全部的Observe即可,简要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class Subject implements ApplicationListener<ContextRefreshedEvent> {

private List<Observe> observes;

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
// 获取到全部的观察者
Map<String, Observe> allObsver = context.getBeansOfType(Observe.class, false, true);
observes = new ArrayList<>(allObsver.values());
}
}

这只是简要的代码,在IoC中能获取到对应的全部 实例,实例的使用则是非常灵活的,可以组成链,也可以组成hash。

策略模式

策略模式讲究的是不同场景下策略的替换,而IoC相当于一个策略池,使用什么策略直接从中获取即可,简要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class StrategyContext implements ApplicationListener<ContextRefreshedEvent> {

private Map<StrategyEnum, Strategy> allStrategy;

private static final Object lockInit = new Object();

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
Map<String, Strategy> strategyPool = context.getBeansOfType(Strategy.class, false, true);
synchronized (lockInit) {
if (allStrategy == null) {
allStrategy = new HashMap<>(strategyPool.size());
}
strategyPool.forEach((k,v) -> {
allStrategy.put(v.getSupport(), v);
});
}
}
}

参考

IoC容器和Dependency Injection模式

Apache--common-cli工具解析
学习笔记--Hbase