精华内容
下载资源
问答
  • 得到为什么下架课程
    千次阅读
    2022-02-28 21:16:27

    您好 Java 开发人员,如果您想学习 Spring 框架并寻找最佳资源、教程,那么您来对地方了。

    Java 开发人员您好,如果您想学习 Spring 框架并寻找最好的资源,例如书籍、在线课程、代码 katas 和教程,那么您来对地方了。

    早些时候,我分享了最好的 Spring Boot 课程,今天,我将为 Java 开发人员分享最好的 Spring Framework 资源。这包括学习 Spring、课程、代码 katas 和交互式学习材料。

    如果您不知道,Spring Framework是世界上使用企业 Java 最流行的开发框架之一

    数以百万计的开发人员使用它来创建易于测试、可重用和高性能的代码,它也是 Java 开发人员的基本技能之一。如果您是 Java 开发人员并且不熟悉 Spring 框架,那么这应该是您应该学习的第一件事

    Spring Framework 的主要优点之一是它是开源的。它最初由 Rod Johnson 于 2003 年创建,并在 Apache 2.0 许可下发布。它在尺寸方面也非常灵活和轻巧。你现在可以安装 Spring Framework 的基本版本,它只会让你回到大约 2MB 的存储空间。

    Spring Framework 具有许多用于构建 Java 应用程序的惊人功能,以及许多可用于使用 Java EE 平台构建 Web 应用程序的扩展。Spring Framework 的 USP 是让程序员更容易开发 J2EE,并启用基于 POJO 的编程模型。

    如果您使用 POJO,则本质上意味着您的应用程序不需要服务器。您将能够使用强大的 servlet 容器,例如 Tomcat 或其他商业产品。Spring 还彻底改变了许多现有技术,例如 ORM 框架、日志框架、JEE、Quartz 和 JDK Timers。

    由于 Spring Framework 还包含与环境相关的代码,因此测试和调试代码要容易得多。JavaBeanstyle POJO 的存在也意味着您可以使用依赖注入来注入测试数据。

    顺便说一句,如果您赶时间并想学习 Spring 以进行全栈开发,那么我还建议您查看由博客作者和 Spring 讲师 Ranga Karnam 编写的Go Java Full Stack with Spring Boot 和 React课程。


    面向初学者和经验丰富的 Java 程序员的 10 部最佳 Spring 框架书籍和课程

    现在,您一定想知道在哪里可以了解有关 Spring 框架的更多信息,对吧?别担心,我们已经为您提供保障。我们搜索了互联网的最远范围,并列出了目前网络上可用的最佳 Spring 框架课程列表。查看下面的列表。

    1. Spring in Action 第 5 版 - 书籍

    当然,这应该是 2022 年最先阅读的几本书之一,如果你还没有读完Effective Java 第 3 版的话。

    尽管 Spring in Action 不需要任何介绍或推荐,并且是目前学习 Spring 框架的最佳书籍,但它也已更新到 Spring 5,这意味着您可以使用它来学习和发现 Spring Framework 5 中的新功能,例如反应式 Spring、WebFlux 等。

    它还将教您如何将 Spring Framework 与 Java 9 和 aster 数据访问和事务一起使用,使用新的功能性 Web 框架以及使用 Spring Boot 和其他云技术创建微服务和其他 Web 服务。

    如果您在 2021 年学习 Spring 框架,我建议您关注这本书。它更最新,并遵循使用 Spring 开发 Java 应用程序的最新实践。

    如果需要,还可以将本书与Udemy 上的Spring Framework 5: Beginner to Guru课程结合起来,这也是学习 Spring 框架的最新课程。


    2. Spring Framework Masterclass:Java Spring the Modern Way [Udemy]

    本课程的主要亮点是您将使用真实项目来学习 Spring 框架。这意味着你将获得更实用的Spring实践体验,让你学得更快。

    本课程非常适合具有一定 Java 编程经验但对 Spring 完全陌生的人。您将了解 Spring 和 Spring 模块的许多特性,例如 JDBC、AOP 和 Data JPA。本课程将是您进入神奇春天世界的完美门户。

    课程时长:12 小时
    课程评分:4.5 星(满分 5 星)
    课程讲师:in28Minutes 官方
    课程价格:14 美元

    购买本课程后,您将终生访问 12 小时的点播视频讲座、13 篇文章和 1 个可下载的教育资源。整个课程内容分为14个部分,139个讲座。


    3.使用 Java Spring 框架进行 Web 开发[Coursera]

    正如标题所示,本课程更侧重于使用流行的 Web Java 框架进行 Web 开发方面的工作。您将了解 Spring 的库如何用于创建流媒体电视、联网汽车和在线购物等技术。

    几乎所有的开发人员都同意 Spring 是应用程序开发的未来,将它放在你的工具包中可能很有价值,并且可能是你失去和获得梦想工作的区别。

    本课程将涵盖 Spring 的主要部分,使您能够使用 Spring MVC 和 Hibernate CRUD 开发 RESTFUL Web 服务。

    课程时长:19 小时
    课程评分:3.7 星(满分 5 星)
    课程讲师:Martyshkin Alexey Ivanovich
    课程价格:15 美元

    完成本课程后,您将牢牢掌握依赖注入、控制反转和使用 Hibernate 访问数据等内容。

    顺便说一句,如果您发现 Coursera 课程很有用,因为它们是由世界各地的知名公司和大学创建的,我建议您加入Coursera Plus,这是 Coursera 的订阅计划,您可以无限制地访问他们最受欢迎的课程,专业化,专业证书和指导项目。

    它每年花费大约 59 美元,但绝对值得。他们还提供7 天免费试用目前,您还可以花 1 美元获得 Coursera Plus 一个月。

    4. 用于学习 Spring® 和 Spring Boot 的 Code Katas

    Spring 课程由两个相关教程组成,分别向开发人员介绍 Spring Framework 和 Spring Boot,方法是解决代码 katas。

    整个课程都在 GitHub 上,您可以免费访问。通过位于assets\docs目录下的文档讨论的课程布局示例。

    学习本课程的最佳方式是在本地查看整个项目,然后查看:

    课程相当独立,熟悉 Spring 框架的人只需访问 Spring Boot 教程即可上手。

    但是,值得回顾一下并阅读 Spring 框架内容(即使没有尝试解决练习)。它们还涵盖了大部分重要的 Spring 主题,例如核心 Spring、Spring Boot、Spring Data JPA、Spring REST、Hibernate 等等

    我发现他们非常擅长深入学习 Spring 框架并练习使用 Spring 框架编写 Java 代码。

    5.春天:大局 [Pluralsight]

    本课程将带您进入 Spring Framework、Spring Boot 和其他 Spring 相关项目的奇妙世界,并在Pluralsight 平台上获得了惊人的 5 星评级。

    对企业级 Java 开发有一点基本了解的人都会同意,如果不深入了解 Spring Framework,它是毫无用处的。

    课程时长:2 小时
    课程评分:5 星(满分 5 星)
    课程讲师:Dustin Schultz
    课程价格:每年 149 美元

    在本课程中,您将获得 Spring Framework 各个方面的广泛、高级概述。在探索 Spring Boot 和 Spring Framework 之前,您将从了解 Spring 是什么开始。

    完成本课程后,您将对 Spring Framework 有基本的了解,这将使您成为一名成功的企业 Java 开发人员。

    顺便说一句,您需要Pluralsight 会员才能加入这门课程,费用约为每月 29 美元或每年 299 美元(14% 折扣)。他们现在还为其年度计划提供 40% 的折扣,这意味着您只需 149 美元即可获得它。

    我向所有程序员强烈推荐此订阅,因为它提供了对 7000 多个在线课程的即时访问,以学习任何技术技能。或者,您也可以使用他们的10 天免费通行证免费观看此课程。

    6. Spring Framework 5:Guru 初学者 [Udemy]

    本课程的主要 USP 是它是由畅销讲师 John Thompson 设计的,他还曾在 Visa、Kohls、联邦住房贷款银行和 Belk 百货公司等大公司担任 Spring 框架顾问。这意味着您可以选择该领域领先专家之一的想法。

    正如标题所示,本课程完全专注于 Spring Framework 5,它是 Spring 的最新版本。您将通过了解如何启动 Spring Boot 项目然后创建一个简单的 Book/Author 应用程序来开始本课程。

    课程时长:57 小时
    课程评分:4.5 星(满分 5 星)
    课程讲师:John Thompson
    *课程价格:14 美元*

    整个课程内容分为35个部分,526个讲座。它也是我最喜欢学习 Spring 框架的课程之一,因为它几乎涵盖了所有重要的 Spring 模块,如 Spring Boot、Spring Security、Spring Boot、Spring Data JPA、Reactive Spring 等。

    7. Spring:深度框架[LinkedIn学习]

    这是一门中级课程,将为您提供 Spring 框架的全面概述。该课程由专业软件架构师 Frank Moley 设计。

    本课程将使您能够使用 Spring 开发应用程序和 Web 服务,您还将学习如何配置 ApplicationContext,这是访问组件和加载文件的接口。您还将实际了解 Java 工作流程和 Spring 生命周期。

    课程时长:2 小时
    课程讲师:Frank Moley
    *课程价格:每年 12 美元*

    顺便说一句,您需要 LinkedIn Learning 会员才能观看这门课程,每月费用约为 19.99 美元,但您也可以通过他们的1 个月免费试用免费观看这门课程,这是探索他们的 16000+ 的好方法最新技术的在线课程。

    9. Spring.io 入门指南【官方】

    Spring Framework 的官方网站 Spring.io 也有许多入门指南,您可以使用这些指南来学习有关 Spring 框架的特定任务。

    这些指南提供了在 Spring 应用程序中完成常见开发任务的分步过程,例如如何上传文件或如何使用 Spring 使用 RESTful Web 服务。

    如果您想深入学习 Spring 框架,那么这些指南会有所帮助,整个页面值得收藏并一次又一次地重新访问,以成为更好的 Java 开发人员

    10.学习 Spring:认证课程[Baeldung]

    这是Java开发者可以同时学习Spring和Spring Boot的又一优质spring框架课程。Eugen 不需要任何介绍,因为他在过去的 9 年中一直在教授和实践 Spring,本课程是从头开始学习 Spring 5 和 Spring Boot 2 的最佳资源,以引导的、以代码为中心的方式。

    本 Spring 课程从非常基础的课程开始,您将从头开始学习 Spring 和 Spring Boot。他们提供分步视频和实践课程来指导您。这也是一门非常实用的课程,您将在课程中构建一个成熟的项目。

    这也是最新的课程,Eugen 最近对其进行了更新,并添加了几个涵盖最新更改的新模块,以使课程变得更好。例如,他添加了一个课程来学习如何将 Spring Boot 与 Docker 一起使用以及如何实现 Spring Security Authorization

    以下是课程新变化的完整列表:

    在模块 2 - 依赖注入和 Spring 上下文中:

    • 第 9 课:弹簧接线 - @Resource 和 @Inject
    • 第 10 课:调试和解决接线异常

    在模块 3 - 项目配置中:

    • 第 7 课:Spring Boot 默认属性
    • 第 8 课:部署 Boot 2 应用程序 - 其他选项

    在模块 5:持久性和数据访问中:

    • 第 6 课:JdbcTemplate 简介 - 理论
    • 第 7 课:JdbcTemplate 简介 - 实现

    在模块 6:Web 基础和 Spring MVC 中:

    • 第 6 课:Servlet 和 DispatcherServlet

    在模块 8:构建 REST API 中:

    • 第 7 课:Spring MVC 中的 HTTP 消息转换器简介

    在第 9 单元:Spring 中的高级特性中:

    • 第 7 课:Spring 安全授权
    • 第 9 课:使用 Docker 进行 Spring Boot

    由于最近的这些更新,他还将课程的价格在当前 297美元的基础上提高了 40 美元,但您仍然可以以原价购买该课程。是的,如果您将其与 Udemy 和 Coursera 的春季课程进行比较,该课程可能看起来有点贵,但这是值得的。

    他们也有不同的选择,例如,如果价格是一个问题,那么您也可以选择成本较低的Learn Spring Masterclass 。

    关于学习 Spring Framework 的常见问题

    现在,让我回答几个想要学习 Java 开发的 Spring 框架的 Java 开发人员经常会遇到的疑问。

    一、什么是Spring框架?
    Spring Framework 是世界上最流行的企业级 Java 开发框架之一。数以百万计的开发人员使用 Spring 来创建易于测试、可重用和高性能的代码。

    2. 有哪些不同的 Spring 框架?
    Spring 框架分为七个不同的模块。它们是 Spring Core、Spring AOP、Spring Web MVC、Spring DAO、ORM、Context 和 Web Flow。

    3、Spring好学吗?
    由于 Spring Framework 旨在解决 POJO,因此它非常简单易学。

    结论

    这就是 Java 开发人员最好的 Spring Framework 资源。正如我所说,如果您想掌握 Spring 框架,这些课程是最佳选择。您将在几周内从一个完全的初学者转变为一名 Spring 专家,并且您的技能将在就业市场上有巨大的需求。

    如果您喜欢此最佳 Spring 框架资源列表和在线课程、书籍和 katas,请随时与您的朋友和家人分享。另外,如果您有任何疑问或问题,请发表评论。

    PS - 如果你热衷于学习 Spring 框架并成为一名全栈 Java 开发人员,但正在寻找免费的在线课程,你可以在 Udemy 上查看这个Angular 和 Spring MVC 课程。它已完成,您只需要一个免费的 Udemy 帐户即可加入此课程。

    更多相关内容
  • 强化学习(RL)是一种流行的处理顺序决策任务的范式,其中agent只有有限的环境反馈。...为了解决这个问题,迁移学习被应用于强化学习,这样在一个任务中获得的经验可以在开始学习一个更困难的任务时得到利用。
  • 本篇介绍了大数据背景选择R语言作为数据挖掘工具的必要性,并详细演示了R和RStudio在windows和linux(ubuntu)的安装及使用,还演示了如何在linux系统安装RStudio-Server,实现多人协同操作的目的;...
  • 前端框架MVVM是什么(整理)

    万次阅读 多人点赞 2020-08-06 06:25:42
    双向数据绑定 前端數據的統一 前端數據的統一:前端应用相同数据的位置实现了数据的统一 双向数据绑定:綁定后vue好dom數據保持統一,一動全動,是前端的 双向数据绑定中的两向分别 view和viewmodel。 6、前端...

    阅读原文 

    目录

     

     

    回到顶部

    v   一、总结(点击显示或隐藏总结内容)

    一句话总结:vm层(视图模型层)通过接口从后台m层(model层)请求数据,vm层继而和v(view层)实现数据的双向绑定。

     

    1、我大前端应该不应该做复杂的数据处理的工作?

    不应该

    只要后端保证对外接口足够简单就行了,我请求api,你把数据返出来,咱俩就这点关系,其他都扯淡。

    后端:我们这里的业务逻辑和数据处理会非常复杂!
    前端:关我屁事!

    后端业务处理再复杂跟我们前端也没有半毛钱关系,只要后端保证对外接口足够简单就行了,我请求api,你把数据返出来,咱俩就这点关系,其他都扯淡。

     

    所以我这边开发也是同样的,前端就负责请求api就好,别的都不需要。

     

    2、mvc和mvvm的关系?

    改进版

    数据分离

    视图模型层

    c(控制层)被换成了vm(viewmodel)层

    MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。

    mvvm层实现了前后端更好的分离(前端需要的数据只需要请求后端的接口即可)

     

    3、MVVM框架编码实例?

    扯了这么多,并没有什么卵用。千言万语不如一个栗子来的干脆,下面用一个 Vue 实例来说明 MVVM 的具体表现。

    Vue 的 View 模板:

    <div id="app">
        <p>{{message}}</p>
        <button v-on:click="showMessage()">Click me</button>
    </div>
     

    Vue 的 ViewModel 层(下面是伪代码):

    var app = new Vue({
        el: '#app',
        data: {     // 用于描述视图状态(有基于 Model 层数据定义的,也有纯前端定义)
            message: 'Hello Vue!',  // 纯前端定义
            server: {}, // 存放基于 Model 层数据的二次封装数据
        },
        methods: {  // 用于描述视图行为(完全前端定义)
            showMessage(){
                let vm = this;
                alert(vm.message);
            }
        },
        created(){
            let vm = this;
    
            // Ajax 获取 Model 层的数据
            ajax({
                url: '/your/server/data/api',
                success(res){
                    // TODO 对获取到的 Model 数据进行转换处理,做二次封装
                    vm.server = res;
                }
            });
        }
    })
     

    服务端的 Model 层(省略业务逻辑处理,只描述对外接口):

    {
        "url": "/your/server/data/api",
        "res": {
            "success": true,
            "name": "IoveC",
            "domain": "www.cnblogs.com"
        }
    }
     

    这就是完整的 MVVM 编程模式。

     

    4、前端框架MVVM出现的最大意义是什么?

    开发效率

    前后端 业务逻辑 分离

    接口

    MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。

    MVVM用接口实现了前后端数据的通信,这样可以使前后端之间的业务逻辑没有什么关系。

    MVVM在感觉上要比mvc模式前后端要分的更开

     

    5、应用MVVM框架的vue.js框架的最主要作用是什么?

    双向数据绑定

    前端數據的統一

    前端數據的統一:前端应用相同数据的位置实现了数据的统一
    双向数据绑定:綁定后vue好dom數據保持統一,一動全動,是前端的

    双向数据绑定中的两向分别为 view和viewmodel。

     

    6、前端框架MVVM中的vm层是干嘛的?

    状态 行为

    DOM操作

    ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

     

    View一般就是我们平常说的HTML文本的Js模板,里面可以嵌入一些js模板的代码,比如Mustache,比如jstl类似的模板伪代码

    ViewModule层里面就是我们对于这个视图区域的一切js可视业务逻辑,举个例子,比如图片走马灯特效,比如表单按钮点击提交,这些自定义事件的注册和处理逻辑都写在ViewModule里面了

    Module就更简单了,就是对于纯数据的处理,比如增删改查,与后台CGI做交互

     

    7、MVVM最主要的特征是什么?

    前后端分离

    前后端分手大师——MVVM 模式

    或者说前后端更好的分离(接口来实现前后端的通信)

     

     

     

    回到顶部

    二、MVVM百度百科

    MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight、音频视频3D动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。

    外文名

    Model-View-ViewModel

    简    称

    MVVM

    例    如

    Silverlight、音频

    隶    属

    微软

    MVVM优点

    低耦合 可重用性

     

    回到顶部

    三、前后端分手大师——MVVM 模式(转)

    转自:前后端分手大师——MVVM 模式 - DOM哥 - 博客园
    https://www.cnblogs.com/iovec/p/7840228.html

    简而言之

    之前对 MVVM 模式一直只是模模糊糊的认识,正所谓没有实践就没有发言权,通过这两年对 Vue 框架的深入学习和项目实践,终于可以装B了有了拨开云雾见月明的感觉。

    Model–View–ViewModel(MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表。

    MVVM 源自于经典的 Model–View–Controller(MVC)模式(期间还演化出了 Model-View-Presenter(MVP)模式,可忽略不计)。MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示:

    MVVM模式

    MVVM 已经相当成熟了,主要运用但不仅仅在网络应用程序开发中。KnockoutJS 是最早实现 MVVM 模式的前端框架之一,当下流行的 MVVM 框架有 Vue,Angular 等。

    组成部分

    简单画了一张图来说明 MVVM 的各个组成部分:

    MVVM分层示意图

    分层设计一直是软件架构的主流设计思想之一,MVVM 也不例外。

    # View 层

    View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建,为了更方便地展现 ViewModel 或者 Model 层的数据,已经产生了各种各样的前后端模板语言,比如 FreeMarker、Marko、Pug、Jinja2等等,各大 MVVM 框架如 KnockoutJS,Vue,Angular 等也都有自己用来构建用户界面的内置模板语言。

    # Model 层

    Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开。后端的处理通常会非常复杂:

    前后端对比

    后端:我们这里的业务逻辑和数据处理会非常复杂!
    前端:关我屁事!

    后端业务处理再复杂跟我们前端也没有半毛钱关系,只要后端保证对外接口足够简单就行了,我请求api,你把数据返出来,咱俩就这点关系,其他都扯淡。

    # ViewModel 层

    ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

    没有什么是一个栗子不能解决的

    扯了这么多,并没有什么卵用。千言万语不如一个栗子来的干脆,下面用一个 Vue 实例来说明 MVVM 的具体表现。

    Vue 的 View 模板:

    <div id="app">
        <p>{{message}}</p>
        <button v-on:click="showMessage()">Click me</button>
    </div>
     

    Vue 的 ViewModel 层(下面是伪代码):

    var app = new Vue({
        el: '#app',
        data: {     // 用于描述视图状态(有基于 Model 层数据定义的,也有纯前端定义)
            message: 'Hello Vue!',  // 纯前端定义
            server: {}, // 存放基于 Model 层数据的二次封装数据
        },
        methods: {  // 用于描述视图行为(完全前端定义)
            showMessage(){
                let vm = this;
                alert(vm.message);
            }
        },
        created(){
            let vm = this;
    
            // Ajax 获取 Model 层的数据
            ajax({
                url: '/your/server/data/api',
                success(res){
                    // TODO 对获取到的 Model 数据进行转换处理,做二次封装
                    vm.server = res;
                }
            });
        }
    })
     

    服务端的 Model 层(省略业务逻辑处理,只描述对外接口):

    {
        "url": "/your/server/data/api",
        "res": {
            "success": true,
            "name": "IoveC",
            "domain": "www.cnblogs.com"
        }
    }
     

    这就是完整的 MVVM 编程模式。

    代码执行之后双向绑定的效果如下:

    Vue实现的响应的数据绑定

    嘿嘿,前后端可以成功分手了,以后再也不用关心后端个锤子开发进度\暴怒脸,复杂实现,blabla...,尽情享用前端如丝般顺滑的开发快感吧:)

     

     

    回到顶部

    四、不要听吹牛逼什么前端MVVM框架就是好,其实都是一帮没学好分层设计的搞出来的,让你彻底看清前端MVVM的本质(转)

    转自:不要听吹牛逼什么前端MVVM框架就是好,其实都是一帮没学好分层设计的搞出来的,让你彻底看清前端MVVM的本质 - 薛端阳 - 博客园
    https://www.cnblogs.com/xueduanyang/p/3601471.html

    最近前端圈子里面,发现大家都在热炒概念,什么knockout,angularJs,都被捧成神了,鄙人不才,最近心情也不好,特地写这篇文章来找骂

    写代码的码农都知道,Java社区虽然不是一个提出分层思想的,确实贯彻的最好的,如今是个Java开发都不会不知道SSH的开发模式,从MVC到MVVM的概念的热炒,其实真没什么技术进步

    (如果你觉得本文言辞激烈,过于愤世嫉俗,实在看不下去,欢迎移步另一位园友的分层进化史科普文章http://www.cnblogs.com/indream/p/3602348.html) 

    先看什么是MVVM

    image

    View一般就是我们平常说的HTML文本的Js模板,里面可以嵌入一些js模板的代码,比如Mustache,比如jstl类似的模板伪代码

    ViewModule层里面就是我们对于这个视图区域的一切js可视业务逻辑,举个例子,比如图片走马灯特效,比如表单按钮点击提交,这些自定义事件的注册和处理逻辑都写在ViewModule里面了

    Module就更简单了,就是对于纯数据的处理,比如增删改查,与后台CGI做交互

     

    那么什么是MVVM框架呢??一般他们都是这么做的

    1.  定义一串所谓的伪模板代码,例如通过className标注,或者自定义tag的方式,将一段html文本区域给标注声明起来,意思就是喊一嗓子,“喂,兄弟们,这块地方我占了,要拉屎去别处拉去”

    2.  通过类似jstl之类lamda表达式,来做js模板,“拜托伙计,天堂有路你不走,非要自己搞一套,你就不能暴露接口让大家用自己的模板语言,比如Mustache或者jtpl吗?”

    3.  很傻比的封装一串自己的所谓数据模块组件,与不同类型的数据源做数据传输和适配,一般都不会分层很清晰,加入后台数据字段改了,写框架的都没脑子的,从来不做数据字段的自定义适配(举个例子,原来后台传递的字段是person.userName,现在改成了小写,person.username,你就傻逼的去吧模板再改一下吧,其实要解决这个问题,非常简单,在MVVM层中引入一层DO,领域对象层,Module到DO之间还有一层转换就可以搞定这个问题)

    4.  非不暴露自己的自定义事件模型,就是那个观察者模式啦,自己乱七八招在页面上绑定一堆form change之类的事件,以实现View与Module的单向绑定

    5.  所谓的双向绑定,也就是OOP语言中早被烂透了的getter,setter模型,ES5+可以用defineProperty,低版本就需要自己在js object赋值的时间做写死代码方式的处理了

     

    我们再来看细节

    1. 双向绑定

    号称是最难理解的地方,其实是框架的作者根本就没理解Java社区对于软件开发分层理解的精髓

    image

    标准的数据驱动开发,应该如上图所示,在一个View的生命周期内,一个ViewModule会管理一个DomainObject(业务模型),一个DO可能包括多个Module数据模型,一个Module可能来自多个数据源,而不是想很多所谓的MVVM框架那样强迫一个M来一个数据源

    按照上图标准分层方式来划分的好处,在于,逻辑清晰,Module层粒度够细,可以被多次复用

    DO层与VM层View层属于一一对应关系,方便对数据做增删改查的同步

    每一层应该是独立的,非一定要使用MVVM框架的紧耦合,可以用自己使用不同的js插件或者模块实现MVVM

    我们抛弃框架,单纯的看数据,其实我们要解决的问题很简单

    a) 当DO对象属性放生变化时候,通知View更新

    b) 当View上表单值放生变化时,通知DO更新,并异步通知队列同步到数据源

    先来看问题a,这个最简单,DO是一个基本的Javascript Object,我们在View上的模板显示是这个Object.property,

    改变一个Object对象的方式无非几种,一种是

    a) 显示Object.property = ‘我是傻逼’

    b) xxxx.methodName(Object, ‘property’, ‘我是傻逼’)

    c) xxxx.merge(Object, {‘property’: ‘我是傻逼’})

    如果是a的情况,ES5+,可以通过设置Object.defefineProperty(‘property’,{set: functiono(){},get:function(){}}),来做赋值和取值的监控触发

    对于IE8一下,因为js不支持运算符重载,所以暂时没有好的办法,所以如果只考虑移动端的话,直接defineProperty就全部搞定,如果是要考虑PC的话,就不建议开发者使用直接赋值的方式,参考java的开发模式,也是推荐OOP时候,使用set方式赋值,而不是直接=赋值

    当然了,如果你非要兼容IE8一下的话,用定时器做轮训,配合for in 反射,通过脏数据与原始备份对比的方法也是一种办法,不过这种办法在当前页面非常耗性能,由于IE8一下不支持多线程,HTML5 worker,如果未来flash 插件支持多线程的话,倒是可以用js和flash插件做线程交互的方式做脏数据检测

    如果是b的情况,那就太简单了,在methodName里面触发对于该属性修改的回调即可,如何注册回调呢,首先我们要实现一个类似Dom Event的自定义对象的Event模型,然后通过类似Dom Event的注册事件方式,注册观察者,订阅事件,当执行了methodName时候,发送消息,通知所有订阅者执行回调

    如果是c的情况,类似b一样处理

    这样一看,双向数据绑定的问题就非常简单的解决了

     

    我们再来看另外一个MVVM的问题,非简单数据模型,复合数据模型(DO的属性值不是一个string,而是一个Object,且这个Object可能还嵌套多层Obejct的时候)的处理办法,这个一般的MVVM框架直接不考虑,或者通过长字段名的方式绕过这个问题

    这个问题是这样的,早在10几年前,java structs框架流行的时候就出现了,当一个表单,出现需要对两个Java Bean做update操作时候,一个bean是user,一个bean是成绩

    对应的表单字段名,就是 user表.name,user表.id,score表.point,

    在struct2里面,处理逻辑是把 “点”作为特殊符号,在做form序列化时候,非包含点的字段的值都是string,包含点的字段是一个Object,比如刚才的form序列化之后结果就是 { user: {id :’’ , name: ‘’}, score: {id: ‘’, point: ‘’}}

    同理在MVVM实现时,也是一样,认为点是分割对象的关键字,这样我们就可以实现把多个对象嵌套到View模板里面,实现复合Object的双向映射

     

    最后一个问题,也就是高级MVVM编程里面必须要面对的问题,就是自定义事件的广播和冒泡,我看过大多数的MVVM框架,对于广播,这块有部分实现了,但是对于冒泡一个都没实现

    其实这个真的不是很复杂的问题,事件广播,这个最简单,三岁小孩都能写,我们在注册回调时候,不是有一个事件队列吗,在回调时候,通过特殊标记位,控制是否继续扩散广播,还是执行完毕终止即可

    而自定义事件的冒泡要骚骚复杂一些,他是由于OOP编程里面的继承和包含关系引申而来的,我们先说包含关系,前面说了MVVM框架里面,都会声明一块地方为VM控制区域,一般垃圾的框架都不会考虑,VM嵌套的情况,因为图简单吗

    但是实际开发过程中,你会遇到很多这种情况,就是VM复用的问题,一般都是发现使用了MVVM框架之后,发现VM定义的太大,没法复用,如果要复用VM就又发现VM定义的太小,出现需要VM嵌套的情况没法用

    其实简单,我们知道DOM事件是有冒泡的,VM同理,只要在自定义事件模型里面定义了VM的父子关系,或者同级关联关系,即可实现VM的自定义事件的广播和冒泡,另外也解决了VM复用的问题,可以让代码颗粒度更小

     

    另外那种,声明式编程这种老掉牙的概念就真的别在吵了,还记得10几年前的structs的tag吗,js圈子里面这种通过自定义tag,自定义className,自定义属性,挂载js来自定识别执行逻辑的例子大把皆是,还是建议广大前端开发,不要浮躁,多像java社区学习,多多从根本上了解分层理念的精髓,不要听了吹牛逼,听风就是雨,还是多了解原理才是真理啊

     

    最近心情很不好,股票大跌,公司的事情你懂的,写这篇文档纯属没事找事,欢迎广大道友开骂,来陪我大战三百回合

     

     

     

    回到顶部

    五、MVVM核心概念(转)

    转自:MVVM核心概念 - iammackong - 博客园
    https://www.cnblogs.com/iammackong/articles/3312565.html

    MVVM模式是Model、View、ViewModel的简称,最早出现在WPF,现在Silverlight中也使用该模式,MVVM模式是对MVC模式的变种。哪儿变了?我认为MVVM和MVC的主要变化在于MVVM更适合于XAML。

    2011-05-03 14h43_20

    MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处

    1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。

    2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

    3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。

    4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

    mvvm 2011-07-02 15h23_34

    如果用Interface接口来表达,基本就是这么个意思:

    复制代码

    复制代码

    1 publicinterface IView
    2 {
    3 IViewModel ViewModel { get; set; }
    4 }
    5
    6  publicinterface IViewModel
    7 {
    8 IModel Model { get; set; }
    9
    10 ///<summary>
    11 /// a property that states the controller is busy doing something (like fetching data from a service),
    12 /// usually the iterface should be blocked
    13 ///</summary>
    14  bool IsBusy { get; }
    15 }

    复制代码

    复制代码

    MVVM_1

    MVVM的Model、View、ViewModel分工

    1. View

    • 负责界面和显示,界面构成元素有window, controls, page, dataTemplete, custom controls….
    • 代码通常有XAML和XAML.CS组成,但后台代码应该很少
    • 通过DataContext和ViewModel绑定
    • 不直接和Model交互!
    • 控件可以和ViewModel的公共属性绑定,update需要双向绑定
    • 控件可以触发Behavior/Command调用ViewModel的方法,Command是View到ViewModel的单向通讯 (View中触发事件,ViewModel中处理事件)

    2. ViewModel

    • 主要包括界面逻辑和模型数据封装,Behavior/Command事件响应,绑定的属性定义等
    • ViewModel继承Model类,或者是Model的继承类
    • 是view和model的桥梁,是对Model的抽象,例如,model中数据格式是“年月日”,可以在viewModel中转换model中的数据为“日月年”以供视图(view)显示。
    • 维护视图状态
    • 实现属性或集合的change notification
    • 2011-07-02 15h21_12

    3. Model

    • 数据和业务逻辑
    • 客户端领域模型
    • 由data entities, business objects, repositories and services构成
    • 可以实现属性或集合的change notification
    • 可以实现validation 接口例如 IDataErrorInfo

    MVVM

    View和ViewModel主要通过数据绑定和Command/Behavior进行交互,如下图所示:

    2011-05-03 10h24_31

    一个例子并且附代码下载(Command未示例)

    有关Model(模型)和DTO的问题

    2011-05-03 17h33_31

    前面说的Model是客户端的,但实际上Domail Model存在服务器端(靠近数据库)和那就需要和客户端搞映射DTO(Data Transfer Ojbect,数据传输对象,带序列化标记,用来远程调用)。在Silverlight中有个很方便的东西来实现这个DTO过程和序列化,那就是WCF RIA Service和DomainService。如果你创建一个简单的Silverlight应用并且调用WCF RIA Service,基本上会生成DTO Model: ObjectContext(EntityObject)。(也有人喜欢在Model里面调用RiaSerivce实现load,save等等,个人认为不太合适,可以参考这篇文章),此外,参考这篇文章:《WCF RIA Services and a guide to use DTO/”Presentation Model”

    DataFlow

    代码例:

    复制代码

    复制代码

    publicpartialclass MyModelsEntities : ObjectContext
    {



    }

    [EdmEntityTypeAttribute(NamespaceName="MyModels", Name="MyEntity")]
    [Serializable()]
    [DataContractAttribute(IsReference=true)]
    publicpartialclass MyEntity: EntityObject
    {



    }

    复制代码

    复制代码

    MVVM的实践要点

    1. View分离要彻底,不要有坏味道

    视图(view)部分,xaml.cs 应该只有很少的代码或没有代码,如果你的xaml.cs包含大量的代码,那么很可能你MVVM用的不对头,需要检查其中代码的坏味道。Xaml和xaml.cs 只能包含处理界面、视图、显示样式、视图元素之间的交互、视图元素动画,等等的内容

    2. ViewModel要可测试

    从重构的观点看,如果你的代码中ViewModel是可测试的,有详细的单元测试Unit Test,你的代码是OK的,否则需要检查其中的坏味道。

    MVVM Basic

    更多MVVM的内容且看下回分解。(附:本例子且附代码下载(Command未示例),channel9有一个MVVM的视频很好,PPT和视频演示的源码

     

    我的旨在学过的东西不再忘记(主要使用艾宾浩斯遗忘曲线算法及其它智能学习复习算法)的偏公益性质的完全免费的编程视频学习网站: fanrenyi.com;有各种前端、后端、算法、大数据、人工智能等课程。

    版权申明:欢迎转载,但请注明出处

    一些博文中有一些参考内容因时间久远找不到来源了没有注明,如果侵权请联系我删除。

    博主25岁,前端后端算法大数据人工智能都有兴趣。

    大家有啥都可以加博主联系方式(qq404006308,微信fan404006308)互相交流。工作、生活、心境,可以互相启迪。

    作者相关推荐

    前端框架MVVM是什么(整理)

    范仁义 2018-11-05 08:32 阅读:4602 评论:0 推荐:0 编辑

    vue.js最最最最简单实例

    范仁义 2018-08-05 22:54 阅读:2951 评论:0 推荐:0 编辑

    翻过这道山,就有人听到你的故事。

    学习

    轻学无用:学生时代学html、java、c++、php、安卓、python等和之前学英语的感受,轻学无用,不为需求学,不学全面无用。

    学习和游戏的区别的启示:像游戏一样,每次学习完,你告诉自己,你学到了什么,你收获到了什么。是你在游戏中获得了多少道具装备等级场景,这个给你的舒适满足感。问题不是你学了多少,而是你到底学会了多少。

    命名:要给招数起名字,没有名字,没有经过大脑,没有深刻印象,他们永远都不是自己的,找一个自己喜欢印象深刻的名字做对应。

    多遍:学习视频或者书并不是只看一遍就够了,因为一遍能收获的内容非常有限,当然也可以只看一遍,那就需要好好记忆,多提问,多入脑子。

    每日清0规律:无论是对学习的劳累,还是对游戏的无趣,还是对生活的感受,每日睡一觉之后就可以清空绝绝绝大部分,只有极极少一部分会留下来。

    接触:那些不好的东西,不要接触,停不下来的,比如游戏,各种接触都是触发的因素,同理可以应用于学习。

    学习的方式:legend2 + legend3 + 知识总结(代码)文档(系统) + 详细知识word总结文档(系统+分类+增加和完善)。

    知识的触类旁通:知识之间是触类旁通的,比如你更加深刻的学了爬虫之后,你可能会对网站开发更加了解。

    做人

    理解:能够使用并不代表理解,比如2岁大的宝宝,在这个阶段,宝宝可以正确告诉别人“我是男孩”或“我是女孩”。但是,这只是一种简单的“转述”行为,因为父母告诉他(她)“你是男(女)宝宝”,而尚未形成真正意义上的性别意识。

    技巧:无论是做人做事还是学习,都是需要技巧的。

    矫枉过正:很多事情都容易矫枉过正(技巧和局势才是通用的)

    结善缘:

    事情原因:生活中分析问题的原因,然后解决问题,会简单很多

    实力为尊:

    觉得自己是否受伤完全是自己的主观情绪:只要放平心态,弄清楚事情发展的规律,看的更透彻,或者转换角色,这样就不必自己给自己找麻烦了,反而能把所有的事情掌控于手心。只要自己不觉得受伤,就没有人可以伤害自己。

    《康熙王朝》:少年康熙被别人玩弄于股掌之间启示:不要被别人玩弄于股掌之间,要做的是想通(可能别人更加高明)、忍耐和积蓄自己的力量

    《易中天讲三国》袁绍性格:【感觉大部分人都是袁绍】:袁绍好面子,凭喜好办事,好听马屁,没有容人之量

    不是放弃,而是最快最便捷:

    人生如负重致远不可急躁:

    不要务虚名而致实祸:

    强夸百害无益:

    人的逐利本质:

    情感回馈:比如看到的l的例子

    感悟

    身体:安全和身体最重要了,这个才是真正的本钱

    刷反思:遇到的各种心理问题和心态的问题都可以去刷反思

    悟透:所有错误的抉择(所有的挣扎)都是因为没想明白,没领悟透。

    人生无大事:重在坚持,重在平时。所谓的希望并非是希望,或者说并非是希望的全部,比如词根词缀,语法,再到背,生活,没有人会告诉你这是几分之几的钥匙,这便是有趣的点。所谓的绝望,未必是绝望,比如大四下,很有可能是希望和机遇。

    换环境:是个可行的方法,但是我不能什么事情都依靠外物啊,内在觉醒,经历,悟透才是王道,而且,内在不改变,激励不够,换个环境真的有用么。

    同,所得:景色不过如此,每日的热搜也不过如此,每日的朋友圈也不过如此,每日的电影也不过如此,每日的游戏(打怪,升级,竞技)也不过如此。重点是,每日,自己到底收获了什么(学到了什么)。所谓的新鲜事真的新鲜吗,那么多年的历史(时间映射和地点映射)还涵盖不了当下么,再稀奇百倍的事情也发生过。

    监管:在心智和阅历不够的情况下缺乏监管是相当恐怖的事情(小孩子没有明辨是非能力,易冲动,缺乏监管很容易从恶),事实上,就算心智和阅历很够,缺乏监管同样是很恐怖的事情(贪官贪财,官员渎职)。

    约定:初期规则和约定:https://www.bilibili.com/video/av35953030/?p=57。

    六欲,迷茫:人皆有六欲,人皆会迷茫,不必太自责,及时收住就对了,不是看谁不迷茫,而是看谁能够及时悬崖勒马,事实上,所有的人都会迷,无一例外。及时亡羊补牢。

    困境:有多少人困在这一层,而生活有趣的是,没有人告诉你需要去突破。(也没有人告诉你要怎么突破)

    生活的真谛:整日饱食终日的玩不是生活的意义,整日疲于奔命的学也不是生活的意义,生活的意义在于在于实力稳步提升,劳逸结合,享受生活。

    危机也是机遇:无论是修真小说还是实际。

    满足与斗志:人一旦满足就容易不思进取。但是人满足人会很开心或者很平静。人不满足的话在一定情况下会激发斗志。所以可以在满足和不满足中找一个平衡。比如 心理满足之后会想去玩游戏,而不是学习或工作。

    展开全文
  • 课程上架与下架 营销信息 营销信息,其实就是设置课程的详细信息 回显课程信息 修改课程信息,包含了图片上传 配置课时 对课程下所属的章节与课时进行配置(一个课程对应多个章节,一个章节有多个课时) ...

    巩固web阶段知识,完成后台管理系统中的课程管理模块,该模块包含了添加课程、配置课程相关信息、管理课程章节等功能。

    项目地址:课程管理模块

    产品的原型图

    课程管理

    • 实现以下功能:
      • 展示课程列表
      • 根据课程名和状态进行查询
      • 新建课程
      • 课程上架与下架

    在这里插入图片描述

    营销信息

    • 营销信息,其实就是设置课程的详细信息
      • 回显课程信息
      • 修改课程信息,包含了图片上传

    在这里插入图片描述

    配置课时

    • 对课程下所属的章节与课时进行配置(一个课程对应多个章节,一个章节有多个课时)
      • 以树形结构的下拉框形式,展示课程对应的章节与课时信息
      • 添加&修改章节功能
      • 修改章节状态功能
      • 添加&修改课时功能

    在这里插入图片描述

    课程管理模块表设计

    在这里插入图片描述

    1. 项目架构

    1.1 前后端分离架构

    前后端分离的核心思想就是前端HTML页面通过AJAX调用后端的API接口,并通过JSON数据进行交互。

    1.1.1 前后端耦合的缺陷 (以JSP为例)

    1. UI出好设计图之后,前端开发工程师只负责将设计图切成HTML,需要由Java开发工程师来将HTML套成JSP页面,修改问题的时候需要双方协同开发,效率低下。
    2. JSP页面必须要在支持Java的WEB服务器上运行(如Tomcat、Jetty等),无法使用Nginx等(官方宣称单实例HTTP并发高达5W),性能提升不上来。
    3. 第一次请求JSP,必须要在WEB服务器中编译成Servlet,第一次运行会较慢。 之后的每次请求JSP都是访问Servlet再用输出流输出的HTML页面,效率没有直接使用HTML高。

    1.1.2 前后端分离的优势

    1. 前后端分离的模式下,如果发现Bug,可以快速定位是谁的问题,不会出现互相踢皮球的现象
    2. 前后端分离可以减少后端服务器的并发/负载压力。除了接口以外的其他所有HTTP请求全部转移到前端Nginx上,接口的请求则转发调用Tomcat。
    3. 前后端分离的模式下,即使后端服务器暂时超时或宕机了,前端页面也会正常访问,只不过数据刷不出来而已。
    4. 前后端分离会更加合理的分配团队的工作量,减轻后端团队的工作量,提高了性能和可扩展性。

    在这里插入图片描述

    1.2 接口文档

    1.2.1 什么是接口文档?

    ​ 在我们的项目中使用的是前后端分离开发方式,需要由前后端工程师共同定义接口,编写接口文档,之后大家都根据这个接口文档进行开发,到项目结束前都要一直进行接口文档的维护。

    1.2.2 为什么要写接口文档?

    1. 项目开发过程中前后端工程师有一个统一的文件进行沟通交流,并行开发
    2. 项目维护中或者项目人员更迭,方便后期人员查看、维护

    1.2.3 接口规范是什么?

    一个接口的描述至少包括下面几项:

    • 名称: findCourseList

    • 描述: 根据条件查询课程信息

    • URL: http://localhost:8080/lagou_edu_home/course/

    • 请求方式: GET

    • 请求参数

      methodName:"findCourseList";
      
    • 响应结果

      {
          "status": "0",
          "msg": "success"
      }
      

    1.3 技术选型

    1.3.1 前端技术选型

    前端技术说明
    Vue.js是一套用于构建用户界面的渐进式JavaScript框架
    Element UI库element-ui 是饿了么前端出品的基于 Vue.js的后台组件库,
    方便程序员进行页面快速布局和构建
    node.js简单的说 Node.js 就是运行在服务端的 JavaScript 运行环境
    axios对ajax的封装, 简单来说就是ajax技术实现了局部数据的刷新,axios实现了对ajax的封装

    1.3.2 后端技术选型

    后端技术说明
    Web层a) Servlet:前端控制器
    b) Filter:过滤器
    c) BeanUtils:数据封装
    Service层a) 业务处理
    dao层a) Mysql:数据库
    b) Druid:数据库连接池
    c) DBUtils: 操作数据库

    2. 环境搭建

    2.1 创建项目

    使用Maven快速构建工程,New Module 项目名为: lagou_edu_home

    在这里插入图片描述

    创建Maven项目

    2.2 项目目录

    在这里插入图片描述

    2.3 导入pom.xml

    见项目地址pom.xml文件

    2.4 导入工具类及配置文件

    见项目地址utils文件夹、resources文件夹内容

    2.5 导入实体类

    导入表对应的实体类

    在这里插入图片描述

    Course.java

    package com.lagou.pojo;
    
    import com.alibaba.fastjson.annotation.JSONField;
    import lombok.Data;
    
    
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 课程类
     * */
    @Data
    public class Course implements Serializable {
    
      //使用 JSONField 设置ordinal的值,来对转换成的JSON数据进行排序
      //课程ID
      @JSONField(ordinal = 1)
      private int id;
    
      //课程名称
      @JSONField(ordinal = 2)
      private String course_name;
    
      //课程介绍
      @JSONField(ordinal = 3)
      private String brief;
    
      //讲师名称
      @JSONField(ordinal = 4)
      private String teacher_name;
    
      //讲师介绍
      @JSONField(ordinal = 5)
      private String teacher_info;
    
      //课程原价
      @JSONField(ordinal = 6)
      private double price;
    
      //原价标签
      @JSONField(ordinal = 7)
      private String price_tag;
    
      //课程优惠价
      @JSONField(ordinal = 8)
      private double discounts;
    
      //课程概述
      @JSONField(ordinal = 9)
      private String preview_first_field;
    
      //课程概述第二个字段
      @JSONField(ordinal = 10)
      private String preview_second_field;
    
      //分享图片url
      @JSONField(ordinal = 11)
      private String course_img_url;
    
      //分享标题
      @JSONField(ordinal = 12)
      private String share_title;
    
      //分享描述
      @JSONField(ordinal = 13)
      private String share_description;
    
      //课程描述
      @JSONField(ordinal = 14)
      private String course_description;
    
      //排序
      @JSONField(ordinal = 15)
      private int sort_num;
    
      //课程状态,0-草稿,1-上架
      @JSONField(ordinal = 16)
      private int status;
    
      //创建时间
      @JSONField(ordinal = 17)
      private String create_time;
    
      //修改时间
      @JSONField(ordinal = 18)
      private String update_time;
    
      //是否删除
      @JSONField(ordinal = 19)
      private int isDel;
    
      @JSONField(ordinal = 20)
      private String share_image_title; //分享图title
    
    
      //使用JSONField(serialize = false)排除不需要转换的字段
    
      @JSONField(serialize = false)
      private int total_course_time; //课时数
    
      @JSONField(serialize = false)
      private int sales; //显示销量
    
      @JSONField(serialize = false)
      private int actual_sales; //真实销量
    
      @JSONField(serialize = false)
      private int is_new; //是否新品
    
      @JSONField(serialize = false)
      private String is_new_des; //广告语
    
      @JSONField(serialize = false)
      private int last_operator_id; //最后操作者
    
      @JSONField(serialize = false)
      private int total_duration; //总时长
    
      @JSONField(serialize = false)
      private long course_type; //课程类型
    
      @JSONField(serialize = false)
      private String last_notice_time;  //最后课程最近通知时间
    
      @JSONField(serialize = false)
      private long is_gray; //是否是灰度课程
    
      @JSONField(serialize = false)
      private long grade; //级别
    }
    

    2.5.1 Lombok

    1) Lombok介绍

    在项目中使用Lombok可以减少很多重复代码的书写,比如getter/setter/toString等方法的编写。

    2) IDEA中安装 lombok插件

    打开IDEA的Setting –> 选择Plugins选项 –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA

    在这里插入图片描述

    3) 添加依赖

    在项目中添加Lombok依赖jar,在pom文件中添加如下部分

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.0</version>
        <scope>provided</scope>
    </dependency>
    

    4) Lombok常用注解

    • @Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法

    • @ToString : 作用于类,覆盖默认的toString()方法 ,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段

    • @AllArgsConstructor:生成全参构造器

    • @NoArgsConstructor:生成无参构造器

    • @Data: 该注解使用在上,该注解会提供 gettersetterequalshashCodetoString 方法。

    2.5.2 FastJson的@JSONField注解

    通过 @JSONField 我们可以自定义字段的名称进行输出,并控制字段的排序,还可以进行序列化标记。

    • 指定 name属性, 字段的名称
    • 使用 ordinal属性, 指定字段的顺序
    • 使用 serialize属性, 指定字段不序列化

    Fastjson 简明教程

    3. 开发流程

    需求分析、数据库表分析、实体类设计、Dao接口及实现类编写、Service接口及实现类编写、Servlet编写。

    其中Servlet层有技巧:BaseServlet简化Servlet操作

    4. 功能实现(举例)

    4.1 功能一: 查询课程列表信息

    4.1.1 需求分析

    页面分析,需要展示哪些数据

    在这里插入图片描述

    4.1.2 编写代码

    Dao层编写

    修改CourseDao,添加 findCourseList 方法

    • idea中try/catch快捷键

      选中要包裹代码 + Ctrl + Alt +t

    接口 CourseDao
        //查询课程列表信息
      	public List<Course> findCourseList();
    
    实现类 CourseDaoImpl
    	 @Override
        public List<Course> findCourseList() {
    
            try {
                //1.创建QueryRunner
                QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
    
                //2.编写SQL
           String sql = "SELECT id,course_name,price,sort_num,STATUS FROM course where id_del =  ?";
    
                //3.执行查询
              List<Course> courseList = qr.query(sql, new BeanListHandler<Course>(Course.class), 0);
    
                return courseList;
    
            } catch (SQLException e) {
                e.printStackTrace();
                return null;
            }
        }
    

    逻辑删除

    • 逻辑删除的本质是修改操作,所谓的逻辑删除其实并不是真正的删除,而是在表中将对应的是否删除标识做修改操作。
    • 比如:0是未删除,1是删除。在逻辑上数据是被删除的,但数据本身依然存在库中。

    物理删除

    • 物理删除就是真正的从数据库中做删除操作了。
    Service层编写

    修改CourseService 添加 findCourseList 方法

    接口 CourseService 
    	public List<Course> findCourseList();
    
    实现类 CourseServiceImpl
    	//创建 CourseDao
        CourseDao courseDao = new CourseDaoImpl();
    
        @Override
        public List<Course> findCourseList() {
    
            //调用Dao 进行查询
            return courseDao.findCourseList();
        }
    
    Servlet编写

    接口开发规范

    ​ 我们在做的是一个前后端分离项目、需要通过接口文档对接的项目。所以开发过程中要仔细查看前端所需的api接口和参数字段。

    为了严格按照接口进行开发,提高效率,对请求及响应格式进行规范化。

    开发规范
    1、get 请求时,采用key/value格式请求,Servlet中可以使用 getParameter() 获取。
    2、post请求时有三种数据格式
    第一种: Json数据,json类型的数据 Servlet中使用 fastjson进行解析
    第二种: 提交form表单数据
    第三种: 文件等多部件类型(multipart/form-data)
    3、响应结果统一格式为json

    为什么使用JSON?

    ​ 数据格式比较简单,易于读写,JSON格式能够直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,但是完成的任务不变,且易于维护。

    JSON 语法

    本项目使用的是 JSON解析工具为阿里巴巴的fastjson,maven工程导入下面的依赖即可。

    Fastjson 简明教程

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.1.37</version>
    </dependency>
    
    <dependency>
        <groupId>com.colobu</groupId>
        <artifactId>fastjson-jaxrs-json-provider</artifactId>
        <version>0.3.1</version>
    </dependency>
    

    接口文档

    ​ 前端的开发基于服务端编写的接口,如果前端人员等待服务端人员将接口开发完毕再去开发前端内容这样做效率是非常低下的,所以当接口定义完成,可以使用工具生成接口文档,前端人员查看接口文档即可进行前端开发,这样前端和服务人员并行开发,大大提高了生产效率。

    接口 查询课程列表信息

    • 名称: findCourseList
    • 描述: 查询课程列表信息
    • URL: http://localhost:8080/lagou_edu_home/course
    • 请求方式: GET
    • 请求参数
    字段说明类型是否必须备注
    methodName要访问的功能名String该字段必须填写,用来确定要访问是
    哪一个的方法
    • 请求参数示例:
    methodName: "findCourseList"
    
    • 响应结果
    字段说明类型是否必须备注
    id课程idint
    course_name课程名称String
    price课程价格double课程的原价格
    sort_num课程排序int数字越大,越排在后面
    status课程状态int0-草稿,1-上架
    • 响应结果示例
    [{
    	"id": 1,
    	"course_name": "32个Java面试必考点",
    	"price": 8000,
    	"sort_num": 1,
    	"status": 1
    }]
    

    编写CourseServlet

    在CourseServlet中添加 findCourseList方法

    @WebServlet("/course")
    public class CourseServlet extends BaseServlet {
    
        //查询课程信息列表
        public void findCourseList(HttpServletRequest request, HttpServletResponse response){
    
            try {
                //1.接收参数
    
                //2.业务处理
                CourseService cs = new CourseServiceImpl();
                List<Course> courseList = cs.findCourseList();
    
                //3.响应结果
                //SimplePropertyPreFilter 指定要转换的JSON字段
                SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
                        "id","course_name","price","sort_num","status");
    
                String result = JSON.toJSONString(courseList,filter);
                response.getWriter().print(result);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • SimplePropertyPreFilter:结合FastJson指定要转换的JSON字段
      • 定制序列化:SimplePropertyPreFilter filter = new SimplePropertyPreFilter(类名.class,"字段1","字段2");
      • JSON.toJSONString(object,filter);(object是Java对象)

    4.1.3 Postman测试接口

    1. 发送请求到指定的
    http://localhost:8080/lagou_edu_home/course?methodName=findCourseList
    

    在这里插入图片描述

    4.2 功能实现中用到的知识及技巧

    4.2.1 功能:根据课程名称和课程状态进行查询

    4.2.1.1 WHERE 1=1

    多条件查询要注意多个参数情况下SQL的编写

    where默认为1=1,这样用户即使不选择任何条件,sql查询也不会出错;如果还选择了其他的条件,就不断在where条件后追加 and语句就行了。

    如果不用1=1的话,每加一个条件,都要判断前面有没有where 条件,如果没有就写where,有就写and语句,因此用1=1简化了应用程序的复杂度。

    4.2.1.2 StringBuffer拼接SQL字符串
    4.2.1.3 like查询(模糊查询)需要拼接 %

    Dao层编写

    实现类
    	/**
         * 根据课程名称 课程状态 查询课程信息
         * */
        //根据条件查询课程信息
        @Override
        public List<Course> findByCourseNameAndStatus(String courseName, String status) {
    
            try {
            //1.创建QueryRunner
            QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
    
            //2.编写SQL 当前的查询为多条件不定项查询
            //2.1 创建StringBuffer 对象,将SQL字符串 添加进缓冲区
            StringBuffer sb = new StringBuffer("SELECT id,course_name,price,sort_num,STATUS FROM course WHERE 1=1 and is_del = ? ");
    
            //2.2 创建list集合 保存参数
            List<Object> list = new ArrayList<>();
            list.add(0);
    
            //2.3 判断传入的参数是否为空
            if(courseName != null && courseName != ""){
                sb.append(" AND course_name LIKE ?");
                //like查询 需要拼接 %
                courseName = "%"+courseName+"%";
                //将条件放进list集合
                list.add(courseName);
            }
    
            if(status != null && status != ""){
                sb.append("AND STATUS = ?");
                //将status 转换为 int
                int i = Integer.parseInt(status);
                list.add(i);
            }
    
            //执行查询
            List<Course> courseList = qr.query(sb.toString(), new BeanListHandler<Course>(Course.class), list.toArray());
    
            //返回结果
            return courseList;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    

    4.2.2 功能:新建课程营销信息

    文字部分:

    Dao层编写

    接口
    
        //保存课程营销信息
        public int saveCourseSalesInfo(Course course);
    
    实现类
    
       	//保存课程营销信息
        @Override
        public int saveCourseSalesInfo(Course course) {
    
            try {
                //1.创建QueryRunner
                QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
    
                //2.编写SQL
                String sql = "INSERT INTO course(\n" +
                        "course_name,\n" +
                        "brief,\n" +
                        "teacher_name,\n" +
                        "teacher_info,\n" +
                        "preview_first_field,\n" +
                        "preview_second_field,\n" +
                        "discounts,\n" +
                        "price,\n" +
                        "price_tag,\n" +
                        "share_image_title,\n" +
                        "share_title,\n" +
                        "share_description,\n" +
                        "course_description,\n" +
                        "course_img_url,\n" +
                        "STATUS,\n" +
                        "create_time,\n" +
                        "update_time\n" +
                        ")VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
    
                //3.准备参数
                Object[] param = {course.getCourse_name(),course.getBrief(),course.getTeacher_name(),course.getTeacher_info(),
                course.getPreview_first_field(),course.getPreview_second_field(),course.getDiscounts(),course.getPrice(),
                course.getPrice_tag(),course.getShare_image_title(),course.getShare_title(),course.getShare_description(),
                course.getCourse_description(),course.getCourse_img_url(),course.getStatus(),course.getCreate_time(),course.getUpdate_time()};
    
                //4.执行插入操作
                int row = qr.update(sql, param);
    
                return row;
            } catch (SQLException e) {
                e.printStackTrace();
                return 0;
            }
        }
    

    Service层编写

    4.2.2.1 枚举类
    1. 编写枚举类,设置响应状态码(放在base文件夹下)

      Java 枚举(enum)

      枚举类的使用方法

      public enum StatusCode {
      
          SUCCESS(0,"success"),
          FAIL(1,"fail");
      
          //定义属性
          private int code;
          private String message;
      
          //定义构造
          StatusCode(int code, String message) {
              this.code = code;
              this.message = message;
          }
      
          //get/set
          public int getCode() {
              return code;
          }
      
          public void setCode(int code) {
              this.code = code;
          }
      
          public String getMessage() {
              return message;
          }
      
          public void setMessage(String message) {
              this.message = message;
          }
      
          //重写toString,将枚举对象转化为JSON
          @Override
          public String toString() {
              JSONObject object = new JSONObject();
              object.put("status",code);
              object.put("msg",message);
              return object.toString();
          }
      }
      
    2. 编写Service

      接口
          public String saveCourseSalesInfo(Course course);
      
      实现类
      	@Override
          public String saveCourseSalesInfo(Course course) {
              //1.补全课程信息
              String dateFormart = DateUtils.getDateFormart();
              course.setCreate_time(dateFormart);
              course.setUpdate_time(dateFormart);
              course.setStatus(0);
      
              //2.调用Dao进行插入
              int i = courseDao.saveCourseSalesInfo(course);
              if(i > 0){
                  //保存成功
                  String result = StatusCode.SUCCESS.toString();
                  return result; // {"msg":"success","status":0}
      
              }else{
                  //保存失败
                  String result = StatusCode.FAIL.toString();
                  return result; // {"msg":"fail","status":1}
              }
          }
      

    文件上传部分:

    4.2.2.2 图片上传分析

    在添加课程营销信息的表单中,有一个图片上传项

    在这里插入图片描述

    文件上传的实质:文件的拷贝

    • 文件上传:从本地将文件拷贝到服务器磁盘上
      • 客户端: 需要编写文件上传表单
      • 服务端: 需要编写代码,接收上传的文件

    客户端编码

    文件上传三要素:

    1. 表单提交方式: post (get方式提交有大小限制,post没有)
    2. 表单的enctype属性:必须设置为 multipart/form-data
      • enctype就是encodetype就是编码类型的意思
      • multipart/form-data是多部件文件上传 , 指表单数据有多部分构成,既有文本数据,又有文件等二进制数据的意思
    3. 表单必须有文件上传项: file,必须要有name属性和值

    在这里插入图片描述

    注意: 默认情况下,表单的enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据

    服务端编码

    4.2.2.3 FileUpload工具类
    1. 导入依赖

      FileUpload包可以很容易地将文件上传到你的Web应用程序

      IOUtils封装了Java中io的常见操作,使用十分方便 ,需要下载 commons-io-1.4.jar 包

      <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>1.4</version>
      </dependency>
      
      <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.2.1</version>
      </dependency>
      
    2. FileUpload 核心类介绍

      类名介绍
      DiskFileItemFactory磁盘文件项工厂, 读取文件时相关的配置,比如: 缓存的大小 , 临时目录的位置
      ServletFileUplaod文件上传的一个核心类
      FileItem代表每一个表单项
    3. 文件上传的API的详解

      • ServletFileUpload
      方法说明
      isMultipartContent(request);判断是否是一个文件上传的表单
      parseRequest(request);解析request获得表单项的集合
      setHeaderEncoding(“UTF-8”);设置上传的文件名的编码方式
      • FileItem
      方法说明
      isFormField()判断是否是普通表单项
      getFieldName()获得表单的name属性值
      item.getString()获得表单的value值
      getName()获得上传文件的名称
      getInputStream()获得上传文件
      delete()删除临时文件
    4. 文件上传后台代码编写

      FileUpload使用步骤:

      ​ 1、创建磁盘文件项工厂

      ​ 2、创建文件上传的核心类

      ​ 3、解析request获得文件项集合

      ​ 4、遍历文件项集合

      ​ 5、判断普通表单项/文件上传项

    @WebServlet("/upload")
    public class FileUploadServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            try {
                //1.创建磁盘文件项工厂
                DiskFileItemFactory factory = new DiskFileItemFactory();
    
                //2.创建文件上传核心类
                ServletFileUpload upload = new ServletFileUpload(factory);
                //2.1 设置上传文件名的编码
                upload.setHeaderEncoding("utf-8");
                //2.2 判断表单是否是文件上传表单
                boolean multipartContent = upload.isMultipartContent(req);
                //2.3 是文件上传表单
                if(multipartContent){
    
                    //3. 解析request ,获取文件项集合
                    List<FileItem> list = upload.parseRequest(req);
    
                    if(list != null){
                        //4.遍历获取表单项
                        for (FileItem item : list) {
                            //5. 判断是不是一个普通表单项
                            boolean formField = item.isFormField();
                            if(formField){
                                //普通表单项, 当 enctype="multipart/form-data"时, request的getParameter()方法 无法获取参数
                                String fieldName = item.getFieldName();
                                String value = item.getString("utf-8");//设置编码
                                System.out.println(fieldName + "=" + value);
                            }else{
    
                                //文件上传项
                                //文件名
                                String fileName = item.getName();
    
                                //避免图片名重复 拼接UUID
                                String newFileName = UUIDUtils.getUUID()+"_"+ fileName;
     
                                //获取输入流
                                InputStream in = item.getInputStream();
    
                                //创建输出流 输出到H盘
                                FileOutputStream fos = new FileOutputStream("H:/upload/" +newFileName);
    
                                //使用工具类IOUtils,copy文件
                                IOUtils.copy(in,fos);
    
                                //关闭流
                                fos.close();
                                in.close();
    
                            }
    
                        }
                    }
                }
            } catch (FileUploadException e) {
                e.printStackTrace();
            }
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    
    }
    
    4.2.2.4 将图片上传到tomcat服务器

    图片若存到服务器磁盘上,不方便访问;上传到tomcat服务器则可以通过外部加载图片。

    1. 将项目部署到webapps

      将部署方式改变为 war模式,把项目部署在tomcat的webapps下

      • idea中部署项目两种方式

        • war模式:将项目以war包的形式上传真实到服务器的webapps目录中;

        • war exploded模式:仅仅是目录的映射,就相当于tomcat在项目源文件夹中启动一样;

          在这里插入图片描述

    2. 在webapps中创建upload目录

      upload目录专门用来保存上传过来的图片

      在这里插入图片描述

    3. 修改代码,将图片上传到服务器

      • 修改图片的输出路径
        1. 获取到项目的运行目录信息
        2. 截取到webapps的 目录路径
        3. 拼接输出路径,将图片保存到upload
      //获取上传文件的内容
      InputStream in = item.getInputStream();
      
      // 动态获取项目的运行目录
      String path = this.getServletContext().getRealPath("/");
      
      // 截取到 webapps路径
      String webappsPath = path.substring(0, path.indexOf("lagou_edu_home"));
      
      // 拼接输出路径
      OutputStream out = new FileOutputStream(webappsPath+"/upload/"+newFileName);
      //拷贝文件到服务器
      IOUtils.copy(in,out);
      
      out.close();
      in.close();
      
    4. 页面加载图片

      将tomcat作为图片服务器使用时,存储上传的图片后,如果想要图片可以访问,需要在idea中进行配置:

      1. 选择external source —> 找到webapps目录下的的upload文件夹

      在这里插入图片描述
      在这里插入图片描述

      1. 上传一张图片到服务器

      2. 在项目内部页面加载图片

      <img src="/upload/abbd99891af442a8a9cb65848744452e_qiyu.jpg">
      
      1. 也可以通过HTTP方式访问
      http://localhost:8080/upload/abbd99891af442a8a9cb65848744452e_qiyu.jpg
      
    4.2.2.5 BeanUtils工具类
    1. 介绍

    ​ BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。可以将一个表单提交的所有数据封装到JavaBean中

    1. 导入依赖
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.8.3</version>
    </dependency>
    
    1. BeanUtils 对象常用方法
    方法描述
    populate(Object bean, Map properties)将Map数据封装到指定Javabean中,
    一般用于将表单的所有数据封装到javabean
    setProperty(Object obj,String name,Object value)设置属性值
    getProperty(Object obj,String name)获得属性值

    Servlet编写

    接口 保存&修改 课程营销信息

    • 名称: courseSalesInfo
    • 描述: 保存课程相关的营销信息
    • URL: http://localhost:8080/lagou_edu_home/courseSalesInfo
    • 请求方式: POST
    • 请求参数
    字段说明类型是否必需备注
    id课程idint添加操作不用携带, 修改操作必须携带ID
    course_name课程名称String
    brief课程简介String一句话介绍课程
    teacher_name讲师名称String
    teacher_info讲师介绍String
    preview_first_field课程概述1String第一段描述 例如: 课程共15讲
    preview_second_field课程概述2String第二段描述 例如: 每周五更新
    discounts售卖价格double课程的售卖价格
    price商品原价double课程的原销售价
    price_tag促销文案String例如: 立即抢购
    share_image_title分享图titleString
    share_title分享标题String
    share_description分享描述String
    course_description课程描述String
    file文件
    • 请求参数示例 key:value 格式
    file:文件
    course_name: 微服务架构
    brief: 大厂架构师带你一起学
    teacher_name: PDD
    teacher_info: 技术精湛安全驾驶30年
    preview_first_field: 共5讲
    preview_second_field: 每周二更新
    discounts: 88.8
    price: 800.0
    price_tag: 先到先得
    share_image_title: hello word
    share_title: IT修炼之路永无止境
    share_description: 金牌讲师带你了解最新最牛的技术让你的实力再次进阶!
    course_description: 十年编程两茫茫,工期短,需求长。千行代码,Bug何处藏。纵使上线又如何,新版本,继续忙。黑白颠倒没商量,睡地铺,吃食堂。夜半梦醒,无人在身旁。最怕灯火阑珊时,手机响,心里慌.
    
    • 响应结果
    字段说明类型是否必须备注
    status表示执行成功或失败int0 表示成功, 1 表示失败
    msg响应消息String
    • 响应结果示例
    成功
    {"msg":"success","status":0}
    
    失败
    {"msg":"fail","status":1}
    

    创建CourseSalesInfoServlet类,继承HttpServlet , 完成保存课程营销信息操作。

    因为上传的信息包含文件信息,无法直接通过request直接获取参数,所以不能继承BaseServlet

    小技巧:判断请求列表里是否有id,若有则为修改操作,若无则为新建操作

    @WebServlet("/courseSalesInfo")
    public class CourseSalesInfoServlet  extends HttpServlet {
    
        /**
         * 保存课程营销信息
         *      收集表单数据,封装到course对象中,将图片上传到tomcat服务器中
         * */
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            try {
                //1.创建Course对象
                Course course = new Course();
    
                //2.创建Map集合,用来收集数据
                Map<String,Object> map = new HashMap<>();
    
                //3.创建磁盘工厂对象
                DiskFileItemFactory factory = new DiskFileItemFactory();
    
                //4.文件上传核心对象
                ServletFileUpload fileUpload = new ServletFileUpload(factory);
    
                //5.解析request对象,获取表单项集合
                List<FileItem> list = fileUpload.parseRequest(req);
    
                //6.遍历集合 判断哪些是普通的表单项,那些是文件表单项
                for (FileItem item : list) {
    
                    boolean formField = item.isFormField();
                    if(formField){
                        //是普通表单项,获取表单项中的数据,保存到map
                        String fieldName = item.getFieldName();
                        String value = item.getString("UTF-8");
                        System.out.println(fieldName +" " + value);
                        //使用map收集数据
                        map.put(fieldName,value);
                    }else{
                        //文件上传项
                        //获取文件名
                        String fileName = item.getName();
                        String newFileName = UUIDUtils.getUUID()+"_"+fileName;
    
                        //获取输入流
                        InputStream in = item.getInputStream();
    
                        //获取webapps的目录路径
                        String realPath = this.getServletContext().getRealPath("/");
                        String wabappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));
    
                        //创建输出流
                        OutputStream out = new FileOutputStream(wabappsPath+"/upload/" + newFileName);
    
                        IOUtils.copy(in,out);
                        out.close();
                        in.close();
    
                        //将图片路径进行保存
                        map.put("course_img_url", Constants.LOCAL_URL+"/upload/" + newFileName);
                    }
                }
    
                //使用BeanUtils 将map中的数据封装到course对象
                BeanUtils.populate(course,map);
    
                // 业务处理
                CourseService cs = new CourseServiceImpl();
                
                String dateFormart = DateUtils.getDateFormart();
    
                if(map.get("id") != null) {
                    //修改操作
                    //补全信息
                    course.setUpdate_time(dateFormart);//修改时间
                    String result = cs.updateCourseSalesInfo(course);
                    //响应结果
                    resp.getWriter().print(result);
                }else {
                    //新建操作
                    //补全信息
                    course.setCreate_time(dateFormart);//创建时间
                    course.setUpdate_time(dateFormart);//修改时间
                    course.setStatus(1); //上架
                    String result = cs.saveCourseSalesInfo(course);
                    //响应结果
                    resp.getWriter().print(result);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • 保存图片URL优化

      1. 创建常量类
      public final class Constants {
      
          //本地访问地址
          public static final String LOCAL_URL = "http://localhost:8080";
      }
      
      1. 拼接图片URL
       //将图片路径进行保存
       map.put("course_img_url", Constants.LOCAL_URL+"/upload/" + newFileName);
      

    postman测试上传文件

    1. 接口地址填写正确
    2. 将请求方式设置为POST
    3. 需要上传文件, 设置Headers: “key”:“Content-Type”, “value”:"multipart/form-data"

    在这里插入图片描述

    1. Body选择form-data

    2. key 右侧下拉选择file;value 点击Select Files选择文件,按照接口文档补全测试参数

    4.2.3 功能:展示课程内容

    要展示的内容是对应课程下的章节与课时信息(一门课程有多个章节,一个章节有多个课时)

    4.2.3.1 泛型

    Course 类与Course_Section 类是一对多关系

    • Course 类中定义一个List集合,并指定List的泛型是 Course_Section 类型,表示 一个课程中可以包含多个章节

      Course类
      //添加list集合 泛型是 Course_Section
      List<Course_Section> sectionList = new ArrayList<>();
      
    • Course_Section 类中,定义一个Course类型的属性,用来保存章节所对应的具体的课程信息

      Course_Section 类
      //添加一个Course类型的属性
      private Course course;
      

    Course_Section 类与 Course_Lesson 类是一对多关系

    Course_Section类
    //添加一个list集合 泛型是 Course_lesson
    List<Course_Lesson> lessonList = new ArrayList<>();
    
    Course_Lesson类
    //添加一个Course_Section类型的属性
    private Course_Section course_section;
    
    • 后续可封装Course_Lesson类对象进行输出
      • 将课时数据封装到 章节对象中 section.setLessonList(lessonList);
      • Course_Section类有FastJson的@JSONField注解,自动生成get/ set方法

    DAO层编写

    我们在程序中尽量避免使用连接查询,将SQL进行拆分,每一条SQL对应一个功能

    接口
        //根据课程ID查询课程相关信息
        public List<Course_Section> findSectionAndLessonByCourseId(int courseId);
    
        //根据章节ID 查询章节相关的课时信息
        public List<Course_Lesson> findLessonBySectionId(int sectionId);
    
    实现类
    	//根据课程ID查询课程相关信息
    	@Override
    	public List<Course_Section> findSectionAndLessonByCourseId(int courseId) {
            try {
                //1.创建QueryRunner
                QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
                
                //2.编写SQL
                String sql = "SELECT \n" +
                    "id,\n" +
                    "course_id,\n" +
                    "section_name,\n" +
                    "description,\n" +
                    "order_num,\n" +
                    "STATUS\n" +
                    "FROM course_section WHERE course_id = ?";
                
                //3.执行查询
                List<Course_Section> sectionList = qr.query(sql, new
                BeanListHandler<Course_Section>(Course_Section.class), courseId);
                
                //4.根据章节ID查询课时信息
                for (Course_Section section : sectionList) {
                    //调用方法 获取章节对应的课时
                    List<Course_Lesson> lessonList =
                    findLessonBySectionId(section.getId());
                    //将课时数据封装到 章节对象中
                    section.setLessonList(lessonList);
                }
                	return sectionList;
                } catch (SQLException e) {
                    e.printStackTrace();
                    return null;
                }
    		}
    
        //根据章节ID查询课时信息
        @Override
        public List<Course_Lesson> findLessonBySectionId(int sectionId) {
            try {
                QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
                
                String sql = "SELECT \n" +
                    "id,\n" +
                    "course_id,\n" +
                    "section_id,\n" +
                    "theme,\n" +
                    "duration,\n" +
                    "is_free,\n" +
                    "order_num,\n" +
                    "STATUS\n" +
                    "FROM course_lesson WHERE section_id = ?";
                
                List<Course_Lesson> lessonList = qr.query(sql, new
                BeanListHandler<Course_Lesson>(Course_Lesson.class), sectionId);
                	return lessonList;
                } catch (SQLException e) {
                    e.printStackTrace();
                    return null;
                }
        }
    
    展开全文
  • 这个“收入”包括:收费 App 的下载费、游戏充值费、订阅服务费等,广泛的抽成范围加上 30% 的高比例,一直开发者所抱怨。好在今年苹果和谷歌都对此进行了相应调整: 苹果:今年起,对每年营收在 100 万美元以下...

    众所周知,苹果税和谷歌税的存在,即“只要你的 App 在 App Store 或 Google Play 上架,产生的收入就必须分给苹果或谷歌。”

    这个“收入”包括:收费 App 的下载费、游戏充值费、订阅服务费等,广泛的抽成范围加上 30% 的高比例,一直为开发者所抱怨。好在今年苹果和谷歌都对此进行了相应调整:

    • 苹果:今年起,对每年营收在 100 万美元以下的小型企业和开发者,抽成比例降至 15%;

    • 谷歌:今年 7 月 1 日起,开发者每年首 100 万美元收入只抽成 15%。

    虽然对于大型企业而言依旧是 30% 的抽成占大头,但这些举措对大多数小型企业和个人开发者来说却是实打实打了“对折”,因此一时之间并无太多异词。

    可近来,Hacker News 上又出现了一则有关谷歌抽成并引起颇多议论的热帖:这款开源 App 因包含项目网站的链接而被 Play Store 下架。

    在这里插入图片描述

    热议的点在于,谷歌将其下架的理由是该 App 中链接的项目网站底部包含向开源作者进行捐赠的方式。该 App 的开发者在 GitHub 上表示:“如果我们想让人们对我们的开源项目进行捐赠,谷歌就要从(捐赠)中抽成。

    在这里插入图片描述

    一、为寻求更广泛的使用,将开源项目封装为 App 却遭拒

    这个被 Google Play 下架的 App 名为 Language Transfer,是由个人开发者 Mihalis Eleftheriou 制作的一款完全免费的语言学习应用。

    图片

    其实这款 App 的原身是一个开源项目,为了快速推广该开源工具的使用,大约一年前 Mihalis Eleftheriou 决定将其封装为 App

    “但当我们第一次(在 Google Play 上)发布时,这个 App 就被拒绝了。”Mihalis Eleftheriou 透露,原因是 App 中有一个为 Language Transfer 项目募集捐赠的 Patreon 链接。(PS:Patreon 是一个创建于 2013 年的众筹网站,旨在通过这个众筹平台来解决艺术家或开发者的创作和收益转化问题,Patreon 用户可为自己支持的工作者赞助一定金额的钱,助其完成新内容的创作。)

    图片
    对此,Mihalis Eleftheriou 直言“糟糕”。

    对免费开源项目而言,借助 Patreon 进行众筹其实是一种募集资金的常见方式,甚至早在 Language Transfer 以 App 形式出现之前,该开源项目就一直是完全依靠捐赠才坚持下来的,但谷歌却因为不能从中获得相应比例的分成,拒绝了这款 App 的上架。

    这就与 Mihalis Eleftheriou 的初衷背道而驰了:原以为将开源项目封装为 App 是将这些免费语言课程推向全球的最佳方式,但不能链接筹款平台却令开源项目的坚持运行都成问题。

    Mihalis Eleftheriou 有些委屈,他认为谷歌为了一些非营利组织可以破例不收取抽成,但 Language Transfer 只是由他一个人经营的小项目,即便通过那个 Patreon 链接进行捐赠只是支持开源项目的一种表达方式,谷歌也一定要从中抽成。

    但最终,Mihalis Eleftheriou 还是屈服了。他将 App 中原本的 Patreon 链接全部替换成了 Language Transfer 官网链接,也不再包含任何引导人们向项目捐赠的字眼

    果不其然,谷歌接受了这个版本的 App,Language Transfer 成功在 Google Play 上架,距今已有一年,下载量达 5w+。

    二、梅开二度:App 被下架,又是同样的理由

    原以为日子会这样平静地继续下去,但 8 月 14 日早上,Mihalis Eleftheriou 突然发现 Language Transfer 又被下架了。

    Mihalis Eleftheriou 描述道:“跟谷歌惯常的作风一样,App 的下架毫无通知,仅提供了一系列截图。”

    在这里插入图片描述
    在这里插入图片描述

    通过这些截图可以明显看出,应该是谷歌的某个审查人员访问了 Language Transfer 中链接的官网,划到了页面最底部,又通过两次点击找到了为该项目提供资金的方法,因此将 App 整个下架了。(注:这并非开发者后期更改的,早在谷歌首次批准 Language Transfer 上架时,这个链接就已经存在了。)

    “我们的 App 不允许链接到项目网站的主页,除非我们完全去除人们能为项目进行捐赠的所有方式。”Mihalis Eleftheriou 无奈表示道。

    这次,Mihalis Eleftheriou 还是对谷歌妥协了。他提交了一个 PR(Pull Request)“删除所有指向 languagetransfer.org 的直接链接”,希望以此解决问题,让 App 重新上架。但 Mihalis Eleftheriou 也表示,作为一款开源 App,他仍将在 App 中链接相应的 GitHub 和 Facebook 页面,通过这些链接应该也能找到对项目捐赠的途径。

    在这里插入图片描述

    三、引起公愤后,谷歌恢复了该 App

    虽然 Language Transfer 并非主流 App,但 Mihalis Eleftheriou 在 GitHub 上诉说的这番遭遇还是引起了许多开发者的关注与愤怒:

    @lodenrogue :“如果他们不解决这个问题,我将删除我所有的 Google 帐户。”

    在这里插入图片描述
    还有许多开发者在 Mihalis Eleftheriou 的 GitHub 下替他出谋划策

    @ocdtrekkie:“你可以联系国会议员,要求他们尽快投票支持《开放应用市场法案》(该法案的主要目标是禁止谷歌和苹果惩罚应用开发商在其平台之外收费的举动)。应用市场不断滥用的唯一解决方案是采取法律行动。”

    @Greg-Boggs:“你可以尝试获得现有非营利组织的支持。”

    @ashsidhu:“唯一的长期解决方案是支持开放网络。我们对封闭式花园屈服得越多,他们就越猖狂。我可以帮助你编写一个 PWA,它的性能和功能与 Android 应用相当。虽然如今 Play Store 拥有更多用户,但我们需要首先通过开放网络平台为每个人都提供服务。”

    通过开发者们的激烈讨论,该话题在 Hacker News 日榜被顶上一位,越来越多人关注到了谷歌这个可谓引起公愤的举动。

    或许是出于舆论压力,谷歌不久后就恢复了 Language Transfer 的上架,并且没有再要求删除其中项目网站的链接。Mihalis Eleftheriou 也提交了一个恢复“删除所有指向 languagetransfer.org 的直接链接”的 PR。

    在这里插入图片描述
    对于谷歌的这个举动,有开发者 @dessant 想起了许多此前因为同样理由被迫暂停的开源 App

    “从现在开始,Google Play 团队是否会允许开源项目在应用菜单中添加捐赠链接?出于完全相同的原因,此前有好几个优秀的开源项目被暂停。如果 Google Play 团队能够明确他们目前对开源 App 中捐赠链接的看法,那么目前由于担心被删除而失去潜在捐赠的项目就可以恢复捐赠,那就太棒了。”

    不过截止目前,谷歌官方还未对此做出明确回应。那么你对此次事件有什么看法呢?

    参考链接:

    • https://github.com/language-transfer/lt-app/pull/44

    • https://news.ycombinator.com/item?id=28172490

    展开全文
  • 深度学习速成课程这是我在意大利巴里大学计算机科学系的深度学习博士学位课程中准备和展示的材料。 由于该主题涉及面很广,因此几乎不可能涵盖所有方面,尤其是在我几个小时内。 因此,本课程旨在建立一个坚实的框架...
  • 课程学习

    千次阅读 2017-12-28 11:35:43
    Methodology:Idealistic Curriculum Learning ...我们提出了师生自主课程学习(TSCL),一个自动课程学习的框架,学生试图学习一个复杂的任务,教师自动选择给定的子任务给学生进行训练。我们描述了一系列依靠
  • java课程设计题目.doc

    千次阅读 2021-03-07 04:57:28
    java课程设计题目Java课程设计题目第一组:1、编写Java Appet 程序打印 “水仙花” 数 (它的个、十、百位数字的立方的和等于该数本身,如:153=1^3+5^3+3^3)。2、定义一个类Point,代表一个点,public属性有x和y,...
  • 计算方法课程总结 心得体会

    千次阅读 2021-07-01 19:29:56
    计算方法课程总结 心得体会一、课程简介:本课程是信息与计算科学、数学与应用数学本科专业必修的一门专业基础课....而所建立的这些数学模型,在许多情况,要获得精确解是十分困难的,甚至是不可能的...
  • 数据库课程设计(饭店点餐系统)

    万次阅读 多人点赞 2020-06-09 11:25:06
    2.1.1订单阶段需要的数据: 2.1.2点菜阶段需要的数据: 2.1.3结账阶段需要的数据: 2.1.4员工管理需要的数据: 2.1.5顾客管理需要的数据: 2.1.6消费记录管理需要的数据有: 2.2事务需求 2.2.1数据录入 ...
  • 金融源自于社会经济活动并在之服务的过程中发展壮大 国内外个经济部门内部与彼此间的经济活动,都需要通过金融来实现,由此形成了多元化的金融需求 金融在服务于社会经济活动的过程中逐渐形成产业和市场,构成了...
  • 数据库课程设计
  • Web前端课程设计

    千次阅读 2021-06-28 19:55:37
    不过很开心的是在我的不懈努力之还是基本完成了。下面是设计要求和课程设计获取方式。 《Web前端技术实验》期末作品 ——个人网站设计要求 栏目: 自我介绍 我的大学 专业及课程 校园活动 室友 社团...
  • 随着技术的发展,技术人才的增长,以及开发学生课程管理系统的经验不断积累,适合大众而且功能多样化,按照需求定制化的学生课程管理系统也在这样的背景应运而生。 系统现状和发展 随着计算机网络应用的逐渐普及,...
  • 2020 MIT 6.824 分布式系统课程

    万次阅读 多人点赞 2020-02-26 19:00:00
    可能有读者开始脑补了,说了半天,还不知道 MIT 6.824 是什么。 不用担心,笔者下面会详细介绍这门非常有名的网上公开课,对于已经知道的博学的读者就温故而知新吧。 MIT 6.824 MIT 6.824 是麻省理工的一门关于...
  • CQF课程简介

    千次阅读 2020-05-05 11:19:44
    CQF课程简介 CQF课程简介 Overview 概述 1. Preparation: Optional primers 前导课阶段 2. CQF Qualification: Modules and electives 认证阶段 3. Lifelong Learning: Continuous education 终身学习 Primers - The...
  • 计算机数学课程收集

    千次阅读 2018-11-13 15:37:02
    编者:李国帅 qq:9611153 微信lgs9611153 时间:2018/10/24   ...课程号:20100440 课程名:泛函分析 课程英文名:Functional Analysis 学时:68 学分:4 先修课程:实变函数、高等代数...
  • Web前端-课程设计-网易严选

    千次阅读 2022-02-07 22:06:14
    1. 项目链接: 2. 项目视频: 3. 项目文档: 本科学生大作业报告 课程名称: Web基础 网站主题: 《网易严选》 小组成员: 1. 2. 3....
  • 本文主要基于WinEdt编译器,以案例的形式介绍了如何使用latex制作课程报告,包括: 1. 案例的完整代码、编译方式;2. 如何写latex的参考文献; 3. 效果展示; 4. 分享相关的参考网址。
  • 网页设计课程设计报告

    万次阅读 多人点赞 2019-03-01 21:45:11
    学号 课 程 设 计 课程名称 网页设计 题 目 鑫晨之家特效网站设计 专 业 软件工程 ...
  • 课程相关信息1.1 课程简介1.2 教师团队1.3 课程编程语言1.4 课程参考教材2. 组织过程资产输入2.1 编程的学习与实践2.2 PMP项目管理经验3. 软件工程3.1 软件生命周期模型3.2 软件需求3.3 软件设计3.4 软件构造3.5 ...
  • 收藏!韦东山所有课程详细目录介绍(最新)

    万次阅读 多人点赞 2018-04-25 17:55:11
    【第一章】资料说明与下载 1-1-为什么讲单片机-讲什么内容 1-2-资料说明与下载 【第二章】嵌入式概念及硬件组成 2-1-处理器的区分MCU-MPU-AP 2-2-嵌入式系统硬件组成 【第三章】第一个程序(点亮LED)-基于STM32F103...
  • Hadoop大数据技术课程总结2021-2022学年第1学期

    千次阅读 多人点赞 2021-12-04 13:57:00
    本文Hadoop大数据技术课程总结,包括大数据概述,HDFS,MapReduce,Yarn,Hive,Zookeeper,Flume的基本介绍,部分内容附上了可供参考的链接,希望通过本博客的学习,各位学生能有所得,欢迎留言回复问题
  • 而只对体系结构的了解是远远不够的,深挖才是归途,为什么现在很多解析内存CPU技术的博文博客火呢?因为大家都忽略啊!又因为大家都知道这是多么有用啊!不做底层的分析,你怎么去精准的定位问题呢?怎么去写出切合...
  • 课程设计pygame实现贪吃蛇

    千次阅读 2020-07-07 20:25:37
    Python语言程序设计课程论文 项目名称:基于pygame的贪吃蛇游戏 摘要 Pygame是被设计用来写游戏的python模块集合,Pygame是在优秀的SDL库之上开发的功能性包。使用python可以导入pygame来开发具有全部特性的游戏和...
  • java web课程设计之图书管理系统

    千次阅读 2020-07-02 19:00:40
    水了一学期的java web,终于在课设付出了代价,真是平时幼儿园,期末似高三做完课程设计,老师不让用框架,只能用HTML+CSS+JavaScript+JSP+Servlet+JavaBean+JDBC+DAO,当然我们悄咪咪的用了一点点EL,下面是用户端...
  • 软件工程导论课程总结

    千次阅读 2019-02-17 10:13:24
    需求分析的目标是获知用户的真实需求, 而这一信息的惟一来源是用户,因此,让用户起积极主动的作用对需求分析工作获得成功是至关重要 的。 快速建立软件原型 • 快速原型就是快速建立起来的旨在演示目标系统...
  • 本次用 MapReduce 计算每门课程的平均成绩、最高成绩、最低成绩是我们《大数据基础》课程的期末大作业的功能需求之一。临近期末,在这里记录一下自己的学习收获,希望大家在浏览的过程中有所收获。由于能力有限,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 70,658
精华内容 28,263
热门标签
关键字:

得到为什么下架课程

友情链接: BmpBrowser.rar