为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程; 通过切面和模板减少样板式代码。
依赖注入
任何一个有实际意义的应用(肯定比Hello World示例更复杂)都会由两个或者更多的类组成,这些 类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作 的对象(即它所依赖的对象)的引用,这将会导致高度耦合和难以测试的代码。
耦合具有两面性(two-headed beast)。一方面,紧密耦合的代码难以测试、难以复用、难以理 解,并且典型地表现出“打地鼠”式的bug特性(修复一个bug,将会出现一个或者更多新的bug)。 另一方面,一定程度的耦合又是必须的——完全没有耦合的代码什么也做不了。为了完成有实际意 义的功能,不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当被小心谨慎地 管理。
通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对 象无需自行创建或管理它们的依赖关系,如图所示,依赖关系将被自动注入到需要它们的对象 当中去。
依赖注入会将所依赖的关系自动交给目标对象,而不是让对象自己去获取依赖
Spring通过应用上下文(Application Context)装载bean的定义并把它们组装起来。Spring应用上 下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现,它们之间主要的区别仅仅在于如何加载配置。
当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:
- 在XML中进行显式配置。
- 在Java中进行显式配置。
- 隐式的bean发现机制和自动装配。
Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
- 单例(Singleton):在整个应用中,只创建bean的一个实例。
- 原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的 bean实例。
- 会话(Session):在Web应用中,为每个会话创建一个bean实例。
- 请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
应用切面
DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming,AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程往往被定义为促使软件系统实现关注点的分离一项技术。系统由许多不同的组件组 成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外 的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中 去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
如果将这些关注点分散到多个组件中去,你的代码将会带来双重的复杂性。
- 实现系统关注点功能的代码将会重复出现在多个组件中。这意味着如果你要改变这些关注点 的逻辑,必须修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
- 组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的方法 应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。
AOP能够使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造成的 结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务 所带来复杂性。总之,AOP能够确保POJO的简单性。
我们可以把切面想象为覆盖在很多组件之上的一个外壳。应用是由那些实现各自业 务功能的模块组成的。借助AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式 灵活地应用到系统中,你的核心应用甚至根本不知道它们的存在。这是一个非常强大的理念,可以 将安全、事务和日志关注点与核心业务逻辑相分离。
使用应用上下文
Spring自带了多种类型的应用上下文。下面罗列的几个是你最有可能遇到的。
- AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载 Spring应用上下文。
- AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加 载Spring Web应用上下文。
- ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载 上下文定义,把应用上下文的定义文件作为类资源。
- FileSystemXmlapplicationcontext:从文件系统下的一个或多个XML配置文件中加 载上下文定义。
- XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文定义。
SpringMVC
SpringMVC流程在请求离开浏览器时 ,会带有用户所请求内容的信息,至少会包含请求的URL。但是还可能带有 其他的信息,例如用户提交的表单信息。 请求旅程的第一站是Spring的DispatcherServlet。与大多数基于Java的Web框架一 样,Spring MVC所有的请求都会通过一个前端控制器(front controller)Servlet。前端控制器是常 用的Web应用程序模式,在这里一个单实例的Servlet将请求委托给应用程序的其他组件来执行实 际的处理。在Spring MVC中,DispatcherServlet就是前端控制器。 DispatcherServlet的任务是将请求发送给Spring MVC控制器(controller)。控制器是一个用 于处理请求的Spring组件。在典型的应用程序中可能会有多个控制器,DispatcherServlet需 要知道应该将请求发送给哪个控制器。所以DispatcherServlet以会查询一个或多个处理器映 射(handler mapping) 来确定请求的下一站在哪里。处理器映射会根据请求所携带的URL信息来 进行决策。 一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器 。到了控制 器,请求会卸下其负载(用户提交的信息)并耐心等待控制器处理这些信息。(实际上,设计良好 的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一个或多个服务对象进行处理。)
控制器在完成逻辑处理后,通常会产生一些信息,这些信息需要返回给用户并在浏览器上显示。这 些信息被称为模型(model)。不过仅仅给用户返回原始的信息是不够的——这些信息需要以用户 友好的方式进行格式化,一般会是HTML。所以,信息需要发送给一个视图(view),通常会是 JSP。
控制器所做的最后一件事就是将模型数据打包,并且标示出用于渲染输出的视图名。它接下来会将 请求连同模型和视图名发送回DispatcherServlet 。 这样,控制器就不会与特定的视图相耦合,传递给DispatcherServlet的视图名并不直接表示 某个特定的JSP。实际上,它甚至并不能确定视图就是JSP。相反,它仅仅传递了一个逻辑名称, 这个名字将会用来查找产生结果的真正视图。DispatcherServlet将会使用视图解析器(view resolver) 来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是JSP。 既然DispatcherServlet已经知道由哪个视图渲染结果,那请求的任务基本上也就完成了。它 的最后一站是视图的实现(可能是JSP) ,在这里它交付模型数据。请求的任务就完成了。视图 将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端(不会像听上去那样硬编码)。
可以看到,请求要经过很多的步骤,最终才能形成返回给客户端的响应。
网友评论