轻量级容器与控制反转
在轻量级容器出现之前,开发一个典型的企业应用有两种选择,要么使用EJB,要么使用原生web应用的方式。通过前一篇,我们知道了EJB的各种弊端,下面我来看看原生web应用又会存在什么问题呢?
原生web应用是怎么样的?
- 创建一个Servlet来相应浏览器的请求;
- 在Servlet中直接new一个Service,然后执行数据的CRUD;
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
DemoService demoService = new DemoService();
demoService.getDemo();
super.doGet(req, resp);
}
}
原生web应用有什么问题?
- 重复创建对象的问题:这里会面临一个问题,如果每一次请求都new一个Service,就会不断的创建大量的Service对象,而这些对象在创建后,很快就要被销毁(Service对象在这次请求完成后就没有用处了)。这不管是对内存的大小要求,还是对垃圾回收的性能都提出了很高的要求。
- 单例模式泥潭:所以就产生出这样一种设计,整个系统中只创建一个Service实例,每个Servlet中都使用这一个实例就行了(这里假设Service是状态无关的)。所以早期的实现方法就是在Service类中通过编写代码的方式来实现单例模式,从而导致大量的重复代码(单例模式泥潭)。而且因为单例模式中会用到锁机制,性能也会有潜在问题。
- 依赖具体类,而不是接口:这是和OO原则严重违背的,当然也可以通过工厂模式之类的方法来避免直接依赖具体类,但是同样会导致大量的辅助性代码,不仅带来了生产效率的低下,也让可维护性很难得到保证。
Spring的解决办法
Spring充分吸收了EJB的容器理念,同时摈弃了EJB容器对容器内对象的层层束缚(容器内对象必须实现各种接口)。
- 容器单例:所以Spring中设计了容器的概念,通过容器来管理业务对象。当应用代码中需要一个对象时,只需要去容器中获取,容器就可以保证每次获取到的,都是同一个业务对象。通过这种方式,既简单又为后续的其他特性提供了基础。
- 控制反转:控制反转是容器自然而然就会衍生出来的一种技术——由容器通过配置的方式来决定运行时对象之间的依赖关系。在控制反转技术中有一种特殊的用法——依赖注入,这是一种对象的装配技术,容器在初始化某个实例时,会根据配置文件中的描述信息,来把对象初始化需要的其它对象进行自动的初始化并注入当前对象中。
- 轻量级容器:轻量级是相对于EJB这种重量级容器来说的,相信现在的Spring5也没人会说它是一个轻量级的容器了吧。但是轻量级除了代码量、框架的复杂度方面,还有一个重要的特性就是容器对业务代码的侵入性,这方面其实才是Spring的容器中最核心的设计思想。
容器的特性
所谓容器,是指应用代码运行的框架。应用代码在容器里面运行,就是我们常说的“被容器管理”。Spring这个容器除了上述所说的三个方面的特性以外,还需要一些特性来让这个容器更完善、易用。综合起来这个容器主要有以下几个特性:
- 生命周期管理:容器用于控制运行其中的业务对象的生存周期,首先将创建对象的逻辑从使用者那里抽离出来。其次,更精密的容器还应该提供某些需要在对象激活、或者容器销毁时需要调用的机制。
- 查找服务:容器应该提供某种途径,用于获得收管对象的引用。容器的查找功能将“对业务对象的实现细节的了解”从使用者那里抽离出来,将其隐藏在容器内部。查找服务的容器的核心服务,也就是说,容器的核心就是一个工厂。
- 配置管理:容器需要提供统一的方法来配置运行其中的对象,并且允许对象的参数化。
- 依赖决议:容器不仅可以管理String、int这种简单类型的配置,还可以管理各个对象之间的关系。