案例上手 Spring 全家桶

《案例上手 Spring 全家桶》课程亮点

  • 从零掌握 Spring 关键技术
  • 68 讲更全面地覆盖 Spring 全家桶核心模块
  • 100+ 段经典代码示例,快速理解 Spring 全家桶要领
  • 3 大项目实战,掌握 Spring 全家桶实际应用
  • 精选 70 道 Spring 高频面试题评估学习成果
  • 免费赠送 16+ 小时的 Spring 实战视频
  • 进入 Spring 技术社群spring bootspring boot


你能收获什么spring boot

  • 掌握 Spring 全家桶核心模块的实际应用
  • 掌握 Spring Boot Web 开发技术
  • 掌握 Spring Boot 集成常用的关系 / 非关系型数据库
  • 掌握 Spring Cloud 微服务开发技术
  • 掌握更多实际工作中的开发技巧
  • 具备 Java 高级开发的技能要求spring boot


适宜人群spring boot

  • 希望提高技术能力的 Java 开发者
  • 希望全面掌握 Spring 全局核心知识的开发者
  • 面向 Spring 微服务架构项目经验不足的开发者spring boot


前置知识spring boot

  • 熟练掌握 Java 核心基础spring boot
  • 熟练掌握 JavaWeb 后端开发技术spring boot


作者介绍spring boot

宁楠,资深 Java 开发工程师、技术总监,互联网讲师、GitChat 认证作者、《Java 零基础实战》图书作者。有多家大型 IT 企业的从业经历,拥有丰富的软件研发、系统架构经验,热衷于知识分享,乐于将自己的行业经验分享给初学者。spring boot


课程背景spring boot

Spring 是当下 Java 行业的开发标准,企业的招聘信息中也越来越多地出现对于 Spring 技术栈开发能力的要求,可以说 Spring 全家桶已经成为高级 Java 开发人员的必备技能。但市面上关于 Spring 全家桶详细全面且实用的内容比较少,尤其对于初学者来讲,学习成本依旧很高。

《案例上手 Spring 全家桶》为初学者提供一站式服务,将 Spring 全家桶的核心模块一次性全部讲清楚,并结合实战案例让读者快速掌握实际开发能力,以输出为结果导向是最高效的学习方法。

希望通过对《案例上手 Spring 全家桶》的学习,让所有需要掌握 Spring 全家桶的读者都能够快速上手,具备使用 Spring 全栈技术进行企业级实战开发的能力成为资深 Java 工程师


课程大纲spring boot

《Spring 全家桶》目录
开篇词:Spring 框架——Java 开发行业的标准
第一部分
Spring 专题
 1-1  Spring 的基本应用——IoC 和 AOP
 1-2  掌握 Spring IoC 两种常规操作,提高工作效率
 1-3  Spring IoC 工厂方法 + 自动装载
 1-4  Spring IoC 基于注解的开发
 1-5  Spring IoC 底层实现
 1-6  Spring 的另一个核心机制 AOP
第二部分
Spring MVC 专题
 2-1  快速搭建第一个 Spring MVC 项目
 2-2  深入探究底层原理,应用更加得心应手
 2-3  Spring MVC 的常用注解
 2-4  搞懂 Spring MVC 数据绑定,让开发更加简单
 2-5  探寻 Spring MVC 视图层的实现机制
 2-6  如何给项目定制数据类型转换器
 2-7  Spring MVC 与主流架构 RESTful 的集成
 2-8  用 Spring MVC 的上传下载机制简化代码
 2-9  数据安全重中之重,Spring MVC 如何处理
 2-10  Spring MVC 表单标签库
 2-11  Spring MVC 国际化
 2-12  JavaWeb 开发的基本操作—EL 表达式
 2-13  EL 表达式的好搭档—JSTL
第三部分
MyBatis 专题
 3-1  速搭建第一个 MyBatis 项目
 3-2  MyBatis 底层实现
 3-3  Mapper.xml 的常用配置
 3-4  MyBatis 逆向工程,简化代码开发
 3-5  MyBatis 延迟加载
 3-6  MyBatis 缓存
 3-7  MyBatis 动态 SQL
 3-8  搭建 Java 常规技术之 SSM 整合
 3-9  Nginx 搭建 Tomcat 集群
第四部分
MongoDB 专题
 4-1  搭建主流 NoSQL MongoDB 环境
 4-2  MongoDB 常用命令
 4-3  Spring Data 集成 MongoDB:MongoTemplate
 4-4  Spring Data 集成 MongoDB:Repository
 4-5  Spring MVC + Layui + Spring Data + MongoDB 项目实战
第五部分
Spring Boot 专题
 5-1  速搭建第一个 Spring Boot 项目
 5-2  Spring Boot 配置文件
 5-3  Spring Boot 整合 JSP
 5-4  Spring Boot 整合 Thymeleaf(上)
 5-5  Spring Boot 整合 Thymeleaf(下)
 5-6  Spring Boot 整合 JdbcTemplate
 5-7  Spring Boot 整合 MyBatis
 5-8  Spring Boot 整合 Spring Data JPA
 5-9  搭建主流 NoSQL Redis 环境
 5-10  Spring Boot 应用中集成 Spring Data Redis
 5-11  Spring Boot 整合 Redis 实现 Session 共享
 5-12  Spring Boot 整合 MongoDB
 5-13  Spring Boot 整合 Spring Security
 5-14  用 Spring Boot 开发一个电商系统
第六部分
Spring Cloud 专题
 6-1  微服务产生的背景
 6-2  搭建微服务系统的核心中枢
 6-3  注册第一个微服务
 6-4  跨服务接口调用神器 RestTemplate
 6-5  微服务调用案例
 6-6  用服务网关统一 URL,开发更简洁
 6-7  Ribbon 负载均衡
 6-8  Spring Cloud Feign 声明式接口调用
 6-9  Hystrix 容错监控机制
 6-10  Spring Cloud Config 本地配置
 6-11  搭建消息中间件 RabbitMQ 环境
 6-12  Spring Cloud Config 远程配置
 6-13  Zipkin 服务跟踪
第七部分
微服务项目实战
 7-1  明确微服务实战项目的详细需求
 7-2  注册中心和配置中心
 7-3  服务提供者 account
 7-4  服务提供者 menu
 7-5  服务提供者 order
 7-6  服务提供者 user
 7-7  服务消费者 clientfeign
* 目前大纲仅供参考,请以实际更新为准

课程内容

开篇词:Spring 框架——Java 开发行业的标准

我是谁

大家好,我叫宁楠,一名撸了多年代码的资深码农,拥有多年软件研发、系统架构经验,历任高级开发工程师 、技术总监。从上大学到现在接触 Java 已有十个年头了,无论是上学期间还是工作之后,身边朋友对我的评价基本是「学习能力强、上手新技术速度快」,这并不是因为我有多聪明,而是需要掌握正确的学习方法。

什么是正确的学习方法?在我看来这个问题要因人而异,我认为好的方法可能并不适用于所有人,每个人都应该找到适合自己的方法。那我的学习方法是什么呢?主要有以下 3 个核心点。

一是我在学习的时候会做深度思考,分析事物的底层原理,并带入到自己当前的学习或工作场景中。比如学到一个新技术,我会思考如果要用它来实现当前的需求,应该怎么做?同时会结合深度思考来做笔记,为了方便查阅,我的笔记都是直接写在书上的,由此,经常会听到借我书看的朋友抱怨:你在书上写那么多字干啥?搞得我都不知道是看你写的字还是看书上的字了,你是没钱买本子吗…

