22.6 注解的应用:DI容器
22.6 注解的应用:DI容器
我们再来看一个简单的DI容器的例子。我们引入两个注解:一个是@SimpleInject;另一个是@SimpleSingleton,先来看@SimpleInject。
1. @SimpleInject
引入一个注解@SimpleInject
,修饰类中字段,表达依赖关系,定义为:
1 |
|
我们看两个简单的服务ServiceA和ServiceB, ServiceA依赖于ServiceB,它们的定义如代码清单22-1所示。
1 | public class ServiceA { |
ServiceA使用@SimpleInject
表达对ServiceB的依赖。
DI容器的类为SimpleContainer,提供一个方法:
1 | public static <T> T getInstance(Class<T> cls) |
应用程序使用该方法获取对象实例,而不是自己new,使用方法如下所示:
1 | ServiceA a = SimpleContainer.getInstance(ServiceA.class); |
SimpleContainer.getInstance会创建需要的对象,并配置依赖关系,其代码为:
1 | public static <T> T getInstance(Class<T> cls) { |
代码假定每个类型都有一个public默认构造方法,使用它创建对象,然后查看每个字段,如果有SimpleInject注解,就根据字段类型获取该类型的实例,并设置字段的值。
2. @SimpleSingleton
在上面的代码中,每次获取一个类型的对象,都会新创建一个对象,实际开发中,这可能不是期望的结果,期望的模式可能是单例,即每个类型只创建一个对象,该对象被所有访问的代码共享,怎么满足这种需求呢?我们增加一个注解@SimpleSingleton
,用于修饰类,表示类型是单例,定义如下:
1 |
|
我们可以这样修饰ServiceB:
1 |
|
SimpleContainer也需要做修改,首先增加一个静态变量,缓存创建过的单例对象:
1 | private static Map<Class<? >, Object> instances = new ConcurrentHashMap<>(); |
getInstance也需要做修改,如下所示:
1 | public static <T> T getInstance(Class<T> cls) { |
首先检查类型是否是单例,如果不是,就直接调用createInstance创建对象。否则,检查缓存,如果有,直接返回,如果没有,则调用createInstance创建对象,并放入缓存中。
createInstance与第一版的getInstance类似,代码为:
1 | private static <T> T createInstance(Class<T> cls) throws Exception { |
本章介绍了Java中的注解,包括注解的使用、自定义注解和应用示例,示例的完整代码在github上,地址为https://github.com/swiftma/program-logic ,位于包shuo.laoma.dynamic. c85下。
注解提升了Java语言的表达能力,有效地实现了应用功能和底层功能的分离,框架/库的程序员可以专注于底层实现,借助反射实现通用功能,提供注解给应用程序员使用,应用程序员可以专注于应用功能,通过简单的声明式注解与框架/库进行协作。
下一章,我们来探讨Java中一种更为动态灵活的机制:动态代理。