1.依赖注入
Java应用,作为一个分散的概念,被广泛应用多个领域,不论是内嵌系统还是服务端企业系统,都是通过很多对象相互协作来完成应用功能的。因为,各对象是彼此依赖的。
尽管Java平台提供了丰富的应用开发功能,但是它缺少能将各个基础模块组织成一个整体的方法,把这个整合工作丢给架构师或者开发者去完成。尽管可以使用设计模式,比如工厂模式(Factory),抽象工厂模式(Abstract Factory),构建者模式(builder),装饰器模式(decorator)以及服务定位(Service Locator)来组装各种类以及对象实例,以此来构建应用,但是,这些设计模式仅仅简单描述了一个名字,说明模式是什么,在哪里可以应用,它解决的问题是什么以及等等。模式最好的实践就是在应用中亲自实现它们。
Spring FrameWork的控制反转(IOC)组件解决以上的问题。IoC通过提供一种标准化的方式来组织各个不同的组件到一个工程应用程序中以备使用。Spring Framework编写了一套标准的设计模式作为first-class对象,你可以将这些对象整合到你自己的应用中。许多组织以及研究机构以这种方式使用Spring Framework,以此来构建强壮的,可维护的应用。
2.Framework模块
Spring Framework按特点分成了大约20个模块。这些模块可以被分组为Core Container, Date Access/Integration, Web, AOP, Instrumentation, Messaging, Test,如下图所示
接下来,会分别介绍以上各个模块的特点,它们的artifact names和它们所覆盖的主题。artifact names与使用依赖管理工具的artifact IDs相关。
2.1 Core Container
Core Container 包含 spring-core,spring-beans,spring-context, spring-context-support,spring-expression这些模块。
spring-core和spring-beans模块提供了框架的基础部分,包含控制反转(Ioc)以及依赖注入功能。BeanFactory是工厂模式的复杂实现。它将配置以及依赖定义从你的实际程序逻辑中解耦开来。
spring-context模块依赖Core和Beans模块。它是以框架模式获取对象的一种方式,类似于JNDI注册。Context模块从Beans模块继承它的特点,并且支持了事件传播,资源加载,透明的上下文创建,类似于Servlet容器。Context模块也支持Java EE的特点,比如EJB, JMX和基础远程调用。ApplicationContext接口是Context模块的核心。
spring-context-support提供了整合公共第三方库到Spring应用中的能力,比如缓存(EhCache, Guava, JCache), 邮件(JavaMail),调度(CommonJ, Quartz)以及模板引擎(FreeMarker, JasperReports, Veloctiy)。
spring-expression 模块提供了强大的Expression语言支持,主要用于在运行期间查询和操作对象图。它是unified EL的扩展。该语言支持属性值的get和set,属性分配,方法调用,数组,集合,索引的内容获取,逻辑和算术操作,命名变量,从Spring的Ioc容器中通过名字检索对象。它也支持列表投影和选择,就像公共的列表集合一样。
2.2 AOP 和 Instrumentation
spring-aop模块提供一种面向切面程序实现,允许开发者定义方法拦截器和切片点来解耦那些应当被隔离的功能代码实现。通过使用源代码级别的元数据函数,开发者也可以把行为信息整合到代码里,类似于.NET的属性。
2.3 Messaging
Spring FrameWork4 包含了spring-messaging 模块。该模块从Spring集成项目,比如Message, MessageChannel, MessageHandler以及其他用来服务基于消息的应用中进行了关键抽象。该模块也提供一系列注解用来将信息映射到方法中,类似于Spring MVC注解。
2.4 Data Access/Integration
Data Access/Integration层包含了JDBC, ORM, OXM, JMS 以及 事务模块。
spring-jdbc 模块提供一个JDBC抽象层,该模块使得开发者不用编写冗长的JDBC代码以及分析各种数据库异常代码。
spring-tx模块支持类级别的编程式和声明式事务管理。这些类实现了特定的接口并且针对所有的POJO对象。
spring-orm模块提供了针对主流对象关系映射APIs的整合,包括JPA, JDO以及Hibernate。通过spring-orm模块,开发者可以使用以上所有的O/R-mapping框架,并结合Spring提供的其他特点,比如事务声明式管理。
spring-jms模块包含了生产和消费消息的功能。从Spring-Framework4.1之后, 它提供了spring-messaging模块整合。
2.5 Web
Web层包含了spring-web,spring-webmvc, spring-websocket以及spring-webmvc-portlet模块。
spring-web模块提供了面向web的基础整合特点,比如多重文件上传功能,使用Servlet监听器来初始化Ioc容器,面向web的应用上下文。它同样包含HTTP客户端和Spring远程支持的web相关部分。
spring-webmvc模块(也被称为Web-Servlet模块)包含了Spring的model-view-controller以及REST Web Services实现。Spring MVC框架提供领域模型代码,Web表单的解耦以及Spring框架其他特点的整合。
spring-webmvc-portlet模块,也被称为Web-Portlet模块,提供了应用在Portlet环境的MVC实现和基本Servlet的spring-webmvc模块的镜像功能。
2.6 Test
sprinig-test模块通过JUnit或者TestNG来支持Spring组建的单元测试以及整体测试。它提供了Spring AppcationContext加载以及这些上下文缓存。它也提供mock对象。开发者可以使用mock对象来测试代码。
3.使用场景
前面所述的各种基础模块使得Sring在很多场景下都是一个可靠的选择,从运行在资源受限的设备上的嵌入式系统到使用Spring的事务管理和Web整合框架的企业级应用。
3.1 典型的Spring web应用
spring的声明式事务管理使得web应用可以具有事务性,就如你使用EJB container-managed事务。所有业务逻辑都可以通过简单POJOs实现和Spring Ioc容器进行管理。其他的一些服务,比如发送邮件,web层的独立校验也是支持的。Spring orm支持整合了JPA, Hibernate以及JDO;比如,当你使用Hibernate时,你可以继续使用已经存在的映射文件和标准的Hibernate SessionFactory配置。表单控制器使用领域模型整合web层,移除了ActionForms以及需要将HTTP参数转换成领域模型的处理类。
3.2 使用第三方web框架的Spring中间层
有些情况并不允许将系统框架完全转换到另一套框架。Spring框架并不强制开发者使用它所包含的所有模块,Spring并不是要么全部用,要么全部不用的解决方案。通过Struts, Tapestry, JSF以及其他UI框架实现的前端逻辑可以和基于Spring的中间层进行整合。Spring中间层可以使用Spring事务特点。开发者只需要使用ApplicationContext来整合业务逻辑,以及使用WebApplicationContext来整合web层。
3.3 远程使用场景
当你需要通过web服务获取已经存在的代码,你可以使用Spring的Hessian-, Burlap-, Rmi- 以及JaxRpcProxyFactory类。
3.4 EJBs - 包装已经存在的POJOs
Spring框架也提供了对JavaBeans的获取和抽象层,使得开发者可以重新使用已经存在的POJOs,然后把它们包装到无状态的session beans,以此提供可扩展,安全失败的web应用。
4. 日志
尽管Spring提供了许多其他额外工具的整合和依赖,但是它仍然有一个强制的对外依赖,那就是日志。如果使用Maven进行依赖管理,开发者并不需要提供显式的日志依赖。
日志对于Spring来说是一个非常重要的依赖,原因有三:1.日志是Spring的唯一强制外部依赖;2.每个人都乐于从他们使用的工具中看到一些输出结果;3.Spring整合了其他很多工具,这些工具也依赖日志。应用开发者常常希望能够对于整个系统,将日志统一配在一个地方。由于日志框架众多,这就变得十分困难。
在Spring中强制的日志依赖是JCL(Jakarta Commons Logging) API。对于开发者来说,所有Spring的版本使用相同的日志库是十分重要的。为了实现这个目标,Spring其中一个模块显式依赖了commons-logging(JCL的实现),然后其他模块在编译阶段都依赖于该模块。如果你正在使用Maven,不知道在什么地方引入commons-logging依赖,那么可以用使用spring-core。
commons-logging一个非常好的地方就是你不需要任何其他东西来使你的应用工作。它有一个运行时发现算法,在类加载路径来查询其他日志框架,并且使用它认为最合适的日志框架。如果找不到任务框架,它会从JDK中获取日志(JUL)。
4.1 使用Log4j 1.2 or 2.x
很多开发者使用Log4j作为一个日志框架来配置和管理。Log4j非常高效且易用。事实上Log4j也是Spring运行时所使用的框架。Spring也提供了一些实用程序来配置和初始化Log4j,因此在一些Spring模块中,Log是optional complie-time 依赖。
为了使Log4j1.2和默认的JCL依赖一起工作,你需要做的就是在类加载路径放置Log4j,然后提供配置文件,如log4j.properties或者是log4j.xml。如果实用Maven进行依赖管理的话,依赖配置如下:
为了使用Log4j2.x 和 JCL,你需要做的就是引入Log4j依赖以及提供配置文件,如log4j.xml, log4j2.properties以及其他一些配置文件格式。对于Maven用户来说,最小的依赖引入如下:
如果你想使用Slf4j(实现初衷类似于commons-logging)来将具体日志实现委托给Log4j,那么除了引入Slf4j以外,以下的依赖也是必须的:
4.2 避免commons-logging依赖
虽然运行时算法发现使用的是commons-logging API 对终端用户来说十分方便,但是有可能会造成一些问题。如果你不想引入JCL的标准查询,有以下两种方式可以移除:
1.从spring-core模块中移除commons-logging的依赖,因为这是唯一的模块显式依赖了commons-logging;
2.依赖一个特殊的commons-logging,可以用空jar替代库。(这个我也不太理解)
现在由于没有JCL API实现,应用被破坏了。那么为了解决这个问题,我们需要提供另外一个实现。在接下来的内容中,将会介绍如何使用SLF4J来实现JCL。
4.3 利用SLF4J来实现Log4j或者Logback
SLF4J是一个非常流行的API,它的实现目标与commons-logging类似,就是提供一个简单的Log接口,向下可以支持多种日志框架。Logback是SLF4J的本地实现。
SLF4J提供了绑定多个日志框架的支持,它也提供了反向支持:日志框架和它的各种桥接。这种桥接是双向的,比如Log4j可以桥接到SLF4J, SLF4J也可以桥接到Log4j。那么现在为了在Spring中使用SLF4J,需要使用SLF4J-JCL桥替代commons-logging依赖。一旦做了替换,那么Spring中所有对日志的调用都会转换到SLF4J的日志调用api,因此,如果你的应用中其他类库使用SLF4J API,那么你只需要在一个地方配置和管理日志。
为了引入SLF4J,一般需要提供Spring到SLF4J的桥接,显式的引入SLF4J到Log4j的绑定。你需要提供一下几个依赖:当然首先要c从Spring中移除commons-logging,然后引入JCL桥接,SLF4J到Log4j的绑定,Long4j。在Maven中你可以这样引入:
对于使用SLF4J用户来说,更常用的日志框架是logback。由于logback本身就是SLF4J的直接实现,所以开发者不需要引入其他一些额外的依赖,你只需要引入jcl-over-slf4j和logback的jar包即可。
4.4 JUL使用(java.util.logging)
当系统在类加载路径探测不到其他log4j框架时,会默认将日志交给java.util.logging处理。因此这种情况下不需要设置特殊的依赖。
4.5 WebSphere日志
Spring应用可能会运行在一个容器上,该容器自身提供了JCL实现,比如IBM的WebSphere应用服务器(WAS)。这个并不会导致问题,但是有两个不同的场景需要注意:
在"parent first"类加载委托模型(WAS默认实现),应用常常会选择服务器提供的日志版本,委托给WAS日志子系统(也是基于JUL)。那么应用提供的各种JCL的实现,不论是标准的Commons Logging还是JCL-over-SLF4J桥接,以及各种日志实现,都会被忽视。
在"parent last"类加载委托模型(常规的Servlet容器默认实现),应用提供的各种JCL实现就会被采纳,开发者可以选择具体的日志实现,不论是log4j还是logback。在没有任何日志实现框架的情况下,系统会默认委托给JUL处理,正如前面提到的"parent first"场景下Websphere的日志子系统所采用的日志实现方式。
总而言之,我们推荐部署Spring应用程序采用"parent last"模式,因为它既可以支持应用选择的日志实现也可以实现服务器的日志子系统。
资料来源:
https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/htmlsingle/#overview-usagescenarios
关于各个日志实现框架关系可参照:https://zhuanlan.zhihu.com/p/24272450