二是学习时一定要以输出结果为目的,我看完书一定要搞出一个成果,可以是一篇技术教程、也可以是一个项目案例。因为只有时刻带着输出结果的思想去学习,才会真正有效,比如写技术教程,首先必须把所有技术点都搞清楚,吸收消化转为自己的东西,然后才能输出。

三是一切以实践为主,我在学习新技术的时候不会把时间花费在理论知识上,首先会大致了解一下技术背景,知道它是做什么的,然后把精力花费在如何使用它,快速实现一个 Demo。这一轮流程走下来,虽说对概念性的内容比较模糊,但是我已经构建起了对技术的直观认知,在此基础上再慢慢打磨细节,就像盖房子,我会先把框架搭起来,再做局部细化,我认为这种方式的效率比较高,至少是适合我的。

一直以来我都在坚持这种学习方法,在学会编程技能的同时也提高了自己深度思考的能力思维认知,对技术的理解一直都在提升,同时也积攒了很多学习笔记,一开始只是自己记,后来开始通过互联网向外输出,比如写博客、公众号等,逐渐积累了一定的读者和关注度,有了一定的影响力之后,就有各种平台抛出合作的橄榄枝,我当然欣然接受,随后就出版了《Java 零基础实战》一书,也输出了一些知识付费内容,包括视频课程和图文课程,比如 GitChat 达人课《案例上手 Spring MVC》。

点击这里了解《Spring 全家桶》

我为什么要写这门课程

什么是 Spring 全家桶

作为一名资深 Java 开发者,与 Spring 打了很多年交道了,真心被这个框架所折服,不光是我,任何一个 Java 开发者都应该有这样的体会。毋庸置疑,Spring 框架目前已经成为 Java 开发行业的标准,Spring 的官方理念也是霸气十足:the source for modern java,意为 Spring 是现代 Java 开发的源头。只要是做 Java 开发的,一定或多或少会接触到 Spring,无论是传统企业还是互联网公司的招聘需求上一定会重点要求具备使用 Spring 框架进行开发的能力。

案例上手 Spring 全家桶_第1张图片

(图片来自 Spring 官网)

Spring 框架从 2002 年诞生至今经过十多年的发展,已经从最初的取代 EJB 这样一个单一功能发展成为一套完整的生态体系,涉及到现代软件开发的各个方面。

核心模块有哪些

作为开发者并不需要掌握 Spring 的所有模块,但是 Spring Framework、Spring Boot、Spring Cloud 这三大模块是所有 Java 开发者必须要掌握的。

  • Spring Framework 是整个 Spring 生态的基础,各个模块都是基于 Spring Framework 衍生出来的。
  • Spring Boot 是一个快速开发框架,让开发者可以迅速搭建一套基于 Spring 的应用程序,并且将常用的 Spring 模块以及第三方模块,如 MyBatis、Hibernate 等都做了很好的集成,只需要简单的配置即可使用,不需要任何的 XML 配置文件,真正做到了开箱即用,同时默认支持 JSON 格式的数据,使用 Spring Boot 进行前后端分离开发也非常便捷。

案例上手 Spring 全家桶_第2张图片

(图片来自 Spring 官网)

  • Spring Cloud 是一套整合了分布式应用常用模块的框架,使得开发者可以快速实现微服务应用。作为目前非常热门的技术,有关微服务的话题总是在各种场景下被大家讨论,企业的招聘信息中也越来越多地出现对于微服务架构能力的要求。

案例上手 Spring 全家桶_第3张图片

(图片来自 Spring 官网)

确实,作为当今互联网时代最先进的业务架构解决方案,微服务发展非常迅速,关注点不仅仅放在开发层面,更多的是开发运维逐步一体的思路。

有些读者可能会认为,我现在就是一个初级程序员,把业务逻辑代码写好就可以了,不需要关心架构层面的东西。这种想法过于片面了,不论你现在处于什么阶段,架构方面的东西早晚都要接触,打个比方,当搬砖对你来说已经驾轻就熟了,这时就需要去思考怎么设计房子,也就是从 CRUD 业务操作到软件设计架构的进阶。

怎样提高自己的软件架构能力呢?首先你要具备扎实的基础知识,第二要有足够的项目经验,第三要视野开阔,技术领域的涉猎面要广。整个学习过程周期是比较长的,需要通过反复的实践,发现问题,解决问题来逐步完善你对于架构的理解,需要沉淀才能到达一定高度,很多之前不理解的东西自然就理解了,因此,从长远角度来看,即使你目前只是一个初级开发者,学习微服务也是非常有必要的。

微服务架构的落地框架有很多,对于 Java 开发者而言,当 Spring 框架已经成为事实上的行业标准时,Spring Cloud 作为 Spring 全家桶的重要一员,自然就是大家的首选,通过横向对比也可以得出结论,Spring Cloud 确实是微服务架构中一个十分优越的解决方案。

初学者的困惑

毫无疑问,Spring 全家桶是当前非常流行的主流框架,也是 Java 开发者的必备技能,无论你是初级菜鸟还是有一定经验的老鸟,都应该好好学习 Spring 全家桶的使用。

但遗憾的是目前市面上有关于 Spring 全家桶详细全面且实用的教程比较少,尤其对于初学者来讲,学习成本依旧很高,主要有以下两方面因素。

(1)目前市面上确实有一些不错的 Spring 课程,但都是分模块讲的,比如只讲 Spring MVC、或只讲 Spring Boot、亦或只讲 Spring Cloud,并没有一个集大成者的系统性课程来帮助初学者一次性搞定所有核心模块,这对初学者来讲是很不利的。要想学完全套 Spring 技术栈,需要同时购买好几个课程,这样成本会比较高,我说的并不是经济成本,而是学习成本,为什么呢?因为不同作者的写作风格是大相径庭的,你在学习 Spring Boot 的时候是按照当前作者的风格进行的,那当学习 Spring Cloud 时又是另外一种风格,在不同类型的教学风格中来回切换思路对于初学者来说不是一件轻松的事儿。

(2)很多课程都侧重于理论讲解,缺乏相应的实战案例,这对于初学者来讲也是挺痛苦的,看似学了很多,真正需要写代码时又不知如何下手,完全没有思路,学了一堆东西却不知道如何应用,那不就背离了我们最初的学习目的了吗?我们学技术就是为了实际应用,提高自己的竞争力,去争取更优质的资源。

我写这门课程就是希望能帮助初学者解决这两个问题,同时提供一站式服务,将 Spring 全家桶的核心模块一次性全部讲清楚,并结合实战案例让读者能够快速掌握实际开发的能力。上面提到过,以输出为结果导向是最高效的学习方法,希望通过我的这门课程,让所有需要掌握 Spring 全家桶的读者都能够快速上手,具备使用 Spring 技术栈进行实际开发的能力。

点击这里了解《Spring 全家桶》

本课程能为你提供哪些价值

在我看来,一门好的课程应该具备以下 4 个特点。

  • 提供新知

这个新知并不一定是新的技术,可以是新的框架版本、新的方法、新的思路、新的项目案例等,因此在本课程中,我们使用的 Spring 全家桶版本都是官方推荐版本(不一定是最新版本,官方推荐的一般都是比较稳定的版本)。

  • 内容全面

本课程重点讲解 Spring 全家桶最核心的 3 个模块:Spring Framework、Spring Boot、Spring Cloud;在此基础上还包括了 Spring Web MVC、Spring Security、Spring Data JPA、Spring Data Redis、Spring Data MongoDB、MyBatis 等框架,以及 MongoDB 数据库、Redis 数据库、Nginx、前端框架 Layui 的使用;同时还包含 3 个项目实战案例,分别是 Spring + Spring MVC + MyBatis + MySQL 电商项目、Spring MVC + Layui + Spring Data + MongoDB 权限管理系统、Layui + Spring Cloud + MyBatis + MySQL 外卖订餐系统,丰富的内容设置以确保读者可以真正学好 Spring 全家桶技术栈并应用于实战。

  • 容易理解

