Spring实战第一章学习笔记

Java开发的简化

为了降低Java开发的复杂性,Spring采取了以下四种策略:

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码;

1. 激发POJO的潜能

POJO简单的Java对象,实际上就是普通JavaBean。

Spring不会因为自身API而弄乱了你的应用代码,它不会让你实现Spring规范的接口或继承Spring规范的类,所以在Sora构建的类中,它的类没有使用Spring的痕迹。Spring的非侵入性编程意味着这个类在Spring应用和非Spring应用中都可以发挥同样的作用。虽然形式看起来很简单,Spring应用到POJO的方式之一就是通过依赖注入来装配它们。

2. 依赖注入(DI)

DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

●谁依赖于谁:当然是应用程序依赖于IoC容器;

  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

那DI功能是如何实现的?一个有实际意义的应用通常都具有两个以上的类。按照传统做法每个对象管理与自己相互协作的对象的引用。这会导致各组件的高度耦合且代码难以测试以至于牵一发而动全身。


public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
} public void embarkOnQuest() {
quest.embark();
} }

这时可以看到DamselRescuingKnight在构造函数中自行创建了RescueDamselQuest,使得无法保证embark方法被调用,所以DameRescuingKnight无法通过测试。这样创建出的骑士执行探险能力被限制了。所以这时我们要通过DI使得对象的依赖关系由第三方组件在创建对象时设定。这样对象无需自行创建或管理他们的依赖关系,而如下面的程序所示依赖关系将被自动注入到需要他们的对象中。

public class BraveKnight implements Knight {

  private Quest quest;

  public BraveKnight(Quest quest) {
this.quest = quest;
} public void embarkOnQuest() {
quest.embark();
} }

这段代码中将探险任务quest作为构造器参数传入这是依赖注入的方式之一,及构造器注入。更重要的是传递的参数类型是Quest,也就是所有的探险任务都必须实现的一个接口。所以BraveKnight可以响应任意的Quest实现。且重点是它没有和任一特定类型的Quest耦合。这是DI带来的最大收益——松耦合。如果对象只通过接口来表明依赖关系,那么这种依赖可以在对象本身毫不知情的情况下以各种实现来进行替换。对BraveKnight进行测试可以验证embark方法被调用了一次。

创建应用组件之间的协作的行为通常称为装配。Spring有多种装配bean的方法。而在下面提供的是基于Java的配置。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import sia.knights.BraveKnight;
import sia.knights.Knight;
import sia.knights.Quest;
import sia.knights.SlayDragonQuest; @Configuration
public class KnightConfig { @Bean
public Knight knight() {
return new BraveKnight(quest());
} @Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
} }

Spring通过应用上下文(ApplicationContext)装载bean的定义并把它们组装起来。Spring应用上下文全权负责对象的创建和组装。Spring自带了多种引用上下文的实现,他们之间主要的区别在于如何加载配置。因为我是使用Java文件配置的所以选择AnnotationConfigApplicationContext作为应用上下文。

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class KnightMain {

  public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
"KnightConfig.class");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
} }

这里main()方法基于KnightConfig.class文件创建了Spring应用上下文。当得到Knight对象的引用后,只需简单调用embarkOnQuest()方法就可以执行探险。且这个类完全不知道这是由BraveKnight来执行的,更不知道执行的是什么探险,只有KnightConfig。class文件才知道。

3. 应用切面

DI能够让相互协作的组件保持松散耦合,而面向切面编程(accept-oriented programming,AOP)允许你把遍布应用各处的功能组件分离出来形成可重用的组件。

AOP可以使得这些服务模块化,并以声明的方式将它们应用到相应的组件中去,这样,这些组件就具有更高内聚性以及更加关注自身业务,完全不需要了解可能涉及的系统服务的复杂性。总之,AOP确保POJO保持简单。

接下来我们回到骑士的例子,来示范如何在Spring中应用切面。每一个人都熟知骑士所做的事迹是因为吟游诗人用诗歌记载并进行传唱。所以需要一个吟游诗人这样的服务类Minstrel。

import java.io.PrintStream;

public class Minstrel {

  private PrintStream stream;

  public Minstrel(PrintStream stream) {
this.stream = stream;
} public void singBeforeQuest() {
stream.println("Fa la la, the knight is so brave!");
} public void singAfterQuest() {
stream.println("Tee hee hee, the brave knight " +
"did embark on a quest!");
} }

如代码所示诗人会在骑士探险前和探险后被调用,所以骑士要调用诗人的方法来歌颂。可当我们将BraveKnight和Minstrel组合起来后发现BraveKnight代码复杂化了。我们发现如果这样实现首先骑士需要管理吟游诗人,同时骑士还要知道吟游诗人所以必须将吟游诗人注入到BraveKnight类中,同时我们还要考虑Minstrel是否会为空。所以这时我们利用AOP将Minstrel抽象为一个切面。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="knight" class="com.spring.sample.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean> <bean id="quest" class="com.spring.sample.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean> <bean id="minstrel" class="com.spring.sample.knights.Minstrel">
<constructor-arg value="#{T(System).out}" />
</bean> <aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/>
<aop:before method="singBeforeQuest" pointcut-ref="embark" />
<aop:after method="singAfterQuest" pointcut-ref="embark" />
</aop:aspect>
</aop:config>
</beans>

