当前位置:首页 > > Spring电子书网盘下载
Spring MVC学习指南 Spring MVC学习指南
码农之家

码农之家 提供上传

资源
34
粉丝
21
喜欢
509
评论
15

    Spring MVC学习指南 PDF 中文超清第2版

    Spring电子书
    • 发布时间:

    给大家带来的一篇关于Spring相关的电子书资源,介绍了关于Spring、MVC、学习指南方面的内容,本书是由人民邮电出版社出版,格式为PDF,资源大小83.3 MB,Paul Deck编写,目前豆瓣、亚马逊、当当、京东等电子书综合评分为:7.3,更多相关的学习资源可以参阅全栈产品DevOps文本处理iOSOffice实战、等栏目。

  • Spring MVC学习指南 PDF 下载
  • 下载地址:https://pan.baidu.com/s/1KUXCSU0g9rTncaekGSOks
  • 提取码:uf28
  •  读者评价

    mvc的书都比较晦涩,还是需要有一定的JAVA基础才看的懂

    如果连servlet都不会的话,可以考虑看一下的,不过其实参照目录,google或baidu一番,并参照官方doc,效果估计也差不多。
    本书想要解决的问题,是springmvc的初步入门。虽然这书夹杂了许多,jstl,jsp,html的内容,有点不对题,但从知识阐述,书本定位角度,个人认为该书合格了。

    第一本从大学图书馆借来完整看完的书。的确比较基础,有三分之一讲JSP相关的内容,可以顺便复习之前学的内容,进阶可以看官方文档。比起国内一些罗列知识点的书还是不错的。

    编辑推荐

    Spring MVC学习指南 Spring MVC的轻松入门学习指南 Spring MVC的入门畅销书新版 Spring MVC是当前十分流行的Web应用开发框架之一。 本书延续了学习指南系列的特点,通俗易懂,非常容易上手。 作为当今业界zui主流的Web开发框架,Spring MVC已经成为当前zui热门的开发技能,同时也广泛用于桌面开发领域。 本书重在讲述如何通过Spring MVC来开发基于Java的Web应用。 本书包括以下内容: Spring和Spring MVC简介; 模型2和MVC模式; 数据绑定和表单标签库; 转换器、格式化和验证器; 表达式语言; JSTL; 国际化; 上传文件和下载文件。 丰富的示例可供读者练习和参考。 

    内容节选

    浅谈spring和spring MVC的区别与关系

    spring是一个开源框架,功能主要是依赖注入和控制反转。
    依赖注入有三种形式
    1、构造注入(bytype)
    2、setter注入
    3、接口注入(byname) 而控制反转则主要是起到操控作用,把对象的创建,初始化,销毁交给spring容器来处理。面向切面(把功能分离出来)实现共用。
    spring MVC类似于struts是负责前台和后台的交互,还有就是spring可以集成许多工具,像数据库配置,缓存配置,定时器配置等等都是在spring中完成的,而spring MVC是做不到的。

    目录

    • 第一章 Spring框架 1
    • 1.1 XML配置文件 4
    • 1.2 Spring控制反转容器的使用 4
    • 1.2.3 通过构造器来创建一个bean实例 5
    • 1.2.2 通过工厂方法来创建一个bean实例 5
    • 1.2.3 Destroy Method的使用 6
    • 1.2.4 向构造器传参 6
    • 1.2.5 Setter方式依赖注入 7
    • 1.2.3 构造器方式依赖注入 10
    • 1.3 小结 10
    • 第二章 模型2和MVC模式 11
    • 2.1 模型1介绍 11
    • 2.2 模型2介绍 11
    • 2.3 模型2之Servlet控制器 13
    • 2.3.1 Product类 15
    • 2.3.2 ProductForm类 15
    • 2.3.3 ControllerServlet类 16
    • 2.3.4 视图 20
    • 2.3.5 测试应用 22
    • 2.4 解耦控制器代码 23
    • 2.5 校验器 27
    • 2.6 后端 32
    • 2.7 本章小结 33
    • 第三章 Spring MVC介绍 34
    • 3.1 采用Spring MVC的好处 34
    • 3.2 Spring MVC的DispatcherServlet 35
    • 3.3 Controller接口 36
    • 3.4 第一个Spring MVC应用 37
    • 3.4.1 目录结构 37
    • 3.4.2 部署描述符文件和Spring MVC配置文件 38
    • 3.4.3 Controller 39
    • 3.4.4 View 40
    • 3.4.5 测试应用 42
    • 3.5 View Resolver 43
    • 3.6 本章小结 45
    • 第四章 基于注解的控制器 46
    • 4.1 Spring MVC注解类型 46
    • 4.1.1 Controller注解类型 46
    • 4.1.2 RequestMapping注解类型 47
    • 4.2 编写请求处理方法 50
    • 4.3 应用基于注解的控制器 52
    • 4.3.1 目录结构 52
    • 4.3.2 配置文件 52
    • 4.3.3 Controller类 55
    • 4.3.4 View 56
    • 4.3.5 测试应用 57
    • 4.4 应用@Autowired和@Service进行依赖注入 58
    • 4.5 重定向和Flash属性 62
    • 4.6 请求参数和路径变量 63
    • 4.7 @ModelAttribute 66
    • 4.8 小结 67
    • 第五章 数据绑定和form标签库 68
    • 5.1 数据绑定概览 68
    • 5.2 form标签库 69
    • 5.2.1 form标签 70
    • 5.2.2 input标签 71
    • 5.2.3 password标签 72
    • 5.2.4 hidden标签 72
    • 5.2.5 textarea标签 73
    • 5.2.6 checkbox标签 73
    • 5.2.7 radiobutton标签 74
    • 5.2.8 checkboxes标签 74
    • 5.2.9 radiobuttons标签 75
    • 5.2.10 select标签 76
    • 5.2.11 option标签 76
    • 5.2.12 options标签 77
    • 5.2.13 errors标签 77
    • 5.3 数据绑定范例 78
    • 5.3.1 目录结构 78
    • 5.3.2 Domain类 78
    • 5.3.3 Controller类 80
    • 5.3.4 Service类 82
    • 5.3.5 配置文件 85
    • 5.3.6 视图 86
    • 5.3.7 测试应用程序范例 88
    • 5.4 小结 90
    • 第六章 Converter和Formatter 91
    • 6.1 converter 91
    • 6.2 formatter 97
    • 6.3 用registrar注册formatter 99
    • 6.4 选择converter,还是formatter? 101
    • 6.5 小结 101
    • 第七章 验证器 102
    • 7.1 验证概览 102
    • 7.2 Spring验证器 103
    • 7.3 ValidationUtils类 104
    • 7.4 Spring的Validator范例 105
    • 7.5 源文件 107
    • 7.6 Controller类 107
    • 7.7 测试验证器 109
    • 7.8 JSR 303验证 110
    • 7.9 JSR 303 Validator范例 112
    • 7.10 小结 114
    • 第八章 表达式语言(EL) 115
    • 8.1 表达式语言(EL)的语法 115
    • 8.1.1 关键字 116
    • 8.1.2 [ ]和.运算符 116
    • 8.1.3 取值规则 117
    • 8.2 访问JavaBean 118
    • 8.3 EL隐式对象 118
    • 8.3.1 pageContext 119
    • 8.4 使用其他EL运算符 122
    • 8.4.1 算术运算符 122
    • 8.3.3 关系运算符 123
    • 8.4.2 逻辑运算符 123
    • 8.4.3 关系运算符 123
    • 8.4.4 empty运算符 124
    • 8.5 如何在JSP 2.0及其更高版本中配置EL 124
    • 8.5.1 实现免脚本的JSP页面 124
    • 8.5.2 禁用EL计算 125
    • 8.6 小结 126
    • 第九章 JSTL 127
    • 9.1 下载JSTL 127
    • 9.2 JSTL库 127
    • 9.3 一般行为 129
    • 9.3.1 out标签 129
    • 9.3.2 set标签 130
    • 9.3.3 remove标签 132
    • 9.4 条件行为 132
    • 9.4.1 if标签 133
    • 9.4.2 choose、when和otherwise标签 134
    • 9.5 遍历行为 135
    • 9.5.1 forEach标签 135
    • 9.5.2 forTokens标签 143
    • 9.6 与URL相关的行为 144
    • 9.6.1 url标签 144
    • 9.6.2 redirect标签 146
    • 9.7 格式化行为 146
    • 9.7.1 formatNumber标签 146
    • 9.7.2 formatDate标签 149
    • 9.7.3 timeZone标签 150
    • 9.7.4 setTimeZone标签 152
    • 9.7.5 parseNumber 152
    • 9.7.6 parseDate标签 153
    • 9.8 函数 155
    • 9.8.1 contains函数 155
    • 9.8.2 containsIgnoreCase函数 155
    • 9.8.3 endsWith函数 156
    • 9.8.4 escapeXml函数 156
    • 9.8.5 indexOf函数 156
    • 9.8.6 join函数 156
    • 9.8.7 length函数 157
    • 9.8.8 replace函数 157
    • 9.8.9 split函数 157
    • 9.8.10 startsWith函数 158
    • 9.8.11 substring函数 158
    • 9.8.12 substringAfter函数 158
    • 9.8.13 substringBefore函数 158
    • 9.8.14 toLowerCase函数 159
    • 9.8.15 toUpperCase函数 159
    • 9.8.16 trim函数 159
    • 9.9 小结 159
    • 第十章 国际化 160
    • 10.1 语言区域 161
    • 10.2 国际化Spring MVC应用程序 163
    • 10.3 将文本元件隔离成属性文件 163
    • 10.4 读取属性文件 165
    • 10.5 告诉Spring MVC要使用哪个语言区域 166
    • 10.6 使用message标签 167
    • 10.7 范例 167
    • 10.8 小结 173
    • 第十一章 上传文件 174
    • 11.1 客户端编程 174
    • 11.2 MultipartFile接口 175
    • 11.3 用Commons FileUpload上传文件 176
    • 11.4 Domain类 177
    • 11.5 控制器 178
    • 11.6 配置文件 179
    • 11.7 JSP页面 181
    • 11.8 应用程序的测试 183
    • 11.9 用Servlet 3及其更高版本上传文件 184
    • 11.10 客户端上传 188
    • 11.11 小结 196
    • 第十二章 下载文件 197
    • 12.1 文件下载概览 197
    • 12.2 范例1:隐藏资源 198
    • 12.3 范例2:防止交叉引用 202
    • 12.4 小结 206
    • 附录A Tomcat 207
    • 附录B servlet 211
    • 附录C JavaServer Pages 236
    • 附录D 部署描述符 258

    读书笔记

    spring+springmvc+mybatis整合注解以及SSM框架整合步骤

    spring+springmvc+mybatis整合注解

    每天记录一点点,慢慢的成长,今天我们学习了ssm,这是我自己总结的笔记,大神勿喷!谢谢,主要代码!! !

    spring&springmvc&mybatis整合(注解)

    1.jar包

    2.引入web.xml文件

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:springmvc.xml</param-value>
        </init-param>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
    
    

    3.创建实体类

    4.引入一个(类名)dao.xml

    <update id="update" parameterType="accounting" >
        update accounting set money=#{money} where name=#{name}
      </update>
      <select id="findMoneyByName" parameterType="string" resultType="accounting">
        select * from accounting where name=#{name}
    </select>

    5.创建一个(类名)dao

    public void update(Accounting a);
    public Accounting findMoneyByName(String name);

    6.写service

    public void remit(String from,String to,double money);

    7.写serviceimpl

    @Service
    public class AccountServiceImpl implements AccountService {
      @Autowired
      private AccountDao ad;
      @Override
      public void remit(String from, String to, double money) {
        Accounting fromAccount=ad.findMoneyByName(from);
        fromAccount.setMoney(fromAccount.getMoney()-money);
        ad.update(fromAccount);
        Accounting toAccount=ad.findMoneyByName(to);
        toAccount.setMoney(toAccount.getMoney()+money);
        ad.update(toAccount);
      }
    
    }
    
    

    8.引入applicationContext.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
    
      <!-- 加载db.properties文件中的内容,db.properties文件中key命名要有一定的特殊规则 -->
      <context:property-placeholder location="classpath:db.properties" />
      <!-- 配置数据源 ,dbcp -->
    
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="30" />
        <property name="maxIdle" value="5" />
      </bean>
      <!-- sqlSessionFactory -->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库连接池 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 加载mybatis的全局配置文件 -->
        <property name="configLocation" value="classpath:sqlMapConfig.xml" />
      </bean>
    
    
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
      </bean>
      <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
          <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
      </tx:advice>
      <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* service..*.*(..))"/>
      </aop:config>
    
    
      <!-- mapper扫描器 -->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 扫描包路径,如果需要扫描多个包,中间使用半角逗号隔开 -->
        <property name="basePackage" value="dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
      </bean>
    
    </beans>
    
    

    9.引入db.properties文件和log4j.properties文件

    10.引入springmvc.xml文件

    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
      <mvc:annotation-driven></mvc:annotation-driven>
      <context:component-scan base-package="action"></context:component-scan>
      <context:component-scan base-package="service"></context:component-scan>
    </beans>
    
    

    11.jsp页面编写

    //index.jsp:
     <form action="account_execute.action" method="post">
      汇款人:<input type="text" name="from"/>
      收款人:<input type="text" name="to"/>
      钱数:<input type="text" name="money"/>
      <input type="submit"/>
     </form>
    //message.jsp
    ${message }

    整合SSM框架(Spring MVC+Spring+MyBatis)详细教程

    前言

    SSM(Spring+SpringMVC+Mybatis)是目前较为主流的企业级架构方案,不知道大家有没有留意,在我们看招聘信息的时候,经常会看到这一点,需要具备SSH框架的技能;而且在大部分教学课堂中,也会把SSH作为最核心的教学内容。

    但是,我们在实际应用中发现,SpringMVC可以完全替代Struts,配合注解的方式,编程非常快捷,而且通过restful风格定义url,让地址看起来非常优雅。

    另外,MyBatis也可以替换Hibernate,正因为MyBatis的半自动特点,我们程序猿可以完全掌控SQL,这会让有数据库经验的程序猿能开发出高效率的SQL语句,而且XML配置管理起来也非常方便。

    好了,如果你也认同我的看法,那么下面我们一起来做整合吧!话不多说,来一起看看详细的介绍:

    在写代码之前我们先了解一下这三个框架分别是干什么的?

    相信大以前也看过不少这些概念,我这就用大白话来讲,如果之前有了解过可以跳过这一大段,直接看代码!

    1. SpringMVC:它用于web层,相当于controller(等价于传统的servlet和struts的action),用来处理用户请求。举个例子,用户在地址栏输入http://网站域名/login,那么springmvc就会拦截到这个请求,并且调用controller层中相应的方法,(中间可能包含验证用户名和密码的业务逻辑,以及查询数据库操作,但这些都不是springmvc的职责),最终把结果返回给用户,并且返回相应的页面(当然也可以只反馈josn/xml等格式数据)。springmvc就是做前面和后面过程的活,与用户打交道!!
    2. Spring:太强大了,以至于我无法用一个词或一句话来概括它。但与我们平时开发接触最多的估计就是IOC容器,它可以装载bean(也就是我们java中的类,当然也包括service dao里面的),有了这个机制,我们就不用在每次使用这个类的时候为它初始化,很少看到关键字new。另外spring的aop,事务管理等等都是我们经常用到的。
    3. MyBatis:如果你问我它跟鼎鼎大名的Hibernate有什么区别?我只想说,他更符合我的需求。第一,它能自由控制sql,这会让有数据库经验的人(当然不是说我啦~捂脸~)编写的代码能搞提升数据库访问的效率。第二,它可以使用xml的方式来组织管理我们的sql,因为一般程序出错很多情况下是sql出错,别人接手代码后能快速找到出错地方,甚至可以优化原来写的sql。

    SSM框架整合配置

    好了,前面bb那么多,下面我们真正开始敲代码了~

    首先我们打开IED,我这里用的是eclipse(你们应该也是用的这个,对吗?),创建一个动态web项目,建立好相应的目录结构(重点!)

    (打了马赛克是因为这里还用不到,你们不要那么污好不好?)

    我说一下每个目录都有什么用吧(第一次画表格,我发现markdown的表格语法很不友好呀~)

    这个目录结构同时也遵循maven的目录规范~

     

    文件名 作用
    src 根目录,没什么好说的,下面有main和test。
    main 主要目录,可以放java代码和一些资源文件。
    java 存放我们的java代码,这个文件夹要使用Build Path -> Use as Source Folder,这样看包结构会方便很多,新建的包就相当于在这里新建文件夹咯。
    resources 存放资源文件,譬如各种的spring,mybatis,log配置文件。
    mapper 存放dao中每个方法对应的sql,在这里配置,无需写daoImpl。
    spring 这里当然是存放spring相关的配置文件,有dao service web三层。
    sql 其实这个可以没有,但是为了项目完整性还是加上吧。
    webapp 这个貌似是最熟悉的目录了,用来存放我们前端的静态资源,如jsp js css。
    resources 这里的资源是指项目的静态资源,如js css images等。
    WEB-INF 很重要的一个目录,外部浏览器无法访问,只有羡慕内部才能访问,可以把jsp放在这里,另外就是web.xml了。你可能有疑问了,为什么上面java中的resources里面的配置文件不妨在这里,那么是不是会被外部窃取到?你想太多了,部署时候基本上只有webapp里的会直接输出到根目录,其他都会放入WEB-INF里面,项目内部依然可以使用classpath:XXX来访问,好像IDE里可以设置部署输出目录,这里扯远了~
    test 这里是测试分支。
    java 测试java代码,应遵循包名相同的原则,这个文件夹同样要使用Build Path -> Use as Source Folder,这样看包结构会方便很多。

     

    resources 没什么好说的,好像也很少用到,但这个是maven的规范。

    我先新建好几个必要的包,并为大家讲解一下每个包的作用,顺便理清一下后台的思路~

     

    包名 名称 作用
    dao 数据访问层(接口) 与数据打交道,可以是数据库操作,也可以是文件读写操作,甚至是redis缓存操作,总之与数据操作有关的都放在这里,也有人叫做dal或者数据持久层都差不多意思。为什么没有daoImpl,因为我们用的是mybatis,所以可以直接在配置文件中实现接口的每个方法。
    entity 实体类 一般与数据库的表相对应,封装dao层取出来的数据为一个对象,也就是我们常说的pojo,一般只在dao层与service层之间传输。
    dto 数据传输层 刚学框架的人可能不明白这个有什么用,其实就是用于service层与web层之间传输,为什么不直接用entity(pojo)?其实在实际开发中发现,很多时间一个entity并不能满足我们的业务需求,可能呈现给用户的信息十分之多,这时候就有了dto,也相当于vo,记住一定不要把这个混杂在entity里面,答应我好吗?
    service 业务逻辑(接口) 写我们的业务逻辑,也有人叫bll,在设计业务接口时候应该站在“使用者”的角度。额,不要问我为什么这里没显示!IDE调皮我也拿它没办法~
    serviceImpl 业务逻辑(实现) 实现我们业务接口,一般事务控制是写在这里,没什么好说的。
    web 控制器 springmvc就是在这里发挥作用的,一般人叫做controller控制器,相当于struts中的action。

     

    还有最后一步基础工作,导入我们相应的jar包,我使用的是maven来管理我们的jar,所以只需要在poom.xml中加入相应的依赖就好了,如果不使用maven的可以自己去官网下载相应的jar,放到项目WEB-INF/lib目录下。关于maven的学习大家可以看慕课网的视频教程,这里就不展开了。我把项目用到的jar都写在下面,版本都不是最新的,大家有经验的话可以自己调整版本号。另外,所有jar都会与项目一起打包放到我的github上,喜欢的给个star吧~

    poom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelversion>4.0.0</modelversion>
     <groupid>com.soecode.ssm</groupid>
     ssm</artifactid>
     <packaging>war</packaging>
     <version>0.0.1-SNAPSHOT</version>
     <name>ssm Maven Webapp</name>
     <url>http://github.com/liyifeng1994/ssm</url>
     <dependencies>
     <!-- 单元测试 -->
     <dependency>
     <groupid>junit</groupid>
     junit</artifactid>
     <version>4.11</version>
     </dependency>
     
     <!-- 1.日志 -->
     <!-- 实现slf4j接口并整合 -->
     <dependency>
     <groupid>ch.qos.logback</groupid>
     logback-classic</artifactid>
     <version>1.1.1</version>
     </dependency>
     
     <!-- 2.数据库 -->
     <dependency>
     <groupid>mysql</groupid>
     mysql-connector-java</artifactid>
     <version>5.1.37</version>
     <scope>runtime</scope>
     </dependency>
     <dependency>
     <groupid>c3p0</groupid>
     c3p0</artifactid>
     <version>0.9.1.2</version>
     </dependency>
     
     <!-- DAO: MyBatis -->
     <dependency>
     <groupid>org.mybatis</groupid>
     mybatis</artifactid>
     <version>3.3.0</version>
     </dependency>
     <dependency>
     <groupid>org.mybatis</groupid>
     mybatis-spring</artifactid>
     <version>1.2.3</version>
     </dependency>
     
     <!-- 3.Servlet web -->
     <dependency>
     <groupid>taglibs</groupid>
     standard</artifactid>
     <version>1.1.2</version>
     </dependency>
     <dependency>
     <groupid>jstl</groupid>
     jstl</artifactid>
     <version>1.2</version>
     </dependency>
     <dependency>
     <groupid>com.fasterxml.jackson.core</groupid>
     jackson-databind</artifactid>
     <version>2.5.4</version>
     </dependency>
     <dependency>
     <groupid>javax.servlet</groupid>
     javax.servlet-api</artifactid>
     <version>3.1.0</version>
     </dependency>
     
     <!-- 4.Spring -->
     <!-- 1)Spring核心 -->
     <dependency>
     <groupid>org.springframework</groupid>
     spring-core</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <dependency>
     <groupid>org.springframework</groupid>
     spring-beans</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <dependency>
     <groupid>org.springframework</groupid>
     spring-context</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <!-- 2)Spring DAO层 -->
     <dependency>
     <groupid>org.springframework</groupid>
     spring-jdbc</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <dependency>
     <groupid>org.springframework</groupid>
     spring-tx</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <!-- 3)Spring web -->
     <dependency>
     <groupid>org.springframework</groupid>
     spring-web</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <dependency>
     <groupid>org.springframework</groupid>
     spring-webmvc</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     <!-- 4)Spring test -->
     <dependency>
     <groupid>org.springframework</groupid>
     spring-test</artifactid>
     <version>4.1.7.RELEASE</version>
     </dependency>
     
     <!-- redis客户端:Jedis -->
     <dependency>
     <groupid>redis.clients</groupid>
     jedis</artifactid>
     <version>2.7.3</version>
     </dependency>
     <dependency>
     <groupid>com.dyuproject.protostuff</groupid>
     <artifactid>protostuff-core</artifactid>
     <version>1.0.8</version>
     </dependency>
     <dependency>
     <groupid>com.dyuproject.protostuff</groupid>
     <artifactid>protostuff-runtime</artifactid>
     <version>1.0.8</version>
     </dependency>
     
     <!-- Map工具类 -->
     <dependency>
     <groupid>commons-collections</groupid>
     <artifactid>commons-collections</artifactid>
     <version>3.2</version>
     </dependency>
     </dependencies>
     <build>
     <finalname>ssm</finalname>
     </build>
    </project>
    

     

    下面真的要开始进行编码工作了,坚持到这里辛苦大家了~

    第一步:我们先在spring文件夹里新建spring-dao.xml文件,因为spring的配置太多,我们这里分三层,分别是dao service web。

    1、读入数据库连接相关参数(可选)

    2、配置数据连接池

         配置连接属性,可以不读配置项文件直接在这里写死

         配置c3p0,只配了几个常用的

    3、配置SqlSessionFactory对象(mybatis)

    4、扫描dao层接口,动态实现dao接口,也就是说不需要daoImpl,sql和参数都写在xml文件上

    spring-dao.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd">
     <!-- 配置整合mybatis过程 -->
     <!-- 1.配置数据库相关参数properties的属性:${url} -->
     <context:property-placeholder location="classpath:jdbc.properties" />
    
     <!-- 2.数据库连接池 -->
     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
     <!-- 配置连接池属性 -->
     <property name="driverClass" value="${jdbc.driver}" />
     <property name="jdbcUrl" value="${jdbc.url}" />
     <property name="user" value="${jdbc.username}" />
     <property name="password" value="${jdbc.password}" />
    
     <!-- c3p0连接池的私有属性 -->
     <property name="maxPoolSize" value="30" />
     <property name="minPoolSize" value="10" />
     <!-- 关闭连接后不自动commit -->
     <property name="autoCommitOnClose" value="false" />
     <!-- 获取连接超时时间 -->
     <property name="checkoutTimeout" value="10000" />
     <!-- 当获取连接失败重试次数 -->
     <property name="acquireRetryAttempts" value="2" />
     </bean>
    
     <!-- 3.配置SqlSessionFactory对象 -->
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource" />
     <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
     <property name="configLocation" value="classpath:mybatis-config.xml" />
     <!-- 扫描entity包 使用别名 -->
     <property name="typeAliasesPackage" value="com.soecode.lyf.entity" />
     <!-- 扫描sql配置文件:mapper需要的xml文件 -->
     <property name="mapperLocations" value="classpath:mapper/*.xml" />
     </bean>
    
     <!-- 4.配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 -->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     <!-- 注入sqlSessionFactory -->
     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
     <!-- 给出需要扫描Dao接口包 -->
     <property name="basePackage" value="com.soecode.lyf.dao" />
     </bean>
    </beans>

    因为数据库配置相关参数是读取配置文件,所以在resources文件夹里新建一个jdbc.properties文件,存放我们4个最常见的数据库连接属性,这是我本地的,大家记得修改呀~还有喜欢传到github上“大头虾们”记得删掉密码,不然别人就很容易得到你服务器的数据库配置信息,然后干一些羞羞的事情,你懂的!!

    jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3307/ssm?useUnicode=true&characterEncoding=utf8
    jdbc.username=root
    jdbc.password=

    友情提示:配置文件中的jdbc.username,如果写成username,可能会与系统环境中的username变量冲突,所以到时候真正连接数据库的时候,用户名就被替换成系统中的用户名(有得可能是administrator),那肯定是连接不成功的,这里有个小坑,我被坑了一晚上!!

    因为这里用到了mybatis,所以需要配置mybatis核心文件,在recources文件夹里新建mybatis-config.xml文件。

    • 使用自增主键
    • 使用列别名
    • 开启驼峰命名转换 create_time -> createTime

    mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
     <!-- 配置全局属性 -->
     <settings>
     <!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 -->
     <setting name="useGeneratedKeys" value="true" />
    
     <!-- 使用列别名替换列名 默认:true -->
     <setting name="useColumnLabel" value="true" />
    
     <!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
     <setting name="mapUnderscoreToCamelCase" value="true" />
     </settings>
    </configuration>

    第二步:刚弄好dao层,接下来到service层了。在spring文件夹里新建spring-service.xml文件。

    • 扫描service包所有注解 @Service
    • 配置事务管理器,把事务管理交由spring来完成
    • 配置基于注解的声明式事务,可以直接在方法上@Transaction

    spring-service.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx.xsd">
     <!-- 扫描service包下所有使用注解的类型 -->
     <context:component-scan base-package="com.soecode.lyf.service" />
    
     <!-- 配置事务管理器 -->
     <bean id="transactionManager"
     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!-- 注入数据库连接池 -->
     <property name="dataSource" ref="dataSource" />
     </bean>
    
     <!-- 配置基于注解的声明式事务 -->
     <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>

    第三步:配置web层,在spring文件夹里新建spring-web.xml文件。

    • 开启SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
    • 对静态资源处理,如js,css,jpg等
    • 配置jsp 显示ViewResolver,例如在controller中某个方法返回一个string类型的”login”,实际上会返回”/WEB-INF/login.jsp”
    • 扫描web层 @Controller

    spring-web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:mvc="http://www.springframework.org/schema/mvc" 
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/mvc
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
     <!-- 配置SpringMVC -->
     <!-- 1.开启SpringMVC注解模式 -->
     <!-- 简化配置: 
     (1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter 
     (2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持 
     -->
     <mvc:annotation-driven />
    
     <!-- 2.静态资源默认servlet配置
     (1)加入对静态资源的处理:js,gif,png
     (2)允许使用"/"做整体映射
     -->
     <mvc:default-servlet-handler/>
    
     <!-- 3.配置jsp 显示ViewResolver -->
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
     <property name="prefix" value="/WEB-INF/jsp/" />
     <property name="suffix" value=".jsp" />
     </bean>
    
     <!-- 4.扫描web相关的bean -->
     <context:component-scan base-package="com.soecode.lyf.web" />
    </beans>

    第四步:最后就是修改web.xml文件了,它在webapp的WEB-INF下。

    web.xml

    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
     http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
     version="3.1" metadata-complete="true">
     <!-- 如果是用mvn命令生成的xml,需要修改servlet版本为3.1 -->
     <!-- 配置DispatcherServlet -->
     <servlet>
     <servlet-name>seckill-dispatcher</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <!-- 配置springMVC需要加载的配置文件
     spring-dao.xml,spring-service.xml,spring-web.xml
     Mybatis - > spring -> springmvc
     -->
     <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:spring/spring-*.xml</param-value>
     </init-param>
     </servlet>
     <servlet-mapping>
     <servlet-name>seckill-dispatcher</servlet-name>
     <!-- 默认匹配所有的请求 -->
     <url-pattern>/</url-pattern>
     </servlet-mapping>
    </web-app>

    我们在项目中经常会使用到日志,所以这里还有配置日志xml,在resources文件夹里新建logback.xml文件,所给出的日志输出格式也是最基本的控制台s呼出,大家有兴趣查看logback官方文档

    logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="true">
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
     <encoder>
     <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
     </encoder>
     </appender>
    
     <root level="debug">
     <appender-ref ref="STDOUT" />
     </root>
    </configuration>

    到目前为止,我们一共写了7个配置文件,我们一起来看下最终的配置文件结构图。

    SSM框架应用实例(图书管理系统)

    一开始想就这样结束教程,但是发现其实很多人都还不会把这个SSM框架用起来,特别是mybatis部分。那我现在就以最常见的“图书管理系统”中【查询图书】和【预约图书】业务来做一个demo吧!

    首先新建数据库名为ssm,再创建两张表:图书表book和预约图书表appointment,并且为book表初始化一些数据,sql如下。

    schema.sql

    -- 创建图书表
    CREATE TABLE `book` (
     `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',
     `name` varchar(100) NOT NULL COMMENT '图书名称',
     `number` int(11) NOT NULL COMMENT '馆藏数量',
     PRIMARY KEY (`book_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='图书表'
    
    -- 初始化图书数据
    INSERT INTO `book` (`book_id`, `name`, `number`)
    VALUES
     (1000, 'Java程序设计', 10),
     (1001, '数据结构', 10),
     (1002, '设计模式', 10),
     (1003, '编译原理', 10)
    
    -- 创建预约图书表
    CREATE TABLE `appointment` (
     `book_id` bigint(20) NOT NULL COMMENT '图书ID',
     `student_id` bigint(20) NOT NULL COMMENT '学号',
     `appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '预约时间' ,
     PRIMARY KEY (`book_id`, `student_id`),
     INDEX `idx_appoint_time` (`appoint_time`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='预约图书表'

    在entity包中添加两个对应的实体,图书实体Book.java和预约图书实体Appointment.java。

    Book.java

    package com.soecode.lyf.entity;
    
    public class Book {
    
     private long bookId;// 图书ID
    
     private String name;// 图书名称
    
     private int number;// 馆藏数量
    
     // 省略构造方法,getter和setter方法,toString方法
    
    }

    Appointment.java

    package com.soecode.lyf.entity;
    
    import java.util.Date;
    
    /**
     * 预约图书实体
     */
    public class Appointment {
    
     private long bookId;// 图书ID
    
     private long studentId;// 学号
    
     private Date appointTime;// 预约时间
    
     // 多对一的复合属性
     private Book book;// 图书实体
    
     // 省略构造方法,getter和setter方法,toString方法
    
    }

    在dao包新建接口BookDao.java和Appointment.java

    BookDao.java

    package com.soecode.lyf.dao;
    
    import java.util.List;
    
    import com.soecode.lyf.entity.Book;
    
    public interface BookDao {
    
     /**
     * 通过ID查询单本图书
     * 
     * @param id
     * @return
     */
     Book queryById(long id);
    
     /**
     * 查询所有图书
     * 
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return
     */
     List<Book> queryAll(@Param("offset") int offset, @Param("limit") int limit);
    
     /**
     * 减少馆藏数量
     * 
     * @param bookId
     * @return 如果影响行数等于>1,表示更新的记录行数
     */
     int reduceNumber(long bookId);
    }

    AppointmentDao.java

    package com.soecode.lyf.dao;
    
    import org.apache.ibatis.annotations.Param;
    
    import com.soecode.lyf.entity.Appointment;
    
    public interface AppointmentDao {
    
     /**
     * 插入预约图书记录
     * 
     * @param bookId
     * @param studentId
     * @return 插入的行数
     */
     int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);
    
     /**
     * 通过主键查询预约图书记录,并且携带图书实体
     * 
     * @param bookId
     * @param studentId
     * @return
     */
     Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId);
    
    }

    提示:这里为什么要给方法的参数添加@Param注解呢?是因为该方法有两个或以上的参数,一定要加,不然mybatis识别不了。上面的BookDao接口的queryById方法和reduceNumber方法只有一个参数book_id,所以可以不用加 @Param注解,当然加了也无所谓~

    注意:这里不需要实现dao接口不用编写daoImpl, mybatis会给我们动态实现,但是我们需要编写相应的mapper。
    在mapper目录里新建两个文件BookDao.xml和AppointmentDao.xml,分别对应上面两个dao接口,代码如下。

    BookDao.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.soecode.lyf.dao.BookDao">
     <!-- 目的:为dao接口方法提供sql语句配置 -->
     <select id="queryById" resultType="Book" parameterType="long">
     <!-- 具体的sql -->
     SELECT
     book_id,
     name,
     number
     FROM
     book
     WHERE
     book_id = #{bookId}
     </select>
    
     <select id="queryAll" resultType="Book">
     SELECT
     book_id,
     name,
     number
     FROM
     book
     ORDER BY
     book_id
     LIMIT #{offset}, #{limit}
     </select>
    
     <update id="reduceNumber">
     UPDATE book
     SET number = number - 1
     WHERE
     book_id = #{bookId}
     AND number > 0
     </update>
    </mapper>

    AppointmentDao.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.soecode.lyf.dao.AppointmentDao">
     <insert id="insertAppointment">
     <!-- ignore 主键冲突,报错 -->
     INSERT ignore INTO appointment (book_id, student_id)
     VALUES (#{bookId}, #{studentId})
     </insert>
    
     <select id="queryByKeyWithBook" resultType="Appointment">
     <!-- 如何告诉MyBatis把结果映射到Appointment同时映射book属性 -->
     <!-- 可以自由控制SQL -->
     SELECT
     a.book_id,
     a.student_id,
     a.appoint_time,
     b.book_id "book.book_id",
     b.`name` "book.name",
     b.number "book.number"
     FROM
     appointment a
     INNER JOIN book b ON a.book_id = b.book_id
     WHERE
     a.book_id = #{bookId}
     AND a.student_id = #{studentId}
     </select>
    </mapper>

    mapper总结:namespace是该xml对应的接口全名,select和update中的id对应方法名,resultType是返回值类型,parameterType是参数类型(这个其实可选),最后#{...}中填写的是方法的参数,看懂了是不是很简单!!我也这么觉得~ 还有一个小技巧要交给大家,就是在返回Appointment对象包含了一个属性名为book的Book对象,那么可以使用"book.属性名"的方式来取值,看上面queryByKeyWithBook方法的sql。

    dao层写完了,接下来test对应的package写我们测试方法吧。

    因为我们之后会写很多测试方法,在测试前需要让程序读入spring-dao和mybatis等配置文件,所以我这里就抽离出来一个BaseTest类,只要是测试方法就继承它,这样那些繁琐的重复的代码就不用写那么多了~

    BaseTest.java

    package com.soecode.lyf;
    
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * 配置spring和junit整合,junit启动时加载springIOC容器 spring-test,junit
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    // 告诉junit spring配置文件
    @ContextConfiguration({ "classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml" })
    public class BaseTest {
    
    }

    因为spring-service在service层的测试中会时候到,这里也一起引入算了!

    新建BookDaoTest.java和AppointmentDaoTest.java两个dao测试文件。

    BookDaoTest.java

    package com.soecode.lyf.dao;
    
    import java.util.List;
    
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.soecode.lyf.BaseTest;
    import com.soecode.lyf.entity.Book;
    
    public class BookDaoTest extends BaseTest {
    
     @Autowired
     private BookDao bookDao;
    
     @Test
     public void testQueryById() throws Exception {
     long bookId = 1000;
     Book book = bookDao.queryById(bookId);
     System.out.println(book);
     }
    
     @Test
     public void testQueryAll() throws Exception {
     List<Book> books = bookDao.queryAll(0, 4);
     for (Book book : books) {
     System.out.println(book);
     }
     }
    
     @Test
     public void testReduceNumber() throws Exception {
     long bookId = 1000;
     int update = bookDao.reduceNumber(bookId);
     System.out.println("update=" + update);
     }
    
    }

    BookDaoTest测试结果

    testQueryById

    testQueryById

    testQueryAll

    testReduceNumber

    AppointmentDaoTest.java

    package com.soecode.lyf.dao;
    
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.soecode.lyf.BaseTest;
    import com.soecode.lyf.entity.Appointment;
    
    public class AppointmentDaoTest extends BaseTest {
    
     @Autowired
     private AppointmentDao appointmentDao;
    
     @Test
     public void testInsertAppointment() throws Exception {
     long bookId = 1000;
     long studentId = 12345678910L;
     int insert = appointmentDao.insertAppointment(bookId, studentId);
     System.out.println("insert=" + insert);
     }
    
     @Test
     public void testQueryByKeyWithBook() throws Exception {
     long bookId = 1000;
     long studentId = 12345678910L;
     Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);
     System.out.println(appointment);
     System.out.println(appointment.getBook());
     }
    
    }

    AppointmentDaoTest测试结果

    testInsertAppointment

    testQueryByKeyWithBook

    嗯,到这里一切到很顺利~那么我们继续service层的编码吧~可能下面开始信息里比较大,大家要做好心理准备~

    首先,在写我们的控制器之前,我们先定义几个预约图书操作返回码的数据字典,也就是我们要返回给客户端的信息。我们这类使用枚举类,没听过的小伙伴要好好恶补一下了(我也是最近才学到的= =)

    预约业务操作返回码说明

     

    返回码 说明
    1 预约成功
    0 库存不足
    -1 重复预约
    -2 系统异常

     

    新建一个包叫enums,在里面新建一个枚举类AppointStateEnum.java,用来定义预约业务的数据字典,没听懂没关系,我们直接看代码吧~是不是感觉有模有样了!

    AppointStateEnum.java

    package com.soecode.lyf.enums;
    
    /**
     * 使用枚举表述常量数据字典
     */
    public enum AppointStateEnum {
    
     SUCCESS(1, "预约成功"), NO_NUMBER(0, "库存不足"), REPEAT_APPOINT(-1, "重复预约"), INNER_ERROR(-2, "系统异常");
    
     private int state;
    
     private String stateInfo;
    
     private AppointStateEnum(int state, String stateInfo) {
     this.state = state;
     this.stateInfo = stateInfo;
     }
    
     public int getState() {
     return state;
     }
    
     public String getStateInfo() {
     return stateInfo;
     }
    
     public static AppointStateEnum stateOf(int index) {
     for (AppointStateEnum state : values()) {
     if (state.getState() == index) {
     return state;
     }
     }
     return null;
     }
    
    }

    接下来,在dto包下新建AppointExecution.java用来存储我们执行预约操作的返回结果。

    AppointExecution.java

    package com.soecode.lyf.dto;
    
    import com.soecode.lyf.entity.Appointment;
    import com.soecode.lyf.enums.AppointStateEnum;
    
    /**
     * 封装预约执行后结果
     */
    public class AppointExecution {
    
     // 图书ID
     private long bookId;
    
     // 秒杀预约结果状态
     private int state;
    
     // 状态标识
     private String stateInfo;
    
     // 预约成功对象
     private Appointment appointment;
    
     public AppointExecution() {
     }
    
     // 预约失败的构造器
     public AppointExecution(long bookId, AppointStateEnum stateEnum) {
     this.bookId = bookId;
     this.state = stateEnum.getState();
     this.stateInfo = stateEnum.getStateInfo();
     }
    
     // 预约成功的构造器
     public AppointExecution(long bookId, AppointStateEnum stateEnum, Appointment appointment) {
     this.bookId = bookId;
     this.state = stateEnum.getState();
     this.stateInfo = stateEnum.getStateInfo();
     this.appointment = appointment;
     }
    
     // 省略getter和setter方法,toString方法
    
    }

    接着,在exception包下新建三个文件

    • NoNumberException.java
    • RepeatAppointException.java
    • AppointException.java

    预约业务异常类(都需要继承RuntimeException),分别是无库存异常、重复预约异常、预约未知错误异常,用于业务层非成功情况下的返回(即成功返回结果,失败抛出异常)。

    NoNumberException.java

    package com.soecode.lyf.exception;
    
    /**
     * 库存不足异常
     */
    public class NoNumberException extends RuntimeException {
    
     public NoNumberException(String message) {
     super(message);
     }
    
     public NoNumberException(String message, Throwable cause) {
     super(message, cause);
     }
    
    }

    RepeatAppointException.java

    package com.soecode.lyf.exception;
    
    /**
     * 重复预约异常
     */
    public class RepeatAppointException extends RuntimeException {
    
     public RepeatAppointException(String message) {
     super(message);
     }
    
     public RepeatAppointException(String message, Throwable cause) {
     super(message, cause);
     }
    
    }

    AppointException.java

    package com.soecode.lyf.exception;
    
    /**
     * 预约业务异常
     */
    public class AppointException extends RuntimeException {
    
     public AppointException(String message) {
     super(message);
     }
    
     public AppointException(String message, Throwable cause) {
     super(message, cause);
     }
    
    }

    咱们终于可以编写业务代码了,在service包下新建BookService.java图书业务接口。

    BookService.java

    package com.soecode.lyf.service;
    
    import java.util.List;
    
    import com.soecode.lyf.dto.AppointExecution;
    import com.soecode.lyf.entity.Book;
    
    /**
     * 业务接口:站在"使用者"角度设计接口 三个方面:方法定义粒度,参数,返回类型(return 类型/异常)
     */
    public interface BookService {
    
     /**
     * 查询一本图书
     * 
     * @param bookId
     * @return
     */
     Book getById(long bookId);
    
     /**
     * 查询所有图书
     * 
     * @return
     */
     List<Book> getList();
    
     /**
     * 预约图书
     * 
     * @param bookId
     * @param studentId
     * @return
     */
     AppointExecution appoint(long bookId, long studentId);
    
    }

    在service.impl包下新建BookServiceImpl.java使用BookService接口,并实现里面的方法。

    BookServiceImpl

    package com.soecode.lyf.service.impl;
    
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.soecode.lyf.dao.AppointmentDao;
    import com.soecode.lyf.dao.BookDao;
    import com.soecode.lyf.dto.AppointExecution;
    import com.soecode.lyf.entity.Appointment;
    import com.soecode.lyf.entity.Book;
    import com.soecode.lyf.enums.AppointStateEnum;
    import com.soecode.lyf.exception.AppointException;
    import com.soecode.lyf.exception.NoNumberException;
    import com.soecode.lyf.exception.RepeatAppointException;
    import com.soecode.lyf.service.BookService;
    
    @Service
    public class BookServiceImpl implements BookService {
    
     private Logger logger = LoggerFactory.getLogger(this.getClass());
    
     // 注入Service依赖
     @Autowired
     private BookDao bookDao;
    
     @Autowired
     private AppointmentDao appointmentDao;
    
    
     @Override
     public Book getById(long bookId) {
     return bookDao.queryById(bookId);
     }
    
     @Override
     public List<Book> getList() {
     return bookDao.queryAll(0, 1000);
     }
    
     @Override
     @Transactional
     /**
     * 使用注解控制事务方法的优点: 1.开发团队达成一致约定,明确标注事务方法的编程风格
     * 2.保证事务方法的执行时间尽可能短,不要穿插其他网络操作,RPC/HTTP请求或者剥离到事务方法外部
     * 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制
     */
     public AppointExecution appoint(long bookId, long studentId) {
     try {
     // 减库存
     int update = bookDao.reduceNumber(bookId);
     if (update <= 0) {// 库存不足
     //return new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);//错误写法 
     throw new NoNumberException("no number");
     } else {
     // 执行预约操作
     int insert = appointmentDao.insertAppointment(bookId, studentId);
     if (insert <= 0) {// 重复预约
     //return new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);//错误写法
     throw new RepeatAppointException("repeat appoint");
     } else {// 预约成功
     Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);
     return new AppointExecution(bookId, AppointStateEnum.SUCCESS, appointment);
     }
     }
     // 要先于catch Exception异常前先catch住再抛出,不然自定义的异常也会被转换为AppointException,导致控制层无法具体识别是哪个异常
     } catch (NoNumberException e1) {
     throw e1;
     } catch (RepeatAppointException e2) {
     throw e2;
     } catch (Exception e) {
     logger.error(e.getMessage(), e);
     // 所有编译期异常转换为运行期异常
     //return new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);//错误写法
     throw new AppointException("appoint inner error:" + e.getMessage());
     }
     }
    
    }

    下面我们来测试一下我们的业务代码吧~因为查询图书的业务不复杂,所以这里只演示我们最重要的预约图书业务!!

    BookServiceImplTest.java

    package com.soecode.lyf.service.impl;
    
    import static org.junit.Assert.fail;
    
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.soecode.lyf.BaseTest;
    import com.soecode.lyf.dto.AppointExecution;
    import com.soecode.lyf.service.BookService;
    
    public class BookServiceImplTest extends BaseTest {
    
     @Autowired
     private BookService bookService;
    
     @Test
     public void testAppoint() throws Exception {
     long bookId = 1001;
     long studentId = 12345678910L;
     AppointExecution execution = bookService.appoint(bookId, studentId);
     System.out.println(execution);
     }
    
    }

    BookServiceImplTest测试结果

    testAppoint

    首次执行是“预约成功”,如果再次执行的话,应该会出现“重复预约”,哈哈,我们所有的后台代码都通过单元测试啦~~是不是很开心~

    咱们还需要在dto包里新建一个封装json返回结果的类Result.java,设计成泛型。

    Result.java

    package com.soecode.lyf.dto;
    
    /**
     * 封装json对象,所有返回结果都使用它
     */
    public class Result<T> {
    
     private boolean success;// 是否成功标志
    
     private T data;// 成功时返回的数据
    
     private String error;// 错误信息
    
     public Result() {
     }
    
     // 成功时的构造器
     public Result(boolean success, T data) {
     this.success = success;
     this.data = data;
     }
    
     // 错误时的构造器
     public Result(boolean success, String error) {
     this.success = success;
     this.error = error;
     }
    
     // 省略getter和setter方法
    }

    最后,我们写web层,也就是controller,我们在web包下新建BookController.java文件。

    BookController.java

    package com.soecode.lyf.web;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Param;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.soecode.lyf.dto.AppointExecution;
    import com.soecode.lyf.dto.Result;
    import com.soecode.lyf.entity.Book;
    import com.soecode.lyf.enums.AppointStateEnum;
    import com.soecode.lyf.exception.NoNumberException;
    import com.soecode.lyf.exception.RepeatAppointException;
    import com.soecode.lyf.service.BookService;
    
    @Controller
    @RequestMapping("/book") // url:/模块/资源/{id}/细分 /seckill/list
    public class BookController {
    
     private Logger logger = LoggerFactory.getLogger(this.getClass());
    
     @Autowired
     private BookService bookService;
    
     @RequestMapping(value = "/list", method = RequestMethod.GET)
     private String list(Model model) {
     List<Book> list = bookService.getList();
     model.addAttribute("list", list);
     // list.jsp + model = ModelAndView
     return "list";// WEB-INF/jsp/"list".jsp
     }
    
     @RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET)
     private String detail(@PathVariable("bookId") Long bookId, Model model) {
     if (bookId == null) {
     return "redirect:/book/list";
     }
     Book book = bookService.getById(bookId);
     if (book == null) {
     return "forward:/book/list";
     }
     model.addAttribute("book", book);
     return "detail";
     }
    
     //ajax json
     @RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = {
     "application/json; charset=utf-8" })
     @ResponseBody
     private Result<AppointExecution> appoint(@PathVariable("bookId") Long bookId, @RequestParam("studentId") Long studentId) {
     if (studentId == null || studentId.equals("")) {
     return new Result<>(false, "学号不能为空");
     }
     //AppointExecution execution = bookService.appoint(bookId, studentId);//错误写法,不能统一返回,要处理异常(失败)情况
     AppointExecution execution = null;
     try {
     execution = bookService.appoint(bookId, studentId);
     } catch (NoNumberException e1) {
     execution = new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);
     } catch (RepeatAppointException e2) {
     execution = new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);
     } catch (Exception e) {
     execution = new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);
     }
     return new Result<AppointExecution>(true, execution);
     }
    
    }

    因为我比较懒,所以我们就不测试controller了,好讨厌写前端,呜呜呜~

    到此,我们的SSM框架整合配置,与应用实例部分已经结束了,我把所有源码和jar包一起打包放在了我的GitHub上,需要的可以去下载,喜欢就给个star吧,这篇东西写了两个晚上也不容易啊。

    补充更新

    修改预约业务代码,失败时抛异常,成功时才返回结果,控制层根据捕获的异常返回相应信息给客户端,而不是业务层直接返回错误结果。上面的代码已经作了修改,而且错误示范也注释保留着,之前误人子弟了,还好有位网友前几天提出质疑,我也及时做了修改。

    修改BookController几处错误

    1.detail方法不是返回json的,故不用加@ResponseBody注解

    2.appoint方法应该加上@ResponseBody注解

    3.另外studentId参数注解应该是@RequestParam

    4.至于controller测试,测试appoint方法可不必写jsp,用curl就行,比如
    curl -H “Accept: application/json; charset=utf-8” -d “studentId=1234567890” localhost:8080/book/1003/appoint

    源码下载:

    github下载地址:http://github.com/liyifeng1994/ssm

    java异常处理教程及实例总结

    Java异常处理实例教程

    1、什么是异常?
    首先,让我们来看看下图的例子:
    在这个例子中,存在的错误码由除以0的结果。由于除以0而导致异常: ArithmeticException
    HelloException.java

    package com.yiibai.tutorial.exception;
    
    public class HelloException {
    
      public static void main(String[] args) {
    
        System.out.println("Three");
    
        // This division no problem.
        int value = 10 / 2;
    
        System.out.println("Two");
    
        // This division no problem.
        value = 10 / 1;
    
        System.out.println("One");
        
        // This division has problem, divided by 0.
        // An error has occurred here.
        value = 10 / 0;
    
        // And the following code will not be executed.
        System.out.println("Let's go!");
    
      }
    
    }
    
    

    运行这个例子,得到的结果是:

    可以看到控制台屏幕上的通知。错误通知是很清楚的,包括代码行的信息。
    让我们通过下图中的流程看看下面的程序:

    • 程序从(1),(2)至(5)步骤正常运行。
    • 在步骤(6)程序除以0。
    • 程序跳转出 main 方法后,而(7)代码行还没有被执行。

    我们将修改上述实施例的代码。

    HelloCatchException.java

    package com.yiibai.tutorial.exception;
    
    public class HelloCatchException {
    
      public static void main(String[] args) {
    
        System.out.println("Three");
    
        // This division no problem.
        int value = 10 / 2;
    
        System.out.println("Two");
    
        // This division no problem.
        value = 10 / 1;
    
        System.out.println("One");
    
        try {
          // This division has problem, divided by 0.
          // An error has occurred here.      
          value = 10 / 0;
    
          // And the following code will not be executed.
          System.out.println("Value =" + value);
    
        } catch (ArithmeticException e) {
    
          // The code in the catch block will be executed
          System.out.println("Error: " + e.getMessage());
    
          // The code in the catch block will be executed
          System.out.println("Ignore...");
    
        }
    
        // This code is executed
        System.out.println("Let's go!");
    
      }
    
    }
    
    

    运行示例结果:

    Three

    Two

    One

    Error: / by zero

    Ignore...

    Let's go!

    我们将按以下实例图像的流程来解释下面的程序。

    • 步骤(1)至(5)是完全正常的。
    • 异常发生在步骤(6),除以0出现了问题。
    • 它立即跳到catch块执行命令,步骤(7)被跳过。
    • 步骤(8),(9)被执行。
    • 步骤(10)被执行。

    2、 异常层次结构
    这是Java异常的分层图的模型。

    最高的类是:Throwable

    两个直接子类是 Error 和 Exception。

    在异常转移有一个RuntimeException子类,包括Java中的编译时未检查异常。检查并取消检查在编译时,在下一部分的实施示例中说明。

    注意:您的类应该从两个分支:Error或Exception继承,而不是直接从Throwable继承。

    当一个动态链接失败,或在虚拟机的一些其他的“硬”故障发生时,虚拟机引发这个错误。典型的Java程序不捕获错误,所以Java程序都不会抛出任何错误。大多数程序抛出并捕获从Exception类派生的对象。异常指示出现了一个问题,但是这些问题并不是严重系统性问题。你写的大多数程序将会抛出和捕获异常。

    Exception类在Java包定义了许多子类。这些子类指明不同类型的可能会发生异常。 例如,NegativeArraySizeException表明程序试图创建一个大小为负的数组。

    一个导演的子类在Java语言中的特殊含义: RuntimeException类表示Java虚拟机中发生(在运行期间)的异常。运行时异常的一个例子是NullYiibaierException异常,其中,当一种方法试图通过一个空引用来访问对象的成员时就会引发。 NullYiibaierException 可以在任何地方出现某个程序试图取消引用一个对象的引用。经常检查异常捕获的好处远远超过它的成本。

    由于运行时异常是无所不在的,在试图捕获或指定所有的时间是徒劳的作法(不可读和不可维护的代码), 编译器允许运行时异常去未捕获和指定。
    Java包定义几个RuntimeException类。您可以捕获这些异常,就像其他异常。但是并不需要一种方法来指定它抛出运行时异常。此外可以创建自己的RuntimeException子类。 运行时异常 - 下面讨论包含何时以及如何使用运行时异常进行了深入探讨。 3、使用try-catch处理异常

    编写从Exception 继承的类。

    AgeException.java

    package com.yiibai.tutorial.exception.basic;
    
    public class AgeException extends Exception {
    
      public AgeException(String message) {
        super(message);
      }
    
    }
    TooYoungException.java
    package com.yiibai.tutorial.exception.basic;
    
    public class TooYoungException extends AgeException {
    
     public TooYoungException(String message) {
       super(message);
     }
    
    }
    
    

    TooOldException.java

    package com.yiibai.tutorial.exception.basic;
    
    public class TooOldException extends AgeException {
    
     public TooOldException(String message) {
       super(message);
     }
    
    }
    
    

    以及AgeUtils类检查年龄的检查静态方法。
    AgeUtils.java

    package com.yiibai.tutorial.exception.basic;
    
    public class AgeUtils {
    
     
      // This method checks the age.
      // If age is less than 18, the method will throw an exception TooYoungException
      // If age greater than 40, the method will throw an exception TooOldException
      public static void checkAge(int age) throws TooYoungException,
          TooOldException {
        if (age < 18) {
    
          // If age is less than 18, an exception will be thrown
          // This method ends here.
          throw new TooYoungException("Age " + age + " too young");
        } else if (age > 40) {
    
          // If age greater than 40, an exception will be thrown.
          // This method ends here.
          throw new TooOldException("Age " + age + " too old");
        }
    
        // If age is between 18-40.
        // This code will be execute.
        System.out.println("Age " + age + " OK!");
      }
    }
    
    

    检查异常和未经检查的异常:
    AgeException是Exception,TooOldException的子类和TooYoungException2是 AgeException直接子类,所以它们是“Checked Exception”
    在AgeUtils.checkAge(int)方法已经抛出异常,需要通过关键字“throws”,列出它们的方法声明。或者可以声明抛出更多的级别。
    在使用 AgeUtils.checkAge(int) 位置也必须进行处理,以捕获异常,或继续抛出去。

    "Checked exception" 是由 "Java Compiler"来检查。

    有两个选择:

    TryCatchDemo1.java

    package com.yiibai.tutorial.exception.basic;
    
    public class TryCatchDemo1 {
    
      public static void main(String[] args) {
    
    
        System.out.println("Start Recruiting ...");
        // Check age
        System.out.println("Check your Age");
        int age = 50;
    
        try {
    
          AgeUtils.checkAge(age);
    
          System.out.println("You pass!");
    
        } catch (TooYoungException e) {
    
          // Do something here ..
          System.out.println("You are too young, not pass!");
          System.out.println(e.getMessage());
    
        } catch (TooOldException e) {
    
          // Do something here ..
          System.out.println("You are too old, not pass!");
          System.out.println(e.getMessage());
    
        }
    
      }
    }
    
    

    在下面的例子中,我们将通过父类捕获异常(超Exception类)。
    TryCatchDemo2.java

    package com.yiibai.tutorial.exception.basic;
    
    public class TryCatchDemo2 {
    
      public static void main(String[] args) {
    
        System.out.println("Start Recruiting ...");
        // Check age
        System.out.println("Check your Age");
        int age = 15;
    
        try {
    
          // Here can throw TooOldException or TooYoungException
          AgeUtils.checkAge(age);
    
          System.out.println("You pass!");
    
        } catch (AgeException e) {
          
          // If an exception occurs, type of AgeException
          // This catch block will be execute
          System.out.println("Your age invalid, you not pass");
          System.out.println(e.getMessage());
    
        }
      }
    }
    
    

    也可以组不同的异常在块中来处理,如果它们对逻辑程序处理是相同的方式。
    TryCatchDemo3.java

    package com.yiibai.tutorial.exception.basic;
    
    public class TryCatchDemo3 {
    
      public static void main(String[] args) {
    
        System.out.println("Start Recruiting ...");
        // Check age
        System.out.println("Check your Age");
        int age = 15;
    
        try {
    
          // Here can throw TooOldException or TooYoungException
          AgeUtils.checkAge(age);
    
          System.out.println("You pass!");
    
        } catch (TooYoungException | TooOldException e) {
          // Catch multi exceptions in one block.
    
          System.out.println("Your age invalid, you not pass");
          System.out.println(e.getMessage());
    
        }
      }
    
    }
    
    

    4、 try-catch-finally
    我们已习惯于通过 try-catch 块捕获错误。Try-catch-finally 来完全处理异常。

    try {
    
      // Do something here
    
    } catch (Exception1 e) {
    
      // Do something here
    
    } catch (Exception2 e) {
    
      // Do something here
    
    } finally {
    
      // Finally block is always executed
      // Do something here
    
    }
    
    

    TryCatchFinallyDemo.java

    package com.yiibai.tutorial.exception.basic;
    
    public class TryCatchFinallyDemo {
    
      public static void main(String[] args) {
    
        String text = "001234A2";
    
        int value = toInteger(text);
    
        System.out.println("Value= " + value);
    
      }
    
      public static int toInteger(String text) {
        try {
    
          System.out.println("Begin parse text: " + text);
    
          // An Exception can throw here (NumberFormatException).
          int value = Integer.parseInt(text);
    
          return value;
    
        } catch (NumberFormatException e) {
    
          
          // In the case of 'text' is not a number.
          // This catch block will be executed.      
          System.out.println("Number format exception " + e.getMessage());
    
          // Returns 0 if NumberFormatException occurs
          return 0;
    
        } finally {
    
          System.out.println("End parse text: " + text);
    
        }
      }
    
    }
    
    

    这是程序的流程。 finally块无论什么情况下总会被执行。

    5、 环绕异常

    • 我们需要一些类参与到这个例子:
    • Person: 模拟一个受试者招募到公司的信息:姓名,年龄,性别。
    • GenderException: 性别异常。
    • ValidateException: 异常评估求职者。
    • ValidateUtils: 静态方法类综合评价面试者。
    • 如果男性年龄在18-40之间的被认为是有效的。

    Person.java

    package com.yiibai.tutorial.exception.wrap;
    
    public class Person {
    
      public static final String MALE = "male";
      public static final String FEMALE = "female";
    
      private String name;
      private String gender;
      private int age;
    
      public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
      }
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    
      public String getGender() {
        return gender;
      }
    
      public void setGender(String gender) {
        this.gender = gender;
      }
    
      public int getAge() {
        return age;
      }
    
      public void setAge(int age) {
        this.age = age;
      }
    }
    
    

    GenderException.java

    package com.yiibai.tutorial.exception.wrap;
    
    // Gender Exception.
    public class GenderException extends Exception {
    
       public GenderException(String message) {
         super(message);
       }
    }
    
    

    ValidateException 类包有其他异常。
    ValidateException.java

    package com.yiibai.tutorial.exception.wrap;
    
    public class ValidateException extends Exception {
      
      // Wrap an Exception
      public ValidateException(Exception e) {
        super(e);
      }
    
    }
    
    

    ValidateUtils.java

    package com.yiibai.tutorial.exception.wrap;
    
    import com.yiibai.tutorial.exception.basic.AgeUtils;
    
    public class ValidateUtils {
    
      public static void checkPerson(Person person) throws ValidateException {
        try {
    
          // Check age.
          // Valid if between 18-40
          // This method can throw TooOldException, TooYoungException.    
          AgeUtils.checkAge(person.getAge());
    
        } catch (Exception e) {
          
          // If not valid
          // Wrap this exception by ValidateException, and throw
          throw new ValidateException(e);
    
        }
    
        // If that person is Female, ie invalid.
        if (person.getGender().equals(Person.FEMALE)) {
    
          GenderException e = new GenderException("Do not accept women");
          throw new ValidateException(e);
    
        }
      }
    
    }
    
    

    WrapperExceptionDemo.java

    package com.yiibai.tutorial.exception.wrap;
    
    public class WrapperExceptionDemo {
    
      public static void main(String[] args) {
        
        // One participant recruitment.
        Person person = new Person("Marry", Person.FEMALE, 20);
    
        try {
    
          // Exceptions may occur here.
          ValidateUtils.checkPerson(person);
    
        } catch (ValidateException wrap) {
    
          // Get the real cause.
          // May be TooYoungException, TooOldException, GenderException
          Exception cause = (Exception) wrap.getCause();
    
          if (cause != null) {
            System.out.println("Not pass, cause: " + cause.getMessage());
          } else {
            System.out.println(wrap.getMessage());
          }
    
        }
      }
    
    }
    
    

    6、RuntimeException和子类 RuntimeException类及其子类都是“未检查的例外”。它不是由Java编译器在编译时进行检查。在某些情况下,你可以从这个分支继承编写自己的异常。

    下面是属于RuntimeException分支一些类(当然,这还不是全部)。
    一些处理这种类型异常的例子:

    6.1- NullYiibaierException
    这是最常见的异常,通常会导致错误在程序中。异常被抛出,当你调用方法或访问一个空对象的字段。
    NullYiibaierExceptionDemo.java

    package com.yiibai.tutorial.exception.runtime;
    
    public class NullYiibaierExceptionDemo {
    
      // For example, here is a method that can return null string.
      public static String getString() {
        if (1 == 2) {
          return "1==2 !!";
        }
        return null;
      }
    
      public static void main(String[] args) {
    
        // This is an object that references not null.
        String text1 = "Hello exception";
    
        // Call the method retrieves the string length.
        int length = text1.length();
    
        System.out.println("Length text1 = " + length);
    
        // This is an object that references null.
        String text2 = getString();
        
        // Call the method retrieves the string length.
        // NullYiibaierException will occur here.
        // It is an exception occurs at runtime (type of RuntimeException)
        // Javac compiler does not force you to use a try-catch block to handle it
        length = text2.length();
    
        System.out.println("Finish!");
      }
    
    }
    
    

    运行示例的结果:

    在现实中,像处理其他异常时,可以使用 try-catch 来捕获并处理这个异常。 然而,这是机械的,通常情况下,我们应该检查,以确保在使用它之前,对象不为空值。
    您可以更正上面的代码,使其类似于下面的以避免空指针异常:

    // This is a null object.
    String text2 = getString();
    
    // Check to make sure 'Text2' are not null.
    // Instead of using try-catch.
    if (text2 != null) {
     length = text2.length();
    }
    
    

    6.2- ArrayIndexOfBoundException
    当您试图访问一个无效的索引的数组元素就会发生此异常。例如,一个数组有10个元素可以访问,但您访问的是索引为20的元素。
    ArrayIndexOfBoundsExceptionDemo.java

    package com.yiibai.tutorial.exception.runtime;
    
    public class ArrayIndexOfBoundsExceptionDemo {
    
      public static void main(String[] args) {
    
        String[] strs = new String[] { "One", "Two", "Three" };
    
        // Access to the element has index 0.
        String str1 = strs[0];
    
        System.out.println("String at 0 = " + str1);
    
        
        // Access to the element has index 5.
        // ArrayIndexOfBoundsException occur here.
        String str2 = strs[5];
    
        System.out.println("String at 5 = " + str2);
    
      }
    
    }
    
    

    为了避免 ArrayIndexOfBoundsException,我们更多的应该是检查数组而不是使用try-catch。

    if (strs.length > 5) {
      String str2 = strs[5];
      System.out.println("String at 5 = " + str2);
    } else {
      System.out.println("No elements with index 5");
    }

    以上就是本文的全部内容,希望对大家的学习有所帮助。

    Java学习笔记之异常处理

    一.异常的分类

    1.由Java虚拟机抛出的异常(Error):程序无法处理的问题,用户不用去进行处理(虚拟机错误丶内存溢出错误丶线程死锁)

    2.Exception异常:程序本身可以进行处理的异常

    1).非检查异常(Unchecked Exception):编译器不需要强制处理的异常(空指针异常丶数组下标越界异常丶算数异常丶类型转换异常)

    2).检查异常(checked Exception):编译器需要强制处理的异常(IOException丶SQLException) 

    二.异常处理的两种方法

    1.通过try丶catch和finally关键字在当前位置进行异常处理

    public static void main(String[] a){
     int sum = 0;
     while(true){
      try {  //以两数相除除数不能为0进行举例
       System.out.println("请依次输入两个数值进行除法操作:");
       Scanner scanner = new Scanner(System.in);
       int one =scanner.nextInt();
       int two =scanner.nextInt();
       sum = one/two;
       System.out.println("最终结果为:"+sum);
      } catch (Exception e) {    //用catch将错误进行捕捉,这里可以使用多重catch,对于不同的错误进行捕捉,但最后的catch建议为Exception。
       // TODO Auto-generated catch block //显示错误堆栈信息
       e.printStackTrace();                          
      }finally{            
    
     System.out.print("无论有没有错误我都会执行");          }
     }
     }
    }

    输出:

    2.通过try丶catch丶finally丶throw和throws抛出异常给函数调用者进行处理

    public class Try {
     public static void main(String[] a){
      try{
       Function();  //在函数调用者处对异常进行处理
      }catch(Exception e)
      {
       e.printStackTrace();
      } 
     }
     static void Function() throws Exception{  //通过throws将异常进行抛出
      
      System.out.println("请输入一个数值进行判断:");
      Scanner scanner = new Scanner(System.in);
      int one =scanner.nextInt();
      if(one<100)
      {
       throw new Exception(); //若输入的数值小于100则抛出异常
      }
     } 
    }
    

    输出:

    3.自定义异常进行处理

    class MyException extends Exception{  //自定义异常,通过super方法传递异常信息给父级
     public MyException(){
     super("这是我自定义的异常");
     }
    }
     
    public class Try {
     public static void main(String[] a){
      try{
       Function();
      }catch(MyException e)
      {
       e.printStackTrace();
      } 
     }
     static void Function() throws MyException{
      
      System.out.println("请输入一个数值进行判断:");
      Scanner scanner = new Scanner(System.in);
      int one =scanner.nextInt();
      if(one<100)
      {
       throw new MyException(); //将自定义异常进行抛出  
      }
     } 
    }
    

    输出:

    三.异常链

    有的时候我们会在处理一个异常的时候抛出一个新的异常,也就是异常的嵌套,但是最后我们得到的异常信息却只有一个。

    示例:

    public class Try {
     public static void main(String[] a){
      try{
       Function1();
      }catch(Exception e)
      {
       e.printStackTrace();
      } 
     }
     static void Function1() throws Exception{
      try{
       Function2();
      }catch(Exception e){
       throw new Exception();
      }
     } 
     static void Function2() throws Exception{
      try{
       Function3();
      }catch(Exception e){
       throw new Exception();
      }
     } 
     static void Function3() throws Exception{
       throw new Exception();
     } 
    }
    

    输入结果:

    这样的话显示出的异常就只有一个了,那我们如果想让这条异常链中的所有异常信息全部输出该怎么办呢?方法很简单,我们在抛出异常的时候将异常对象也当作参数进行抛出就行了。

    示例:

    public class Try {
     public static void main(String[] a){
      try{
       Function1();
      }catch(Exception e)
      {
       e.printStackTrace();
      } 
     }
     static void Function1() throws Exception{
      try{
       Function2();
      }catch(Exception e){
       throw new Exception("异常2",e);
      }
     } 
     static void Function2() throws Exception{
      try{
       Function3();
      }catch(Exception e){
       throw new Exception("异常2",e);
      }
     } 
     static void Function3() throws Exception{
       throw new Exception("异常3");
     } 
    }
    

    运行结果:

    到此,我们Java中的异常便是描述完了。

    上一篇:量化交易之路-用Python做股票量化分析  下一篇:PHP7从入门到精通:教学版

    展开 +

    收起 -

     
    Spring 相关内容
    《Spring MVC学习指南》学习笔记

    Copyright 2018-2020 xz577.com 码农之家

    本站所有电子书资源不再提供下载地址,只分享来路

    免责声明:网站所有作品均由会员网上搜集共同更新,仅供读者预览及学习交流使用,下载后请24小时内删除

    版权投诉 / 书籍推广 / 赞助:QQ:520161757