本课程内容深入浅出、通俗易懂,我本人是比较反感长篇大论的讲概念,然后没有多少实际干货的教程。我们学习的目的主要在于应用,而不在于研究理论,因此我的写作风格是偏向于实际应用的,让读者快速掌握 Spring 全家桶各个组件的使用,即使是没有接触过 Spring 框架的初学者也完全可以上手。但也不是完全没有门槛,本课程的学习者必须掌握 Java 核心基础以及 Java Web 开发技能,如果不具备这个条件,建议先学习 Java 基础的内容

  • 售后服务

知识付费产品的售后服务主要是指读者在购买课程后,如果在学习上遇到一些问题,能否及时得到解答,这也是读者比较关心的一个问题,如果做不到这一点,恐怕再好的课程对于初学者来讲也会比较吃力。只要购买了本课程的读者,都可以加入专属读者交流群,我会在群里为大家解决学习过程中遇到的各种问题,争取做到及时、准确地为读者提供在线答疑,用“保姆式”的服务为读者的学习保驾护航。

上述这 4 个特点是我写作这门课程的核心框架和指导方向,力图为读者打造一个学习闭环,一站式解决学习中的各种问题,为读者输出最有价值的内容。

认识一下即将要学习的全家桶成员

  • Spring Framework

Spring Framework 就是我们通常所说的 Spring 框架,它是一个软件设计架构层面的框架,为基于 Java 的企业级应用程序提供了一套标准流程和配置模型,可部署在任何类型的平台上。Spring 优势在于为开发者提供了应用级别的基础结构支持,实现应用层面的解耦合,允许开发者自主选择相关组件,开发者只需专注于业务逻辑的开发,不需要关注特定的部署环境。

  • Spring Web MVC

Spring Web MVC(官方名称)就是我们通常所说的 Spring MVC,它是 Spring Framework 中的一个模块,是 Spring Framework 在 Web 领域实现 MVC 设计模式的具体方案,主要是基于 DispatcherServer 的前端路由处理和 ViewResolver 视图解析器来简化开发者的工作效率。

  • Spring Boot

Spring Boot 是目前 Spring 全家桶系列中最流行的一个产品,在 Spring 官网的介绍排在第一位,可见对其重视程度,Spring 官方对 Spring Boot 的描述是“build anything”,翻译过来是构建任何事物,这样一个非常简单的描述将 Spring Boot 的特点展现的淋漓尽致,即通过 Spring Boot 可以快速构建一个基于 Spring 的独立生存级别的应用程序,开发者直接运行程序即可,无需处理各种繁琐的配置文件。简单理解,Spring Boot 就是为了让开发者快速启动和运行 Spring 应用程序而设计的。

  • Spring Cloud

Spring 官方对 Spring Cloud 的描述是“coordinate anything”,翻译过来是协调任何事物,通过这个描述可以明确 Spring Cloud 并不是为了实现某个业务模块而存在的,它是一个集大成者,将分布式系统开发中常用的模块进行整合,如服务注册、服务发现、配置管理、熔断器、控制总线等,基于 Spring Boot 形成一套框架体系,开箱即用,使得开发者可以快速实现分布式、微服务应用。

  • Spring Data

Spring Data 是 Spring 提供的持久层产品,主要功能是为应用程序中的数据访问提供统一的开发模型,同时保留不同数据存储的特殊性,并且这套开发模式是基于 Spring 的。根据不同类型的数据存储类型又可分为 Spring Data JDBC、Spring Data JPA、Spring Data Redis、Spring Data MongoDB 等,适用于关系型数据库和非关系型数据库。

  • Spring Security

Spring Security 是 Spring 提供的一个功能强大的安全框架,为 Java 应用程序提供授权功能,通过定制身份验证来实现对于访问权限的控制,Spring Security 的特点在于扩展性好,可以根据具体的业务需求来实现定制验证服务。

课程入口:学习者需要具备哪些条件

虽然这门课程我力图做到通俗易懂、深入浅出,让读者可以更加轻松地掌握所有技能,但是毕竟写的是企业级开发框架课程,还是需要读者具备一定基础的,比如:

  • 熟练掌握 Java 核心基础
  • 熟练掌握 Java Web 后端开发技术
  • 对 Spring 框架有基本的了解
  • 渴望全面提高自己的编程能力

课程环境参数

  • macOS Mojave 10.14.5
  • JDK 10.0.1
  • Maven 3.6.1
  • Tomcat 9.0.8
  • IntelliJ IDEA 2019.1
  • Spring Boot 2.1.5
  • Spring Framework 5.1.7
  • Spring Cloud Finchley.RELEASE
  • MySQL 8.0.11
  • MongoDB 4.0.0
  • Redis 4.0.10
  • Nginx 1.16.0
  • RabbitMQ 3.7.10

课程大纲

本课程内容分为七大部分,共计 68 课(含开篇词)。

第一部分:Spring 专题(第 1-1 ~ 1-6 课)

万丈高楼平地起,这部分内容将讲解 Spring Framework 的基本概念、组成,为后面的课程打下基础。

第二部分:Spring MVC 专题(第 2-1 ~ 2-13 课)

这部分内容将详细地讲解 Spring MVC,包括常用模块的使用以及梳理 Spring MVC 的底层实现原理。

第三部分:MyBatis 专题(第 3-1 ~ 3-9 课)

这部分内容将详细讲解主流的 ORMapping 框架 MyBatis,包括常用模块的使用和底层实现原理,作为持久层的实现方案,MyBatis 在实际项目开发中会与 Spring MVC 整合使用。

第四部分:MongoDB 专题(第 4-1 ~ 4-5 课)

这部分内容将详细讲解非关系型数据库 MongoDB 的安装及使用,以及 Spring 全家桶的整合方案 Spring Data MongoDB 的使用,同时完成本套课程的第 2 个项目案例,使用 Spring MVC + Layui + Spring Data MongoDB 实现权限管理系统。

第五部分:Spring Boot 专题(第 5-1 ~ 5-14 课)

重点突破,这部分内容将详细讲解 Spring 全家桶的重头戏——Spring Boot 核心模块的使用,Spring Boot 作为一个快速构建 Spring 应用的利器,对各种主流框架模块做了很好的集成,开箱即用,这部分内容将为大家详细讲解具体操作。

第六部分:Spring Cloud 专题(第 6-1 ~ 6-13 课)

突破重点,这部分内容将详细讲解 Spring 全家桶最热门的模块 Spring Cloud 的使用,包括服务网关、Ribbon、Feign、Hystrix、Spring Cloud Config 等,涵盖了实际开发中常用的技能点,理论结合实践的方式不仅仅让读者掌握基本概念,同时具备使用 Spring Cloud 搭建微服务架构的能力。

第七部分:微服务项目实战(第 7-1 ~ 7-7 课)

上手实战,技能升华。有了前面的 Spring Cloud 基础,这部分内容将详细讲解 Spring Cloud 的实战操作,包括 Spring Cloud 的高可用、集群、负载均衡,以及使用 Layui + Spring Cloud + MyBatis + MySQL 的技术选型来完成本套课程的最终项目实战。

案例上手 Spring 全家桶_第4张图片

课程出口:Spring Cloud 微服务项目实战