在这个配置文件中增加了aop配置名字空间。首先定义Minstrel的bean,然后利用aop:config标签定义aop相关的配置;然后在aop:aspect节点中引用minstrel,定义方面;aspect负责将pointcut和要执行的函数(before、after或者around)连接在一起。在之后会详细介绍AOP的内容。

4. 使用模板消除样板式代码

在编写代码时我们经常会用大量重复无用的代码来实现简单的功能,最典型的就是JDBC的使用。

public Employee getEmployeeById(long id) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement("select id, name from employee where id=?");
stmt.setLong(1, id);
rs = stmt.executeQuery();
Employee employee = null;
if (rs.next()) {
employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setName(rs.getString("name"));
}
return employee;
} catch (SQLException e) {
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
}
return null;
}

而通过Spring的模板封装可以消除样板式代码。

public Employee getEmployeeById(long id) {
return jdbcTemplate.queryForObject(
"select id, name from employee where id=?",
new RowMapper<Employee>() {
public Employee mapRow(ResultSet resultSet, int i) throws SQLException {
Employee employee = new Employee();
employee.setId(resultSet.getLong("id"));
employee.setName(resultSet.getString("name"));
return employee;
}
});
}

容纳你的Bean

在基于Spring的应用中,应用对象生存于Spring容器中,Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期。容器是Spring框架的核心。Spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。

