5

前言

一年多前,我开始了Java Web学习之旅。这个旅程是断断续续的,因为它与我的工作领域没有太多重合。最近看了很多Java和Spring的资料,然而离感觉自己学会了,仍有一点距离。因此,就继续之前的Servlet和JSP博客系列吧。

本系列将会覆盖到Spring的基础以及Spring MVC, 当然还有Spring Boot. 除了开始介绍 Spring及其配置的文章,后面将都使用 Spring Boot。

同时,本系列将不是照本宣科的学习,还会有一些思考和讨论。否则,很容易陷入一堆配置的泥潭,越学越糊涂。做好准备,我们开始吧!

Spring简介

本段内容引自这里, 很棒的博客!

Spring框架由Rod Johnson开发,第一版于2004年发布。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。

Spring总结起来优点如下:

  • 低侵入式设计,代码的污染极低。
  • 独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺。
  • Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦。
  • Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用。
  • Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
  • Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部。
  • 蓬勃的社区,Spring全家桶,你懂的。

spring

依赖注入

Spring的历史不多谈,但提到Spring,一定会提到它的核心容器。你可能还听过另一个词:IoC,控制反转。这个词显然更加不知所云。
IoC是更早提出的概念,它是OO设计模式的一种。而在IoC之前,还有个设计原则,叫依赖倒置原则(DIP)。

依赖倒置原则:高层模块不应该依赖低层模块,而两个都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

关于它们的前世今生,这篇文章讲的非常好。

总结起来就是:

  • DIP强调要面向接口编程,因此使用者最好只持有被使用者的接口的引用。
  • IoC进一步指出创建所依赖的对象这个动作也不应该由使用者负责,那就要交给第三方处理。
  • DI (依赖注入)是IoC的一种实现。常见方式是构造函数注入属性注入
  • IoC 容器是功能更加强大的依赖注入框架,可以实现通过配置文件配置依赖,以及对象的生命周期管理等等。

使用容器是必须的吗?

显然,对于Java Web来说,使用Spring容器别无选择。以至于会让一些人认为这是天经地义的。然而放眼其他语言,几乎都有各自的容器框架,却很少有像Spring这样得到了普遍的应用。甚至,在我们公司的.NET开发规范中,明确指出了Unity这种容器框架过重,不推荐使用。以下是一些讨论(在讨论之前,我们先明确一点 —— IoC 不等于 IoC容器):

  • “使用容器可以方便单元测试”。并不是,只要面向接口编程,多提供几个构造函数,就可以很方便的使用Mock做单元测试。
  • “创建依赖对象有时过于复杂,交给容器即可”。创建对象可以交给工厂模式解决。
  • "容器可以集中配置,而且只修改XML,不编译就可以切换应用策略"。额,这些功能使用工厂+配置文件+反射也可以做。况且,使用XML配置依赖真的好吗?那为什么Spring 4.0之后改为推荐使用代码配置呢?
  • “使用容器可以方便地管理对象生命周期,方便实现单例模式”。 额,算是一个小理由。
  • "使用容器可以方便实现AOP,否则很难实现一个动态代理"。这算是一个比较好的理由。

而使用容器的缺点也显而易见,如今到处都是Annotation,根本不是集中配置。需要记住的约定一大堆,出了问题还要排查是不是Spring注入的错。在代码中定位依赖也同样很不直观。

以下是网上的一些讨论,并非为了撕,只是提供更全面的认识。
知乎
水木社区

最后,既来之则安之,下面我们正式开始。

Bean与容器

JavaBean、EJB、POJO这些东西又有很多历史可以扯。具体参见这里。结论就是,在Spring容器里,Bean就是POJO。也就是说:

  • Spring容器是一个超级大工厂(和仓库),负责创建、管理所有的Java对象,这些Java对象被称为Bean。
  • Spring容器管理容器中Bean之间的依赖关系,Spring使用"依赖注入"来管理Bean之间的依赖关系。

Spring容器的入口

Spring通过应用上下文(Application Context)来行使容器的工作。Spring自带了多种类型的应用上下文实现,每种都以具体的类表示。下面是常用的几个:

  • AnnotationConfigApplicationContext :从Java配置类中加载Spring应用上下文。
  • AnnotationConfigWebApplicationContext :从Java配置类中加载Spring Web应用上下文。
  • ClassPathXmlApplicationContext :从ClassPath下的XML配置文件中加载Spring应用上下文。
  • FileSystemXmlApplicationContext :从文件系统的XML配置文件中加载Spring应用上下文。
  • XmlWebApplicationContext :从Web应用中的XML配置文件中加载Spring Web应用上下文。

既然是入口类,使用它们只有一种方式:直接或间接地new出一个实例。如下是一个简单的例子:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User u = (User)context.getBean("user");
System.out.println(u);

Spring配置的三种方案

Spring的配置方案随着历史的演进,支持以下三种:

  • 在XML中显式配置
  • 隐式的bean自动扫描和自动装配
  • 在Java中显式配置

XML配置是最老的技术;使用Java类配置是最新的推荐。而自动扫描和自动装配使用XML和Java都可以指定。

使用Java显式配置

  • 使用@Configuration注解创建配置类。
  • 使用@Bean注解声明Bean。

隐式的bean自动扫描和自动装配

所谓装配(wiring)就是指创建所依赖的对象的行为,即依赖注入。自动装配就是容器推断出所依赖的具体对象,创建它并设置好使用类。
组件扫描则是指Spring可以扫描应用中有特殊注解的类,为其创建Bean。

  • 通过@Component注解标记可被发现的类。当介绍到Spring Web部分时,我们还会看到其他几个相似的注解。
  • 通过@ComponentScan注解启用组件扫描。
  • 通过@Autowired注解开启自动装配。另外Spring也重用了JDK中的@Resource注解。两者有微小的差异。

使用XML显式配置

使用Java Config能做到的在XML中也能做到。例如:

  • <bean class="xxx"/>声明bean。
  • <context:component-scan base-package="yyy"/> 启用组件扫描。
  • <bean autowire="true"/>开启自动装配。

Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下五种作用域:

  1. Singleton: 单例模式,在整个Spring容器中,singleton作用域的Bean将只生成一个实例。
  2. Prototype: 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
  3. Request: 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
  4. Session: 对于一次HTTP会话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP会话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
  5. Global session: 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。

如果不指定Bean的作用域,Spring默认使用Singleton作用域。Prototype作用域的Bean的创建、销毁代价比较大。而Singleton作用域的Bean实例一旦创建成功,就可以重复使用。因此,应该尽量避免将Bean设置成Prototype作用域。


Toconscience
153 声望39 粉丝