本套课程以开发一个基于 Spring Cloud 的分布式微服务项目为输出结果,如果最终能顺利完成,那么恭喜你,本课程的核心内容已经完全掌握了,如果暂时不能独立完成,那也没关系,继续学习就对了,我会帮助你尽快完成课程出口目标。

来看看我们要做一个什么样的项目,使用 Layui + Spring Cloud + MyBatis + MySQL 的技术选型完成外卖订餐系统,旨在通过这个项目让大家真正掌握 Spring Cloud 各个组件在实际开发中的使用。

本项目分为客户端和后台管理系统两个界面:

  • 客户端针对普通用户,功能包括用户登录、用户退出、菜品订购、我的订单;
  • 后台管理系统针对管理员,功能包括管理员登录、管理员退出、添加菜品、查询菜品、修改菜品、删除菜品、订单处理、添加用户、查询用户、删除用户。

系统架构设计分配出 4 个服务提供者:account、menu、order、user。

  • account 提供账户服务:用户和管理员登录。
  • menu 提供菜品服务:添加菜品、查询菜品、修改菜品、删除菜品。
  • order 提供订单服务:添加订单、查询订单、删除订单、处理订单。
  • user 提供用户服务:添加用户、查询用户、删除用户。

接下来分配出 1 个服务消费者,包括客户端的前端页面和后台接口、后台管理系统的前端页面和后台接口,用户 / 管理员直接访问的资源都保存在服务消费者中,然后服务消费者调用 4 个服务提供者对应的接口完成业务逻辑,并通过 Feign 实现负载均衡。

4 个服务提供者和 1 个服务消费者都需要在注册中心进行注册,同时要注册配置中心,提供远程配置信息读取,服务提供者和服务消费者的配置信息保存在 Git 远程仓库,由配置中心负责拉取。

本系统共由 8 个模块组成,包括注册中心、配置中心、Git 仓库配置信息、服务消费者、4 个服务提供者,关系如下图所示。

案例上手 Spring 全家桶_第5张图片

成果截图

案例上手 Spring 全家桶_第6张图片

案例上手 Spring 全家桶_第7张图片

课程寄语

希望大家学完本课程后,可以明确什么是 Spring 全家桶,以及如何使用 Spring 全家桶的核心模块,并且可以开发出实际项目。我们学习一门技术的目的全在于实际应用,对概念的理解一定要透彻但没必要钻牛角尖,重点是知道它是什么、能干什么以及怎么干,这种以实践为主的方式会贯穿整个课程,我也希望大家能够逐步养成这种编程思想,很多时候当我们真正掌握了一门技术的实际应用并且经过反复实践之后,才能在理论层面上升到一定高度,最重要的是构建自己的体系,这套体系才是你在职场的核心竞争力,预祝学习愉快~

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

点击这里了解《Spring 全家桶》

第 1-1 课:Spring 的基本应用——IoC 和 AOP

前言

对于任何一个 Java 开发人员,Spring 的大名一定如雷贯耳,在行业中可谓是无人不知、无人不晓,说它是 Java 领域第一框架毫不为过。

案例上手 Spring 全家桶_第8张图片

(图片来自 Spring 官网)

Spring 概念诞生于 2002 年,创始人 Rod Jahnson 在其著作《Expert One-on-One J2EE Design and Development》中第一次提出了 Spring 的核心思想,于 2003 年正式发布第一个版本 Spring Framework 0.9。

经过十几年的优化迭代,Spring Framework 已经从最初的取代 EJB 框架逐步发展为一套完整的生态,最新的版本是 5.X,支持现代 Java 开发的各个技术领域,家族两大核心成员 Spring Boot 和 Spring Cloud 更是当下 Java 领域最为热门的技术栈。

毋庸置疑,Spring 已经成为 Java 开发的行业标准,无论你是初级程序员还是架构师,只要是做 Java 开发的,工作中或多或少一定会接触到 Spring 相关技术栈。

我们所说的 Spring 全家桶各个模块都是基于 Spring Framework 衍生而来,通常所说的 Spring 框架一般泛指 Spring Framework,它包含 IoC 控制反转、DI 依赖注入、AOP 面向切面编程、Context 上下文、bean 管理、Spring Web MVC 等众多功能模块,其他的 Spring 家族成员都需要依赖 Spring Framework。

可以简单理解 Spring Framework 是一个设计层面的框架,通过分层思想来实现组件之间的解耦合,开发者可以根据需求选择不同的组件,并且可以非常方便的进行集成,Spring Framework 的这一特性使得企业级项目开发变得更加简单方便。

Spring 的两大核心机制是 IoC(控制反转)和 AOP(面向切面编程),对于初学者来讲,搞清楚这两个核心机制就掌握了 Spring 的基本应用。这两大核心机制也是 Java 设计模式的典型代表,其中 IoC 是工厂模式,AOP 是代理模式。

点击这里了解《Spring 全家桶》

什么是 IoC 和 AOP

下面来详细了解 IoC,IoC 是 Spring 框架的灵魂,非常重要,理解了 IoC 才能真正掌握 Spring 框架的使用。

IoC 也叫控制反转,首先从字面意思理解,什么叫控制反转?反转的是什么?

在传统的程序开发中,需要获取对象时,通常由开发者来手动创建实例化对象,但是在 Spring 框架中创建对象的工作不再由开发者完成,而是交给 IoC 容器来创建,我们直接获取即可,整个流程完成反转,因此是控制反转。

举个例子:超市购物。

  • 传统方式:你去超市买东西,需要自己拿着袋子去超市购买商品,然后自己把袋子提回来。
  • IoC 容器:你只需要把袋子放在家门口,袋子里面会自动装满你需要的商品,直接取出来用就可以了。

我们通过创建一个 Student 对象的例子来对比两种方式的区别。

传统方式

(1)创建 Student 类

public class Student {    private int id;    private String name;    private int age;}

(2)测试方法中调用构造函数创建对象

Student student = new Student();

IoC 容器

实现步骤
  • 在 pom.xml 中添加 Spring 依赖
  • 创建配置文件,可以自定义文件名 spring.xml
  • 在 spring.xml 中配置 bean 标签,IoC 容器通过加载 bean 标签来创建对象
  • 调用 API 获取 IoC 创建的对象

IoC 容器可以调用无参构造或者有参构造来创建对象,我们先来看无参构造的方式。

无参构造

配置一个 bean 标签:

  • id,对象名
  • class,对象的模板类

接下来调用 API 获取对象,Spring 提供了两种方式来获取对象:id 或者运行时类。

(1)通过 id 获取对象

//1.加载 spring.xml 配置文件ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.通过 id 值获取对象Student stu = (Student) applicationContext.getBean("stu");System.out.println(stu);

第一步:加载 spring.xml 配置文件,生成 ApplicationContext 对象。

第二步:调用 ApplicationContext 的 getBean 方法获取对象,参数为配置文件中的 id 值。程序在加载 spring.xml 时创建 stu 对象,通过反射机制调用无参构造函数,所有要求交给 IoC 容器管理的类必须有无参构造函数。

运行结果如下图所示。

案例上手 Spring 全家桶_第9张图片

可以看到,此时 stu 对象的属性全部为空,因为调用无参构造只会创建对象而不会进行赋值,如何赋值呢?只需要在 spring.xml 中进行相关配置即可,如下所示。

            

添加 property 标签:name 对应属性名,value 是属性的值。若包含特殊字符,比如 name="<张三>",使用 \]]> 进行配置,如下所示。

             ]]>      

运行结果如下图所示。

案例上手 Spring 全家桶_第10张图片