而Spring容器的实现可分为两类:

  • Bean factories(由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,只提供基本的依赖注入功能;
  • Application context(由org.springframework.context.ApplicationContext接口定义)在bean factory的基础上提供application-framework框架服务,例如可以从properties文件中解析配置信息、可以对外公布application events。

使用应用上下文

Spring自带多种类型的应用上下文。最常见的如下:

  • AnnotationConfigApplicationContext——从Java配置文件中加载应用上下文;
  • AnnotationConfigWebApplicationContext——从Java配置文件中加载Spring web应用上下文;
  • ClassPathXmlApplicationContext——从classpath(resources目录)下加载XML格式的应用上下文定义文件;
  • FileSystemXmlApplicationContext——从指定文件系统目录下加载XML格式的应用上下文定义文件;
  • XmlWebApplicationContext——从classpath(resources目录)下加载XML格式的Spring web应用上下文。

通过应用上下文实例,可以通过getBean()方法获得对应的bean。

bean的生命周期

传统的Java应用中bean的生命周期很简单。实例化一个bean后一旦不使用就由Java自动进行垃圾回收。

而在Spring中:

  1. Spring初始化bean;
  2. Spring将值和其他bean的引用注入(inject)到当前bean的对应属性中;
  3. 如果Bean实现了BeanNameAware接口,Spring会传入bean的ID来调用setBeanName方法;
  4. 如果Bean实现了BeanFactoryAware接口,Spring传入bean factory的引用来调用setBeanFactory方法;
  5. 如果Bean实现了ApplicationContextAware接口,Spring将传入应用上下文的引用来调用setApplicationContext方法;
  6. 如果Bean实现了BeanPostProcessor接口,则Spring调用postProcessBeforeInitialization方法,这个方法在初始化和属性注入之后调用,在任何初始化代码之前调用;
  7. 如果Bean实现了InitializingBean接口,则需要调用该接口的afterPropertiesSet方法;如果在bean定义的时候设置了init-method属性,则需要调用该属性指定的初始化方法;
  8. 如果Bean实现了BeanPostProcessor接口,则Spring调用postProcessAfterInitialization方法
  9. 在这个时候bean就可以用于在应用上下文中使用了,当上下文退出时bean也会被销毁;
  10. 如果Bean实现了DisposableBean接口,Spring会调用destroy()方法;如果在bean定义的时候设置了destroy-method, 则此时需要调用指定的方法。

总结

关于第一章就先总结这么多,前两节已经清晰地介绍了Spring的功能特性。其中依赖注入和AOP是Spring框架最核心的两部分,而第一章只是介绍了点皮毛,之后几章将深入探讨。

Spring实战第一章学习笔记的更多相关文章

  1. #Spring实战第二章学习笔记————装配Bean

    Spring实战第二章学习笔记----装配Bean 创建应用对象之间协作关系的行为通常称为装配(wiring).这也是依赖注入(DI)的本质. Spring配置的可选方案 当描述bean如何被装配时, ...

  2. Spring实战第九章学习笔记————保护Web应用

    保护Web应用 在这一章我们将使用切面技术来探索保护应用程序的方式.不过我们不必自己开发这些切面----我们将使用Spring Security,一种基于Spring AOP和Servlet规范的Fi ...

  3. Spring实战第八章学习笔记————使用Spring Web Flow

    Spring实战第八章学习笔记----使用Spring Web Flow Spring Web Flow是一个Web框架,它适用于元素按规定流程运行的程序. 其实我们可以使用任何WEB框架写流程化的应 ...

  4. AS开发实战第二章学习笔记——其他

    第二章学习笔记(1.19-1.22)像素Android支持的像素单位主要有px(像素).in(英寸).mm(毫米).pt(磅,1/72英寸).dp(与设备无关的显示单位).dip(就是dp).sp(用 ...

  5. Day1 《机器学习》第一章学习笔记

    <机器学习>这本书算是很好的一本了解机器学习知识的一本入门书籍吧,是南京大学周志华老师所著的鸿篇大作,很早就听闻周老师大名了,算是国内机器学习领域少数的大牛了吧,刚好研究生做这个方向相关的 ...

  6. [蛙蛙推荐]SICP第一章学习笔记-编程入门

    本书简介 <计算机程序的构造与解释>这本书是MIT计算机科学学科的入门课程, 大部分学生在学这门课程前都没有接触过程序设计,也就是说这本书是针对编程新手写的. 虽然是入门课程,但起点比较高 ...

  7. 《Django By Example》第一章 学习笔记

    首先看了下目录,在这章里 将会学到 安装Django并创建你的第一个项目 设计模型(models)并且生成模型(model)数据库迁移 给你的模型(models)创建一个管理站点 使用查询集(Quer ...

  8. 《Java基础教程》第一章学习笔记

    Java 是什么呀! 计算机语言总的来说分成机器语言,汇编语言,高级语言.其中Java一种高级计算机语言,它是一种可以编写跨平台应用软件,完全面向对象的程序设计语言. Java划分为三个技术平台,Ja ...

  9. Scala第一章学习笔记

    面向对象编程是一种自顶向下的程序设计方法.用面向对象方法构造软件时,我们将代码以名词(对象)做切割,每个对象有某种形式的表示服(self/this).行为(方法).和状态(成员变量).识别出名词并且定 ...

随机推荐

  1. C#解析HTML神器 Html Agility Pack

    曾经,我傻乎乎的用正则表达式成功的解析了学校的新闻网.教务管理系统.图书馆管理系统中我想要的所有的内容.那时候废了好大的劲写那正则啊,而且最后还是各种不给力,经常会有意想不到的bug出现,最后经过无数 ...

  2. requireJs sass侧边栏

    koala 下载 将sass编译为css文件 1 嵌套 2 $变量 3 @mixin 函数名(参数) 4 @include 函数    调用 5 @import "xxx"   引 ...

  3. Redis集群整合到springboot框架

    整合步骤 1 配置application.properties spring.redis.cluster.nodes=192.168.60.131:8000,192.168.60.131:8001,1 ...

  4. SpringBoot非官方教程 | 第二十六篇: sprinboot整合elk,搭建实时日志平台

    转载请标明出处: 原文首发于https://www.fangzhipeng.com/springboot/2017/07/11/sprinboot25-elk/ 本文出自方志朋的博客 这篇文章主要介绍 ...

  5. SpringBoot非官方教程 | 第二十四篇: springboot整合docker

    转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springboot/2017/07/11/springboot24-docker/ 本文出自方志朋的博客 这篇文 ...

  6. SpringBoot非官方教程 | 第十七篇:上传文件

    转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springboot/2017/07/11/springboot14-upload/ 本文出自方志朋的博客 这篇文 ...

  7. ATK系列库说明

    初衷 重构和复用是软件的一个古老话题. 在日常的软件项目开发的过程序中,如何保证团队代码的强健,同时在不断变化的需过程中最大限度的保障代码的一致性,是项目开发中的难以控制的,我们可以借助各种源码管理和 ...

  8. LVS NAT,DR,TUN三种负载原理

    负载均衡简单介绍 用通俗的话来说负载均衡,就是通过不同的调度机制将用户的请求分派到后端不同的服务器.缓解服务器的请求压力,实现负载均衡的方案有多种,下面简单说说了解的几种方式: DNS 负载:利用DN ...

  9. 因为之前完全没有接触过Spring,所以准备先把spring实战看完再落实项目

    因为之前完全没有接触过Spring,所以准备先把spring实战看完再落实项目

  10. 23种java设计模式之装饰者模式及动态代理

    设计模式不管对于何种语言都是存在的,这里介绍的是java的模式 装饰者模式是在二次开发中应用比较多的一款模式,当然了用反射也是可以实现的,今天介绍的是装饰模式,有兴趣的朋友可以自己去了解一下反射是怎么 ...