工厂模式和 DI 容器有何区别?
1.DI 容器底层最基本的设计思路就是基于工厂模式的。DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪 些其他类对象)事先创建好对象。当应用程序需要使用某个类对象的时候,直接从容器中获取即可。
2.工厂模式中,一个工厂类只负责某个类对象或者某一组相关类对象(继承自同一抽 象类或者接口的子类)的创建,而 DI 容器负责的是整个应用中所有类对象的创建。
3.DI 容器负责的事情要比单纯的工厂模式要多。
DI 容器的核心功能有哪些?
1.配置解析
我们将需要由 DI 容器来创建的类对象和创建类对象的必要信息(使用哪个构造函数以及对 应的构造函数参数都是什么等等),放到配置文件中。容器读取配置文件,根据配置文件提 供的信息来创建对象。
public class RateLimiter {
private RedisCounter redisCounter;
public RateLimiter(RedisCounter redisCounter) {
this.redisCounter = redisCounter; }
public void test() {
System.out.println("Hello World!");
}
//...
}
public class RedisCounter {
private String ipAddress;
private int port;
public RedisCounter(String ipAddress, int port) {
this.ipAddress = ipAddress;
this.port = port;
}
//...
}
配置文件beans.xml:
<beans>
<bean id="rateLimiter" class="com.xzg.RateLimiter">
<constructor-arg ref="redisCounter"/>
</bean>
<bean id="redisCounter" class="com.xzg.redisCounter">
<constructor-arg type="String" value="127.0.0.1">
<constructor-arg type="int" value=1234>
</bean>
</beans>
Spring 容器的配置文件。Spring 容器读取这个配置文件,解析出要创 建的两个对象:rateLimiter 和 redisCounter,并且得到两者的依赖关系:rateLimiter 依 赖 redisCounter。
2.对象创建
如果我们给每个类都对应创建一个工厂类,那项目中类的个数会成倍增加, 这会增加代码的维护成本。我们只需要将所有类对象的创建都放到 一个工厂类中完成,比如 BeansFactory。
3.对象的生命周期管理。
在 Spring 框架中,我 们可以通过配置 scope 属性,来区分这两种不同类型的对象。scope=prototype 表示返 回新创建的对象,scope=singleton 表示返回单例对象。
配置对象是否支持懒加载。如果 lazy-init=true,对象在真正被使用 到的时候(比如:BeansFactory.getBean(“userService”))才被被创建;如果 lazyinit=false,对象在应用启动的时候就事先创建好。
还可以配置对象的 init-method 和 destroy-method 方法
如何实现一个简单的 DI 容器?
配置文件解析、根据配置文件通过“反射”语法来创建对象。
- 最小原型设计
配置文件beans.xml
<beans>
<bean id="rateLimiter" class="com.xzg.RateLimiter">
<constructor-arg ref="redisCounter"/>
</bean>
<bean id="redisCounter" class="com.xzg.redisCounter" scope="singleton" lazy-
<constructor-arg type="String" value="127.0.0.1">
<constructor-arg type="int" value=1234>
</bean>
</beans>
最小原型的使用方式跟 Spring 框架非常类似
public class Demo {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext "beans.xml");
RateLimiter rateLimiter = (RateLimiter) applicationContext.getBean("rateLim rateLimiter.test();
//...
}
}
2. 提供执行入口
ApplicationContext
是接口,ClassPathXmlApplicationContext
是接口的实现类。
public interface ApplicationContext {
Object getBean(String beanId);
}
public class ClassPathXmlApplicationContext implements ApplicationContext {
private BeansFactory beansFactory;
private BeanConfigParser beanConfigParser;
public ClassPathXmlApplicationContext(String configLocation) { this.beansFactory = new BeansFactory();
this.beanConfigParser = new XmlBeanConfigParser(); loadBeanDefinitions(configLocation);
}
private void loadBeanDefinitions(String configLocation) {
InputStream in = null;
try {
in = this.getClass().getResourceAsStream("/" + configLocation);
if (in == null) {
throw new RuntimeException("Can not find config file: " + configLocatio }
List beanDefinitions = beanConfigParser.parse(in);
beansFactory.addBeanDefinitions(beanDefinitions);
} finally {
if (in != null) {
try { in.close();
} catch (IOException e) {
// TODO: log error
}
}
}
}
@Override public Object getBean(String beanId) {
return beansFactory.getBean(beanId);
}
}
3. 配置文件解析
配置文件解析主要包含 BeanConfigParser
接口和 XmlBeanConfigParser
实现类,负责 将配置文件解析为 BeanDefinition
结构,以便 BeansFactory
根据这个结构来创建对象。
public interface BeanConfigParser {
List parse(InputStream inputStream);
List parse(String configContent); }
public class XmlBeanConfigParser implements BeanConfigParser { @Override
public List parse(InputStream inputStream) {
String content = null;
// TODO:...
return parse(content); }
@Override
public List parse(String configContent) {
List beanDefinitions = new ArrayList<>();
// TODO:...
return beanDefinitions;
}
}
public class BeanDefinition {
private String id;
private String className;
private List constructorArgs = new ArrayList<>();
private Scope scope = Scope.SINGLETON;
private boolean lazyInit = false;
// 省略必要的getter/setter/constructors
public boolean isSingleton() {
return scope.equals(Scope.SINGLETON); }
public static enum Scope { SINGLETON, PROTOTYPE }
public static class ConstructorArg {
private boolean isRef; private Class type; private Object arg; // 省略必要的getter/setter/constructors
}
}
4. 核心工厂类设计
BeansFactory
创建对象用到的主要技术点就是 Java 中的反射语法:一种动态加 载类和创建对象的机制,它负责根据从配置文件解析得到的 BeanDefinition
来创建对象。
public class BeansFactory {
private ConcurrentHashMap singletonObjects = new ConcurrentHa
private ConcurrentHashMap beanDefinitions = new Concu
public void addBeanDefinitions(List beanDefinitionList) {
for (BeanDefinition beanDefinition : beanDefinitionList) { this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition) }
for (BeanDefinition beanDefinition : beanDefinitionList) {
if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) createBean(beanDefinition);
}
}
}
public Object getBean(String beanId) {
BeanDefinition beanDefinition = beanDefinitions.get(beanId);
if (beanDefinition == null) {
throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId) }
return createBean(beanDefinition); }
@VisibleForTesting
protected Object createBean(BeanDefinition beanDefinition) {
if (beanDefinition.isSingleton() && singletonObjects.contains(beanDefinitio return singletonObjects.get(beanDefinition.getId());
}
Object bean = null;
try { Class beanClass = Class.forName(beanDefinition.getClassName());
List args = beanDefinition.getConstructorA if (args.isEmpty()) { bean = beanClass.newInstance();
} else { Class[] argClasses = new Class[args.size()];
Object[] argObjects = new Object[args.size()];
for (int i = 0; i < args.size(); ++i) { BeanDefinition.ConstructorArg arg = args.get(i);
if (!arg.getIsRef()) { argClasses[i] = arg.getType();
argObjects[i] = arg.getArg();
} else { BeanDefinition refBeanDefinition = beanDefinitions.get(arg.getArg()
if (refBeanDefinition == null) {
throw new NoSuchBeanDefinitionException("Bean is not defined: " + } argClasses[i] = Class.forName(refBeanDefinition.getClassName()); argObjects[i] = createBean(refBeanDefinition); } } bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTarget throw new BeanCreationFailureException("", e);
}
if (bean != null && beanDefinition.isSingleton()) { singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
return singletonObjects.get(beanDefinition.getId());
}
return bean;
}
}