Spring 通过调用每个属性的 setter 方法来完成属性的赋值,因此实体类必须有 setter 方法,否则加载时报错,getter 方法可省略。

(2)通过运行时类获取对象

//1.加载 spring.xml 配置文件ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.通过运行时类获取对象Student stu = applicationContext.getBean(Student.class);System.out.println(stu);

此方法有一个弊端,当 spring.xml 中配置两个 Student 的 bean 时程序会抛出异常,因为此时两个 bean 都是由 Student 类生成的,IoC 容器无法将两个 bean 都返回,必须指定一个唯一的 bean。

             ]]>                    

异常信息如下图所示。

案例上手 Spring 全家桶_第11张图片

以上是 IoC 容器通过无参构造创建对象的方式,同时 IoC 容器也可以调用有参构造来创建对象。

有参构造

(1)在实体类中创建有参构造

public Student(int id, String name, int age) {        super();        this.id = id;        this.name = name;        this.age = age;}

(2)spring.xml 中进行配置

         

(3)此时 IoC 容器会根据 constructor-arg 标签去加载对应的有参构造函数,创建对象并完成属性赋值。name 的值需要与有参构造的形参名对应,value 是对应的值。除了使用 name 对应参数外,还可以通过下标 index 对应,如下所示。

         

以上是 IoC 容器通过有参构造创建对象的方式,获取对象同样有两种方式可以选择:id 和运行时类。

如果 IoC 容器管理多个对象,并且对象之间有级联关系,如何实现?

(1)创建 Classes 类

public class Classes {    private int id;    private String name;}

(2)在 Student 类中添加 Classes 属性

public class Student {    private int id;    private String name;    private int age;    private Classes classes;}

(3)spring.xml 中配置 classes 对象,然后将该对象赋值给 stu 对象

                   ]]>            

再次获取 Student 对象,结果如下图所示。

案例上手 Spring 全家桶_第12张图片

在 spring.xml 中,通过 ref 属性将其他 bean 赋给当前 bean 对象,这种方式叫做依赖注入(DI),是 Spring 非常重要的机制,DI 是将不同对象进行关联的一种方式,是 IoC 的具体实现方式,通常 DI 和 IoC 是紧密结合在一起的,因此一般说的 IoC 包括 DI。

如果是集合属性如何依赖注入?

(1)Classes 类中添加 List\ 属性。

public class Classes {    private int id;    private String name;    private List students;}

(2)spring.xml 中配置 2 个 student 对象、1 个 classes 对象,并将 2 个 student 对象注入到 classes 对象中。

                                                                     ]]>               

运行结果如下图所示。

案例上手 Spring 全家桶_第13张图片

集合属性通过 list 标签和 ref 标签完成注入,ref 的 bean 属性指向需要注入的 bean 对象。

总结

这一讲我们讲解了 Spring IoC 的基本概念以及如何使用,IoC 是 Spring 的核心,这很重要。使用 Spring 开发项目时,控制层、业务层、DAO 层都是通过 IoC 来完成依赖注入的。

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

请单击这里下载源码

点击这里了解《Spring 全家桶》

第 1-2 课:掌握 Spring IoC 两种常规操作,提高工作效率

前言

上一讲介绍了 Spring 的 IoC,即控制反转,程序中由 Spring 来管理对象,当需要使用某个对象时,直接通过 IoC 容器来获取对象,并通过 DI 来完成对象之间的注入关系。下面继续来学习 IoC 的相关知识。

Spring 中的 bean

bean 是根据 scope 来生成的,表示 bean 的作用域,scope 有 4 种类型:

  • singleton,单例,表示通过 Spring 容器获取的该对象是唯一的;
  • prototype,原型,表示通过 Spring 容器获取的对象都是不同的;
  • reqeust,请求,表示在一次 HTTP 请求内有效;
  • session,会话,表示在一个用户会话内有效。

后两种只适用于 Web 项目,大多数情况下,我们只会使用 singleton 和 prototype 两种 scope,并且 scope 的默认值是 singleton。

我们通过一个例子来学习这两种配置的区别。

(1)创建 User 实体类

public class User {    private int id;    private String name;    private int age;    public User() {         System.out.println("创建了User对象");    }   }

(2)在 spring.xml 配置 User 的实例化 bean

         

(3)测试类中通过 Spring 容器获取两个 User 实例化对象 user1 和 user2,并且通过 == 方法判断是否为同一个对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = (User) applicationContext.getBean("user");User user2 = (User) applicationContext.getBean("user");System.out.println(user == user2);

运行结果如下图所示。

案例上手 Spring 全家桶_第14张图片

看到结果打印 true,并且 User 的构造函数只执行了一次,表示 user1 和 user2 是同一个对象,因此 scope 默认值为 singleton,即默认通过 Spring 容器创建的对象都是单例模式。

修改 spring.xml 中的配置,将 scope 改为 prototype。

         

执行代码,结果如下图所示。

案例上手 Spring 全家桶_第15张图片

可以看到,调用了两次构造函数,并且 == 的结果为 false,表示现在的 user1 和 user2 是两个对象。

点击这里了解《Spring 全家桶》

Spring 的继承

Spring 的继承与 Java 的继承不一样,但思想很相似,子 bean 可以继承父 bean 中的属性。看到这里,你可能会有这样的疑问:子 bean 可以继承父 bean 中的方法吗?

其实这里不存在方法的继承,Spring 的继承是在对象层面进行操作的,即两个 bean 来自同一个类,因此方法是一样的,不存在继承关系,具体使用如下所示。

(1)在 spring.xml 中配置两个 User bean,并建立继承关系。

         

(2)运行代码,结果如下。

User user2 = (User) applicationContext.getBean("user2");System.out.println(user2);

案例上手 Spring 全家桶_第16张图片

可以看到,创建了两个 User 对象 user1 和 user2,并且 user2 继承了 user1 的所有属性。user2 在继承 user1 所有属性的基础之上,还可以对属性进行覆盖,直接在 spring.xml 中添加 property 即可。

               

再次运行代码,结果如下图所示。

案例上手 Spring 全家桶_第17张图片

name 属性已经被修改为李四,完成覆盖。有读者可能会问,Spring 中的 bean 能不能在不同类之间继承?

答案是可以的,但是需要这两个类的属性列表完全一致,否则会报错,实际开发中并不会使用到这种方式。

Spring 的依赖

与继承类似,依赖也是 bean 和 bean 之间的一种关联方式,配置依赖关系后,被依赖的 bean 一定先创建,再创建依赖的 bean,我们还是通过代码来理解。

(1)创建 Car 实体类

public class Car {    private int id;    private String brand;    public Car() {        System.out.println("创建了Car对象");    }}

(2)在 spring.xml 中配置 User bean、Car bean

               

(3)测试类中获取两个 bean

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = (User) applicationContext.getBean("user");Car car = (Car) applicationContext.getBean("car");

结果如下图所示。

案例上手 Spring 全家桶_第18张图片

看到结果,先创建 User,后创建 Car,这是由 spring.xml 中 bean 的配置顺序来决定的,先到先得,先配置 User bean,因此先创建了 User 对象。现在修改 spring.xml 配置,User 依赖 Car,设置 depends-on 属性,如下所示。

               

再次运行代码,看到结果,先创建 Car,后创建 User,因为 User 依赖于 Car,所以必须先创建 Car 对象,User 对象才能完成依赖。

案例上手 Spring 全家桶_第19张图片

Spring 读取外部资源

在实际开发中,数据库配置一般会保存在 Properties 文件中方便维护,如果使用 Spring 容器来生成数据源对象,如何读取到 properties 配置文件中的内容?

(1)创建 jdbc.properties

driverName = com.mysql.jdbc.Driverurl = jdbc:mysql://localhost:3306/myTest?useUnicode=true&characterEncoding=UTF-8user = rootpwd = root

(2)spring.xml 中配置 C3P0 数据源

            

第一步:导入外部资源文件。

使用 context:property-placeholder 标签,需要导入 context 命名空间。

第二步:通过 ${} 表达式取出外部资源文件的值。

(3)测试类中获取 Spring 创建的 dataSource 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");DataSource ds = (DataSource) applicationContext.getBean("dataSource");Connection conn = null;try {     conn = ds.getConnection();} catch (SQLException e) {     // TODO Auto-generated catch block     e.printStackTrace();}System.out.println(conn);

(4)运行代码,看到结果,打印 dataSource 对象

案例上手 Spring 全家桶_第20张图片

除了使用 \ 元素为 Bean 的属性装配值和引用外,Spring 还提供了另外一种 bean 属性的装配方式:p 命名空间,该方式进一步简化配置代码。

p 命名空间

p 命名空间可以简化 bean 的各种配置,直接通过代码来学习。

(1)在 User 实体类中添加 Car 属性

public class User {    private int id;    private String name;    private int age;    private Car car;    public User() {        System.out.println("创建了User对象");    }    @Override    public String toString() {        return "User [id=" + id + ", name=" + name + ", age=" + age + ", car="                + car + "]";    }}

(2)spring.xml 中创建 User bean 和 Car bean,并且通过 p 命名空间给属性赋值,同时完成依赖注入,注意需要引入 p 命名间

(3)测试类中获取 User 对象,并打印

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = (User) applicationContext.getBean("user");System.out.println(user);

运行结果如下图所示,创建了 User 对象和 Car 对象,并且完成属性赋值,以及级联关系。

案例上手 Spring 全家桶_第21张图片

总结

这一讲我们讲解了 Spring IoC 两种常规操作,分别是依赖注入与 p 命名空间,依赖注入是维护对象关联关系的机制。p 命名空间实际上是对配置文件的简化,以提高我们的开发效率。

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

请单击这里下载源码

点击这里了解《Spring 全家桶》

第 1-3 课:Spring IoC 工厂方法 + 自动装载

前言

这一讲我们继续来学习 IoC 的两个常用知识点:IoC 通过工厂方法创建对象、IoC 自动装载(autowire)。

IoC 通过工厂方法创建对象

之前说过 IoC 是典型的工厂模式,下面我们就来学习如何使用工厂模式来创建 bean,IoC 通过工厂模式创建 bean 有两种方式:

  • 静态工厂方法
  • 实例工厂方法

按照惯例,我们还是通过代码来带大家去学习工厂方法,先来学习静态工厂方法。

(1)创建 Car 实体类

public class Car {    private int num;    private String brand;    public Car(int num, String brand) {        super();        this.num = num;        this.brand = brand;    }}

(2)创建静态工厂类、静态工厂方法

public class StaticCarFactory {    private static Map cars;    static{        cars = new HashMap();        cars.put(1, new Car(1,"奥迪"));        cars.put(2, new Car(2,"奥拓"));    }    public static Car getCar(int num){        return cars.get(id);    }}

(3)在 spring.xml 中配置静态工厂

   
  • factory-method 指向静态方法;
  • constructor-arg 的 value 属性为调用静态方法所传的参数。

(4)在测试类中直接获取 car1 对象。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Car car = (Car) applicationContext.getBean("car1");System.out.println(car);

运行结果如下图所示。

案例上手 Spring 全家桶_第22张图片

点击这里了解《Spring 全家桶》

实例工厂方法

(1)创建实例工厂类、工厂方法

public class InstanceCarFactory {    private Map cars;    public InstanceCarFactory() {        cars = new HashMap();        cars.put(1, new Car(1,"奥迪"));        cars.put(2, new Car(2,"奥拓"));    }    public Car getCar(int num){        return cars.get(num);    }}

(2)在 spring.xml 中配置 bean

     

(3)在测试类中直接获取 car2 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Car car2 = (Car) applicationContext.getBean("car2");System.out.println(car2);

运行结果如下图所示。

案例上手 Spring 全家桶_第23张图片

对比两种方式的区别,静态工厂方法的方式创建 car 对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用。所以 spring.xml 只需要配置一个 Car bean,而不需要配置工厂 bean。

实例工厂方法创建 car 对象,必须先实例化工厂对象,因为调用的是非静态方法,必须通过对象调用,不能直接通过类来调用,所以 spring.xml 中需要先配置工厂 bean,再配置 Car bean。

其实这里考察的就是静态方法和非静态方法的调用。

IoC 自动装载(autowire)

我们通过前面的学习,掌握了 IoC 创建对象的方式,以及 DI 完成依赖注入的方式,通过配置 property 的 ref 属性,可将 bean 进行依赖注入。

同时 Spring 还提供了另外一种更加简便的方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成依赖注入。

自动装载有两种方式:

  • byName,通过属性名自动装载;
  • byType,通过属性对应的数据类型自动装载。

通过代码来学习。

(1)创建 Person 实体类

public class Person {    private int id;    private String name;    private Car car;}

(2)在 spring.xml 中配置 Car bean 和 Person bean,并通过自动装载进行依赖注入。

创建 person 对象时,没有在 property 中配置 car 属性,因此 IoC 容器会自动进行装载,autowire="byName" 表示通过匹配属性名的方式去装载对应的 bean,Person 实体类中有 car 属性,因此就将 id="car" 的 bean 注入到 person 中。

注意:通过 property 标签手动进行 car 的注入优先级更高,若两种方式同时配置,以 property 的配置为准。

          

(3)测试类中获取 person 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Person person = (Person) applicationContext.getBean("person");System.out.println(person);

运行结果如下图所示。

案例上手 Spring 全家桶_第24张图片

同理,byType 即通过属性的数据类型来配置。

(1)spring.xml 配置如下

             

(2)测试类中获取 person 对象

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Person person = (Person) applicationContext.getBean("person");System.out.println(person);

运行结果如下图所示。

案例上手 Spring 全家桶_第25张图片

可以看到控制台直接打印异常信息,这是因为使用了 byType 进行自动装载,但是 spring.xml 中配置了两个 Car bean,IoC 容器不知道应该将哪一个 bean 装载到 person 对象中,所以抛出异常。使用 byType 进行自动装载时,spring.xml 中只能配置一个被装载的 bean。

(3)现修改 spring.xml 配置,只保留一个 bean,如下所示:

             

(4)测试类中获取 person 对象,运行结果如下图所示:

案例上手 Spring 全家桶_第26张图片

总结

本讲我们讲解了 Spring IoC 的工厂方法与自动装载,工厂方式是 Spring 框架对工厂模式的实现。自动装载是对上一讲内容的依赖注入优化,在维护对象关联关系的基础上,进一步简化配置。

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号,并注明「全家桶」。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

请单击这里下载源码

点击这里了解《Spring 全家桶》

第 2-2 课:深入探究底层原理,应用更加得心应手

前言

上一讲我们学习了 Spring MVC 框架的使用,为了更好地理解这个框架,本讲来仿写一个 Spring MVC 框架,用到的技术比较简单,只需要 XML 解析 + 反射就可以完成,不需要 JDK 动态代理。

自己手写框架的前提是必须理解框架的底层原理和运行机制,因此我们还是先来回顾一下 Spring MVC 的实现原理。

Spring MVC 实现原理

Spring MVC 的核心组件和工作流程的内容具体可以参考第 2-1 讲的内容,通过上一讲的分析,大致可以将 Spring MVC 流程理解如下:

首先需要一个前置控制器 DispatcherServlet,作为整个流程的核心,由它去调用其他组件,共同完成业务。

主要组件有两个:

一是 Controller,调用其业务方法 Method,执行业务逻辑。

二是 ViewResolver 视图解析器,将业务方法的返回值解析为物理视图 + 模型数据,返回客户端。

我们按照这个思路来自己写框架。

点击这里了解《Spring 全家桶》

初始化工作

  • 根据 Spring IoC 容器的特性,需要将参与业务的对象全部创建并保存到容器中,供流程调用。首先需要创建 Controller 对象,HTTP 请求是通过注解找到对应的 Controller 对象,因此我们需要将所有的 Controller 与其注解建立关联,很显然,使用 key-value 结构的 Map 集合来保存最合适不过了,这样就模拟了 IoC 容器。
  • Controller 的 Method 也是通过注解与 HTTP 请求映射的,同样的,我们需要将所有的 Method 与其注解建立关联, HTTP 直接通过注解的值找到对应的 Method,这里也用 Map 集合保存。
  • 实例化视图解析器。

初始化工作完成,接下来处理 HTTP 请求,业务流程如下:

(1)DispatcherServlet 接收请求,通过映射从 IoC 容器中获取对应的 Controller 对象;

(2)根据映射获取 Controller 对象对应的 Method;

(3)调用 Method,获取返回值;

(4)将返回值传给视图解析器,返回物理视图;

(5)完成页面跳转。

思路捋清楚了,接下来开始写代码,我们需要创建下面这四个类:

(1)MyDispatcherServlet,模拟 DispatcherServlet;

(2)MyController,模拟 Controller 注解;

(3)MyRequestMapping,模拟 RequestMapping 注解;

(4)MyViewResolver,模拟 ViewResolver 视图解析器。

首先创建 MyDispatcherServlet,init 方法完成初始化:

(1)将 Controller 与注解进行关联,保存到 iocContainer 中,哪些 Controller 是需要添加到 iocContainer 中的?

必须同时满足两点:

  • springmvc.xml 中配置扫描的类
  • 类定义处添加了注解

注意这两点必须同时满足。

代码思路:

  • 解析 springmvc.xml
  • 获取 component-scan 标签配置包下的所有类
  • 判断这些类若添加了 @MyController 注解,则创建实例对象,并且保存到 iocContainer
  • @MyRequestMapping 的值为键,Controller 对象为值

(2)将 Controller 中的 Method 与注解进行关联,保存到 handlerMapping 中。

代码思路:

  • 遍历 iocContainer 中的 Controller 实例对象
  • 遍历每一个 Controller 对象的 Method
  • 判断 Method 是否添加了 @MyRequestMapping 注解,若添加,则进行映射并保存
  • 保存到 handlerMapping 中,@MyRequestMapping 的值为键,Method 为值

(3)实例化 ViewResolver。

代码思路:

  • 解析 springmvc.xml
  • 根据 bean 标签的 class 属性获取需要实例化的 MyViewResolver
  • 通过反射创建实例化对象,同时获取 prefix 和 suffix 属性,以及 setter 方法
  • 通过反射调用 setter 方法给属性赋值,完成 MyViewResolver 的实例化

doPost 方法处理 HTTP 请求的流程:

(1)解析 HTTP,分别得到 Controller 和 Method 对应的 uri;

(2)通过 uri 分别在 iocContainer 和 handlerMapping 中获取对应的 Controller 及 Method;

(3)通过反射调用 Method,执行业务方法,获取结果;

(4)将结果传给 MyViewResolver 进行解析,返回真正的物理视图(JSP 页面);

(5)完成 JSP 页面跳转。

案例上手 Spring 全家桶_第27张图片

代码实现

(1)创建 MyController 注解,作用目标为类。

/** * 自定义 Controller 注解 * @author southwind * */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface MyController {    String value() default "";}

(2)创建 MyRequestMapping 注解,作用目标为类和方法。

/** * 自定义 RequestMapping 注解 * @author southwind * */@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MyRequestMapping {    String value() default "";}

(3)创建 MyDispatcherServlet,核心控制器,init 完成初始化工作,doPost 处理 HTTP 请求。

/** * DispatcherServlet * @author southwind * */public class MyDispatcherServlet extends HttpServlet{    //模拟 IOC 容器,保存 Controller 实例对象    private Map iocContainer = new HashMap();    //保存 handler 映射    private Map handlerMapping = new HashMap();    //自定视图解析器    private MyViewResolver myViewResolver;    @Override    public void init(ServletConfig config) throws ServletException {        // TODO Auto-generated method stub        //扫描 Controller,创建实例对象,并存入 iocContainer        scanController(config);        //初始化 handler 映射        initHandlerMapping();        //加载视图解析器        loadViewResolver(config);    }    /**     * 扫描 Controller     * @param config     */    public void scanController(ServletConfig config){        SAXReader reader = new SAXReader();        try {            //解析 springmvc.xml            String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");               Document document = reader.read(path);            Element root = document.getRootElement();            Iterator iter = root.elementIterator();            while(iter.hasNext()){                Element ele = (Element) iter.next();                if(ele.getName().equals("component-scan")){                    String packageName = ele.attributeValue("base-package");                    //获取 base-package 包下的所有类名                    List list = getClassNames(packageName);                    for(String str:list){                        Class clazz = Class.forName(str);                        //判断是否有 MyController 注解                        if(clazz.isAnnotationPresent(MyController.class)){                            //获取 Controller 中 MyRequestMapping 注解的 value                            MyRequestMapping annotation = (MyRequestMapping) clazz.getAnnotation(MyRequestMapping.class);                            String value = annotation.value().substring(1);                            //Controller 实例对象存入 iocContainer                            iocContainer.put(value, clazz.newInstance());                        }                    }                }            }        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    /**     * 获取包下的所有类名     * @param packageName     * @return     */    public List getClassNames(String packageName){        List classNameList = new ArrayList();        String packagePath = packageName.replace(".", "/");          ClassLoader loader = Thread.currentThread().getContextClassLoader();          URL url = loader.getResource(packagePath);          if(url != null){            File file = new File(url.getPath());              File[] childFiles = file.listFiles();            for(File childFile : childFiles){                String className = packageName+"."+childFile.getName().replace(".class", "");                classNameList.add(className);            }        }        return classNameList;    }    /**     * 初始化 handler 映射     */    public void initHandlerMapping(){        for(String str:iocContainer.keySet()){            Class clazz = iocContainer.get(str).getClass();            Method[] methods = clazz.getMethods();               for (Method method : methods) {                 //判断方式是否添加 MyRequestMapping 注解                 if(method.isAnnotationPresent(MyRequestMapping.class)){                     //获取 Method 中 MyRequestMapping 注解的 value                     MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);                     String value = annotation.value().substring(1);                     //method 存入 methodMapping                     handlerMapping.put(value, method);                 }             }        }    }    /**     * 加载自定义视图解析器     * @param config     */    public void loadViewResolver(ServletConfig config){        SAXReader reader = new SAXReader();        try {            //解析 springmvc.xml            String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");               Document document = reader.read(path);            Element root = document.getRootElement();            Iterator iter = root.elementIterator();            while(iter.hasNext()){                Element ele = (Element) iter.next();                if(ele.getName().equals("bean")){                    String className = ele.attributeValue("class");                    Class clazz = Class.forName(className);                    Object obj = clazz.newInstance();                    //获取 setter 方法                    Method prefixMethod = clazz.getMethod("setPrefix", String.class);                    Method suffixMethod = clazz.getMethod("setSuffix", String.class);                    Iterator beanIter = ele.elementIterator();                    //获取 property 值                    Map propertyMap = new HashMap();                    while(beanIter.hasNext()){                        Element beanEle = (Element) beanIter.next();                        String name = beanEle.attributeValue("name");                        String value = beanEle.attributeValue("value");                        propertyMap.put(name, value);                    }                    for(String str:propertyMap.keySet()){                        //反射机制调用 setter 方法,完成赋值                        if(str.equals("prefix")){                            prefixMethod.invoke(obj, propertyMap.get(str));                        }                        if(str.equals("suffix")){                            suffixMethod.invoke(obj, propertyMap.get(str));                        }                    }                    myViewResolver = (MyViewResolver) obj;                }            }        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        // TODO Auto-generated method stub        this.doPost(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        // TODO Auto-generated method stub        //获取请求        String handlerUri = req.getRequestURI().split("/")[2];        //获取 Controller 实例        Object obj = iocContainer.get(handlerUri);        String methodUri = req.getRequestURI().split("/")[3];        //获取业务方法        Method method = handlerMapping.get(methodUri);        try {            //反射机制调用业务方法            String value = (String) method.invoke(obj);            //视图解析器将逻辑视图转换为物理视图            String result = myViewResolver.jspMapping(value);            //页面跳转            req.getRequestDispatcher(result).forward(req, resp);        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }     }}

(4)创建视图解析器 MyViewResolver。

/** * 自定义视图解析器 * @author southwind * */public class MyViewResolver {    private String prefix;    private String suffix;    public String getPrefix() {        return prefix;    }    public void setPrefix(String prefix) {        this.prefix = prefix;    }    public String getSuffix() {        return suffix;    }    public void setSuffix(String suffix) {        this.suffix = suffix;    }    public String jspMapping(String value){        return this.prefix+value+this.suffix;    }}

(5)创建 TestController,处理业务请求。

@MyController@MyRequestMapping(value = "/testController")public class TestController {    @MyRequestMapping(value = "/test")    public String test(){        System.out.println("执行test相关业务");        return "index";    }}

(6)测试。

案例上手 Spring 全家桶_第28张图片

案例上手 Spring 全家桶_第29张图片

跳转 index.jsp,同时控制台打印业务日志,访问成功。

总结

本节课我们讲解了 Spring MVC 的底层原理,同时仿照 Spring MVC 手写了一个简单的框架,目的不是让大家自己去写框架,在实际开发中也不需要自己写框架,直接使用成熟的第三方框架即可。手写框架的目的在于让大家更透彻地理解 Spring MVC 的底层流程、学习优秀框架的编程思想,理解了原理,才能更熟练地应用。

分享交流

我们为本课程付费读者创建了微信交流群,以方便更有针对性地讨论课程相关问题。入群方式请到第 1-4 课末尾添加小助手的微信号。

阅读文章过程中有任何疑问随时可以跟其他小伙伴讨论,或者直接向作者提问(作者看到后抽空回复)。你的分享不仅帮助他人,更会提升自己。

温馨提示:需购买才可入群哦,加小助手微信后需要截已购买的图来验证~

请单击这里下载源码

点击这里了解《Spring 全家桶》

第 1-4 课:Spring IoC 基于注解的开发
第 1-5 课:Spring IoC 底层实现
第 1-6 课:Spring 的另一个核心机制 AOP
第 2-1 课:快速搭建第一个 Spring MVC 项目
第 2-3 课:一文学会 Spring MVC 的常用注解
第 2-4 课:搞懂 Spring MVC 数据绑定,让开发更加简单
第 2-5 课:探寻 Spring MVC 视图层的实现机制
第 2-6 课:如何给项目定制数据类型转换器
第 2-7 课:Spring MVC 与主流架构 RESTful 的集成
第 2-8 课:用 Spring MVC 的上传下载机制简化代码
第 2-9 课:数据安全重中之重,Spring MVC 如何处理
第 2-10 课:Spring MVC 表单标签库
第 2-11 课:Spring MVC 国际化
第 2-12 课:Java Web 开发的基本操作之 EL 表达式
第 2-13 课:EL 表达式的好搭档—JSTL
第 3-1 课:搭建第一个 MyBatis 项目
第 3-2 课:MyBatis 底层实现
第 3-3 课:Mapper.xml 的常用配置
第 3-4 课:MyBatis 逆向工程,简化代码开发
第 3-5 课:MyBatis 延迟加载
第 3-6 课:MyBatis 缓存
第 3-7 课:MyBatis 动态 SQL
第 3-8 课:搭建 Java 常规技术之 SSM 整合
第 3-9 课:Nginx 搭建 Tomcat 集群
第 4-1 课:搭建主流 NoSQL MongoDB 环境
第 4-2 课:MongoDB 常用命令
第 4-3 课:Spring Data 集成 MongoDB:MongoTemplate
第 4-4 课:Spring Data 集成 MongoDB:Repository
第 4-5 课:Spring MVC + Layui + Spring Data + MongoDB 项目实战
第 5-1 课:速搭建第一个 Spring Boot 项目
第 5-2 课:Spring Boot 配置文件
第 5-3 课:Spring Boot 整合 JSP
第 5-4 课:Spring Boot 整合 Thymeleaf(上)
第 5-5 课:Spring Boot 整合 Thymeleaf(下)
第 5-6 课:Spring Boot 整合 JdbcTemplate
第 5-7 课:Spring Boot 整合 MyBatis
第 5-8 课:Spring Boot 整合 Spring Data JPA
第 5-9 课:搭建主流 NoSQL Redis 环境
第 5-10 课:Spring Boot 应用中集成 Spring Data Redis
第 5-11 课:Spring Boot 整合 Redis 实现 Session 共享
第 5-12 课:Spring Boot 整合 MongoDB
第 5-13 课:Spring Boot 整合 Spring Security
第 5-14 课:用 Spring Boot 开发一个电商系统
第 6-1 课:微服务产生的背景
第 6-2 课:搭建微服务系统的核心中枢
第 6-3 课:注册第一个微服务
第 6-4 课:跨服务接口调用神器 RestTemplate
第 6-5 课:微服务调用案例
第 6-6 课:用服务网关统一 URL,开发更简洁
第 6-7 课:Ribbon 负载均衡
第 6-8 课:Spring Cloud Feign 声明式接口调用
第 6-9 课:Hystrix 容错监控机制
第 6-10 课:Spring Cloud Config 本地配置
第 6-11 课:搭建消息中间件 RabbitMQ 环境
第 6-12 课:Spring Cloud Config 远程配置
第 6-13 课:Zipkin 服务跟踪
第 7-1 课:明确微服务实战项目的详细需求
第 7-2 课:注册中心和配置中心
第 7-3 课:服务提供者 account
第 7-4 课:服务提供者 menu
第 7-5 课:服务提供者 order
第 7-6 课:服务提供者 user
第 7-7 课:服务消费者 clientfeign
「第一部分:Spring 专题」面试题
「第二部分:Sprint MVC 专题」面试题
「第三部分:MyBatis 专题」面试题
「第四部分:MongoDB 专题」面试题
「第五部分:Spring Boot 专题」面试题
「第六部分:Spring Cloud 专题」面试题
「第七部分:微服务项目实战」

阅读全文: http://gitbook.cn/gitchat/column/5d2daffbb81adb3aa8cab878

你可能感兴趣的:(案例上手 Spring 全家桶)