Java EE核心框架实战(第二版) PDF 高清版

  • 更新时间:
  • 5707人关注
  • 点击下载

给大家带来的一篇关于Java相关的电子书资源,介绍了关于Java、Java核心框架、Java实战方面的内容,本书是由人民邮电出版社出版,格式为PDF,资源大小24.72MB,高洪岩编写,目前豆瓣、亚马逊、当当、京东等电子书综合评分为:9.9分

资源详情相关推荐
Java EE核心框架实战(第二版)
  • 出版社:人民邮电出版社
  • 作者:高洪岩
  • 大小:24.72MB
  • 类别:Java
  • 热度:627
  • Java并发编程的艺术
  • 菜鸟成长之路:Java程序员职场全攻略
  • Java开发实战1200例 (第Ⅰ卷)
  • 精通lambda表达式:Java多核编程
  • Java程序设计教程(第2版)
  • Java EE核心框架实战 第二版

    出版时间:2017

    《Java EE核心框架实战(第2版)》的宗旨是提高读者学习Java EE的效率,增强其项目实战能力。为此,本书摒弃了软件公司中不常用或不实用的技术,而是采用近200个开发案例,为读者讲解了开发商业软件的知识,帮助读者进行“精要”式的学习,汲取Java EE的思想,正确地进行项目实战。《Java EE核心框架实战(第2版)》涵盖了MyBatis 3、Struts 2、Ajax、JSON、jQuery、Spring 4 MVC、Hibernate 5、Spring 4等主流Java EE框架的核心开发技术,介绍了MVC框架的原理实现、上传、下载、数据验证、国际化、多模块分组开发、转发/重定向、JSON的解析;将Ajax及JSON和MVC框架进行整合开发;ORM框架的CURD操作以及MyBatis和Hibernate中的映射文件使用。本书还使用大量篇幅介绍了Spring 4中的核心技术DI与AOP,以及企业中常用框架的整合开发,框架包含Struts 2、Spring 4 MVC、MyBatis 3、Hibernate 5、Spring 4整合开发等内容。《Java EE核心框架实战(第2版)》语言简洁,示例丰富,可帮助读者迅速掌握使用主流开源Java EE框架进行开发所需的各种技能。本书适合具有一定Java编程基础的读者,以及使用Java进行软件开发、测试的从业人员阅读。

    目录

    • 第1章 MyBatis 3操作数据库1
    • 1.1MyBatis介绍1
    • 1.2MyBatis基本使用4
    • 1.2.1使用XML配置文件创建SqlSessionFactory对象5
    • 1.2.2SqlSessionFactoryBuilder和SqlSessionFactory类信息7
    • 1.2.3使用MyBatis Generator工具逆向操作Oracle数据库7
    • 1.2.4使用MyBatis Generator工具逆向操作MySQL数据库14
    • 1.3使用MyBatis实现CURD-2种数据库(Oracle-MySQL)16
    • 1.3.1针对Oracle的CURD17
    • 1.3.2针对MySQL的CURD23
    • 1.3.3SQL映射文件中namespace命名空间的作用24
    • 1.4MyBatis核心对象的生命周期与封装26
    • 1.4.1创建GetSqlSessionFactory.java类27
    • 1.4.2创建GetSqlSession.java类28
    • 1.4.3创建DBOperate.java类29
    • 1.4.4创建userinfoMapping.xml映射文件29
    • 1.4.5创建连接数据库mybatis-config.xml配置文件30
    • 1.4.6创建名称为test的Servlet对象30
    • 1.4.7添加记录及异常回滚的测试31
    • 1.4.8删除记录33
    • 1.4.9更改记录34
    • 1.4.10查询单条记录35
    • 1.4.11查询多条记录36
    • 第2章 MyBatis 3必备开发技能37
    • 2.1使用getMapper()面向接口编程37
    • 2.1.1接口-SQL映射的对应关系37
    • 2.1.2创建Userinfo.java实体类38
    • 2.1.3创建UserinfoMapper.java接口38
    • 2.1.4创建SQL映射文件UserinfoMapper.xml39
    • 2.1.5增加记录insert的操作代码39
    • 2.1.6查询全部selectAll的操作代码41
    • 2.1.7查询单条记录selectById的操作代码41
    • 2.1.8修改记录updateById的操作代码42
    • 2.1.9删除记录deleteById的操作代码43
    • 2.2使用typeAliases配置别名43
    • 2.2.1使用typeAlias单独配置别名44
    • 2.2.2使用package批量配置别名44
    • 2.3使用properties文件保存数据库信息46
    • 2.3.1在mybatis-config.xml配置文件中读取properties文件中的参数46
    • 2.3.2将数据库信息封装进properties属性文件中47
    • 2.4与数据源DataSource有关的操作49
    • 2.4.1配置多个environment环境49
    • 2.4.2什么是JNDI以及如何从JNDI获得数据源DataSource50
    • 2.4.3如何在MyBatis中使用数据源DataSource54
    • 2.4.4在MyBatis中使用HikariCP连接池57
    • 2.5多数据库执行不同SQL语句的支持60
    • 2.6多种获取Mapper的方式61
    • 2.7MyBatis3的SQL映射文件与动态SQL62
    • 2.7.1标签62
    • 2.7.2标签64
    • 2.7.3使用${}拼接SQL语句65
    • 2.7.4插入null值时的处理第1种方法jdbcType67
    • 2.7.5插入null值时的处理第2种方法68
    • 2.7.6标签69
    • 2.7.7标签的使用71
    • 2.7.8标签的使用72
    • 2.7.9标签的使用73
    • 2.7.10使用标签对like语句进行适配77
    • 2.8插入超大的字符串String文本内容81
    • 2.9对查询的数据进行分页84
    • 2.10批处理SQL语句86
    • 第3章 Struts 2必备开发技能88
    • 3.1使用Struts 2进行登录功能的开发88
    • 3.1.1为什么要使用MVC89
    • 3.1.2准备jar文件94
    • 3.1.3创建Web项目、添加jar文件及配置web.xml文件96
    • 3.1.4创建控制层Controller文件-Login.java96
    • 3.1.5创建业务逻辑层Model文件-UserinfoService.java97
    • 3.1.6创建视图层View文件-login.jsp98
    • 3.1.7添加核心配置文件struts.xml及解释98
    • 3.1.8添加ok.jsp和no.jsp登录结果文件99
    • 3.1.9运行项目99
    • 3.1.10Struts 2的拦截器101
    • 3.1.11Struts 2的数据类型自动转换106
    • 3.2MVC框架的开发模型112
    • 3.2.1基础知识准备1——XML文件的CURD113
    • 3.2.2基础知识准备2——Java的反射120
    • 3.2.3实现MVC模型——自定义配置文件122
    • 3.2.4实现MVC模型——ActionMapping.java封装信息123
    • 3.2.5实现MVC模型——ResultMapping.java封装信息124
    • 3.2.6实现MVC模型——管理映射信息的ActionMappingManager.java对象124
    • 3.2.7实现MVC模型——创建反射Action的ActionManager.java对象126
    • 3.2.8实现MVC模型——创建核心控制器ActionServlet.java127
    • 3.2.9实现MVC模型——创建Action接口及控制层Controller实现类129
    • 3.2.10实现MVC模型——创建视图层V对应的JSP文件130
    • 3.2.11实现MVC模型——在web.xml中配置核心控制器131
    • 3.2.12实现MVC模型——运行效果131
    • 3.3Struts 2的刷新验证功能131
    • 3.3.1Action接口132
    • 3.3.2Validateable和ValidationAware接口133
    • 3.3.3TextProvider和LocaleProvider接口134
    • 3.3.4使用ActionSupport实现有刷新的验证134
    • 3.4对Struts 2有刷新验证的示例进行升级137
    • 3.4.1加入xml配置来屏蔽自动生成的table/tr/td代码137
    • 3.4.2解决“出错信息不能自动显示”的问题139
    • 3.5用标签显示全部出错信息142
    • 3.6出错信息进行传参及国际化144
    • 3.6.1创建info_en_US.properties和info_zh_CN.properties属性文件144
    • 3.6.2在JSP文件中显示国际化的静态文本147
    • 3.6.3在JSP文件中显示国际化的静态文本时传递参数149
    • 3.6.4在Action中使用国际化功能149
    • 3.7用实体类封装URL中的参数——登录功能的URL封装151
    • 3.8Struts 2中的转发操作153
    • 3.8.1Servlet中的转发操作153
    • 3.8.2Struts 2中的转发操作154
    • 3.9由Action重定向到Action——无参数157
    • 3.9.1什么样的情况下使用重定向157
    • 3.9.2新建起始控制层Login.java157
    • 3.9.3新建目的控制层List.java157
    • 3.9.4在struts.xml文件中配置重定向的重点158
    • 3.9.5新建显示列表的JSP文件159
    • 3.10由Action重定向到Action——有参数159
    • 3.10.1什么样的情况下需要重定向传递参数159
    • 3.10.2新建起始控制层Login.java文件159
    • 3.10.3更改struts.xml配置文件160
    • 3.10.4新建目的控制层List.java文件161
    • 3.10.5用JSTL和EL在JSP文件中打印数据162
    • 3.11让Struts 2支持多模块多配置文件开发164
    • 3.11.1新建4个模块的控制层164
    • 3.11.2新建3个模块的配置文件165
    • 3.11.3使用include标记导入多个配置文件167
    • 3.11.4创建各模块使用的JSP文件167
    • 3.11.5运行各模块的效果168
    • 3.12在Action中有多个业务方法时的处理169
    • 3.13自定义全局result171
    • 3.13.1新建全局result实例和控制层代码171
    • 3.13.2声明全局的result对象172
    • 3.13.3部属项目并运行172
    • 3.14在Action中使用servlet的API(紧耦版)173
    • 3.14.1将数据放到不同的作用域中173
    • 3.14.2从不同作用域中取值174
    • 3.15在Action中使用Servlet的API(松耦版)175
    • 3.15.1新建控制层175
    • 3.15.2新建JSP视图176
    • 第4章 Struts 2文件的上传与下载178
    • 4.1使用Struts 2进行单文件上传178
    • 4.1.1Struts 2上传功能的底层依赖178
    • 4.1.2新建上传文件的JSP文件178
    • 4.1.3新建上传文件的控制层Register.java文件179
    • 4.1.4Action中File实例的命名规则180
    • 4.1.5设置上传文件的大小180
    • 4.1.6设计struts.xml配置文件180
    • 4.1.7成功上传单个文件181
    • 4.2使用Struts 2进行多文件上传181
    • 4.2.1新建上传多个文件的JSP182
    • 4.2.2设计上传的控制层代码182
    • 4.2.3成功上传多个文件184
    • 4.3使用属性驱动形式的文件上传185
    • 4.3.1创建上传多个文件的JSP185
    • 4.3.2设计上传文件的控制层186
    • 4.3.3新建上传文件的封装类187
    • 4.3.4将JSP文件中s:file标签的name属性进行更改188
    • 4.3.5以属性驱动方式成功上传多个文件189
    • 4.4用Struts 2实现下载文件的功能(支持中文文件名与IE和FireFix兼容)190
    • 4.4.1新建下载文件的JSP文件190
    • 4.4.2新建下载文件的控制层文件191
    • 4.4.3更改struts.xml配置文件192
    • 4.4.4成功下载中文文件名的文件192
    • 第5章 JSON、Ajax和jQuery与Struts 2联合使用193
    • 5.1JSON介绍193
    • 5.2用JSON创建对象194
    • 5.2.1JSON创建对象的语法格式194
    • 5.2.2在JSP中用JSON创建一个对象194
    • 5.2.3运行效果194
    • 5.3用JSON创建字符串的限制195
    • 5.3.1需要转义的特殊字符195
    • 5.3.2在JSP中对JSON特殊字符进行转义195
    • 5.3.3运行效果196
    • 5.4用JSON创建数字类型语法格式196
    • 5.4.1在JSP中用JSON创建数字类型196
    • 5.4.2运行效果197
    • 5.5用JSON创建数组对象的语法格式197
    • 5.5.1JSON创建一个数组对象198
    • 5.5.2运行效果198
    • 5.6用JSON创建嵌套的对象类型198
    • 5.7将对象转成JSON字符串200
    • 5.7.1什么情况下需要将对象转成JSON字符串200
    • 5.7.2使用stringify方法将对象转成JSON字符串200
    • 5.8将对象转成JSON字符串提交到Action并解析(以post方式提交)201
    • 5.8.1在JSP中创建JSON和Ajax对象201
    • 5.8.2用Action控制层接收通过Ajax传递过来的JSON字符串202
    • 5.8.3运行效果203
    • 5.8.4在控制台输出的数据204
    • 5.9将对象转成JSON字符串提交到Action并解析(get方式提交)204
    • 5.9.1新建创建JSON字符串的JSP文件204
    • 5.9.2新建接收JSON字符串的Action控制层205
    • 5.9.3运行结果206
    • 5.9.4在控制台输出的数据206
    • 5.10将数组转成JSON字符串提交到Action并解析(get和post方式提交)207
    • 5.10.1在服务器端用get方法解析JSON字符串208
    • 5.10.2在服务器端用post方法解析JSON字符串209
    • 5.10.3运行结果210
    • 5.10.4在控制台输出的数据210
    • 5.11使用Ajax调用Action并生成JSON再传递到客户端(get和post方式
    • 提交)210
    • 5.11.1新建具有Ajax提交功能的JSP211
    • 5.11.2在Action控制层创建List中存String213
    • 5.11.3在Action控制层创建List中存Bean214
    • 5.11.4在Action控制层创建Map中存放的String215
    • 5.11.5在Action控制层创建Map中存放的Bean216
    • 5.11.6单击不同的button按钮调用不同的Action217
    • 5.12jQuery、JSON和Struts 2218
    • 5.12.1jQuery框架的Ajax功能介绍218
    • 5.12.2用jQuery的Ajax功能调用远程action(无返回结果)219
    • 5.12.3jQuery的Ajax方法结构220
    • 5.12.4用jQuery的Ajax功能调用远程action(有返回结果)221
    • 5.12.5用jQuery的Ajax功能调用远程action并且传递JSON格式参数(有返回值)223
    • 5.12.6用jQuery解析从action返回List中存String的JSON字符串226
    • 5.13在服务器端解析复杂结构的JSON对象228
    • 第6章 用Hibernate 5操作数据库230
    • 6.1Hibernate概述与优势230
    • 6.2持久层、持久化与ORM231
    • 6.3用MyEclipse开发第一个Hibernate示例233
    • 6.3.1在MyEclipse中创建MyEclipse Database Explorer数据库连接233
    • 6.3.2创建Web项目并添加Hibernate框架235
    • 6.3.3开始Hibernate逆向239
    • 6.3.4数据访问层DAO与实体类entity的代码分析241
    • 6.3.5使用Hibernate进行持久化242
    • 6.3.6映射文件Userinfo.hbm.xml的代码分析243
    • 6.3.7查询—修改—删除的操作代码245
    • 6.3.8其他类解释247
    • 第7章 Hibernate 5核心技能248
    • 7.1工厂类HibernateSessionFactory.java中的静态代码块248
    • 7.2SessionFactory介绍249
    • 7.3Session介绍249
    • 7.4使用Session实现CURD功能250
    • 7.4.1数据表userinfo结构与映射文件250
    • 7.4.2创建SessionFactory工厂类251
    • 7.4.3添加记录251
    • 7.4.4查询单条记录252
    • 7.4.5更改记录253
    • 7.4.6删除记录253
    • 7.5Hibernate使用JNDI技术254
    • 7.5.1备份Tomcat/conf路径下的配置文件254
    • 7.5.2更改配置文件context.xml254
    • 7.5.3更改配置文件web.xml254
    • 7.5.4添加Hibernate框架配置的关键步骤255
    • 7.5.5逆向工程255
    • 7.5.6支持JNDI的hibernate.cfg.xml配置文件内容255
    • 7.5.7创建查询数据的Servlet256
    • 7.5.8部属项目验证结果256
    • 7.6缓存与实体状态256
    • 7.6.1Hibernate的OID与缓存256
    • 7.6.2Hibernate中的对象状态:瞬时状态、持久化状态和游离状态258
    • 7.7双向一对多在MyEclipse中的实现258
    • 7.7.1添加主表记录261
    • 7.7.2添加子表记录262
    • 7.7.3更改主表数据262
    • 7.7.4更改子表数据262
    • 7.7.5删除子表数据263
    • 7.7.6删除主表main数据263
    • 7.7.7通过主表获取子表数据264
    • 7.8Hibernate备忘知识点265
    • 7.9对主从表结构中的HashSet进行排序267
    • 7.10延迟加载与load()和get()的区别267
    • 7.10.1主从表表结构的设计267
    • 7.10.2对省表和市表内容的添充268
    • 7.10.3更改映射文件268
    • 7.10.4新建测试用的Servlet对象268
    • 7.10.5更改映射文件Sheng.hbm.xml269
    • 7.11Hibernate对Oracle中CLOB字段类型的读处理270
    • 7.12Hibernate中的inverse与cascade的测试270
    • 第8章 Hibernate 5使用HQL语言进行检索275
    • 8.1Hibernate的检索方式275
    • 8.1.1HQL表别名276
    • 8.1.2HQL对结果进行排序与list()和iterator()方法的区别278
    • 8.1.3HQL索引参数绑定281
    • 8.1.4HQL命名参数绑定与安全性282
    • 8.1.5HQL方法链的使用284
    • 8.1.6HQL中的uniqueResult方法的使用284
    • 8.1.7HQL中的where子句与查询条件285
    • 8.1.8查询日期——字符串格式287
    • 8.1.9查询日期——数字格式288
    • 8.1.10分页的处理289
    • 8.1.11HQL中的聚集函数:distinct-count-min-max-sum-avg290
    • 8.1.12HQL中的分组查询292
    • 第9章 JPA核心技能294
    • 9.1什么是JPA以及为什么要使用JPA294
    • 9.2搭建JPA开发环境与逆向295
    • 9.3分析逆向出来的Java类300
    • 9.4使用IUserinfoDAO.java接口中的方法301
    • 9.4.1方法public void save(Userinfo entity)的使用302
    • 9.4.2方法public Userinfo findById(Long id)的使用303
    • 9.4.3方法public List findByProperty(String propertyName, final Object value, final int... rowStartIdxAndCount)的使用304
    • 9.4.4方法public List findByUsername(Object username, int... rowStartIdxAndCount)的使用304
    • 9.4.5方法public List findByPassword(Object password, int... rowStartIdxAndCount)的使用304
    • 9.4.6方法public List findByAge(Object age, int... rowStartIdxAndCount)的使用305
    • 9.4.7方法public List findAll(final int... rowStartIdxAndCount)的使用305
    • 9.4.8方法public Userinfo update(Userinfo entity)的使用305
    • 9.4.9方法public void delete(Userinfo entity)的使用306
    • 9.5JPA核心接口介绍306
    • 9.5.1类Persistence306
    • 9.5.2JPA中的事务类型307
    • 9.5.3接口EntityManagerFactory308
    • 9.5.4接口EntityManager308
    • 9.6实体类的状态308
    • 9.7使用原生JPA的API实现1个添加记录的操作309
    • 9.8从零开始搭建JPA开发环境309
    • 9.9EntityManager核心方法的使用311
    • 9.9.1方法void persist(Object entity)保存一条记录311
    • 9.9.2 T merge(T entity)方法和 T find(Class entityClass, Object primaryKey)方法311
    • 9.9.3方法void remove(Object entity)312
    • 9.9.4getReference(Class, Object)方法312
    • 9.9.5createNativeQuery(string)方法315
    • 9.9.6clear()和contains(Object)方法317
    • 9.9.7createQuery(String)方法319
    • 9.10双向一对多的CURD实验319
    • 9.10.1逆向Main.java和Sub.java实体类319
    • 9.10.2创建Main322
    • 9.10.3创建Sub322
    • 9.10.4更新Main323
    • 9.10.5更新Sub323
    • 9.10.6删除Main时默认将Sub也一同删除324
    • 9.10.7从Main加载Sub时默认为延迟加载324
    • 9.11JPQL语言的使用325
    • 9.11.1参数索引式查询325
    • 9.11.2命名式参数查询326
    • 9.11.3JPQL支持的运算符与聚合函数与排序326
    • 9.11.4is null为空运算符的使用327
    • 9.11.5查询指定字段的示例327
    • 9.11.6JPQL语言对日期的判断329
    • 9.11.7JPQL语言中的分页功能331
    • 第10章 Spring 4的DI与AOP332
    • 10.1Spring介绍332
    • 10.2依赖注入333
    • 10.3DI容器333
    • 10.4AOP的介绍334
    • 10.5Spring的架构334
    • 10.6一个使用传统方式保存数据功能的测试335
    • 10.7在Spring中创建JavaBean336
    • 10.7.1使用xml声明法创建对象337
    • 10.7.2使用Annotation注解法创建对象340
    • 10.8DI的使用350
    • 10.8.1使用xml声明法注入对象350
    • 10.8.2使用注解声明法注入对象352
    • 10.8.3多实现类的歧义性353
    • 10.8.4使用@Autowired注解向构造方法参数注入356
    • 10.8.5在set方法中使用@Autowired注解357
    • 10.8.6使用@Bean向工厂方法的参数传参358
    • 10.8.7使用@Autowired(required = false)的写法358
    • 10.8.8使用@Bean注入多个相同类型的对象时出现异常360
    • 10.8.9使用@Bean对JavaBean的id重命名361
    • 10.8.10对构造方法进行注入362
    • 10.8.11使用p命名空间对属性进行注入368
    • 10.8.12Spring上下文环境的相关知识370
    • 10.8.13使用Spring的DI方式保存数据功能的测试375
    • 10.8.14BeanFactory与ApplicationContext377
    • 10.8.15注入null类型377
    • 10.8.16注入Properties类型378
    • 10.8.17在DI容器中创建Singleton单例和Prototype多例的JavaBean对象379
    • 10.8.18Spring中注入外部属性文件的属性值381
    • 10.9面向切面编程AOP的使用383
    • 10.9.1AOP的原理之代理设计模式384
    • 10.9.2与AOP相关的必备概念391
    • 10.9.3面向切面编程AOP核心案例395
    • 10.9.4Strust 2、Spring 4整合及应用AOP切面432
    • 第11章 Struts 2+Hibernate 5+Spring 4整合436
    • 11.1目的436
    • 11.2创建数据库环境436
    • 11.2.1新建数据表userinfo436
    • 11.2.2创建序列对象437
    • 11.3新建整合用的Web项目437
    • 11.4添加Struts 2框架支持437
    • 11.4.1添加Struts 2框架437
    • 11.4.2在web.xml文件中注册Struts 2的过滤器438
    • 11.4.3在项目的src目录下创建struts.xml配置文件438
    • 11.5添加Hibernate 5框架支持439
    • 11.6添加Spring 4框架支持440
    • 11.7创建spring-dao.xml文件440
    • 11.8创建spring-service.xml文件440
    • 11.9创建spring-controller.xml文件441
    • 11.10创建applicationContext.xml文件441
    • 11.11在web.xml文件中注册Spring监听器442
    • 11.12加Spring 4框架后的Web项目结构443
    • 11.13创建Hibernate中的实体类与映射文件444
    • 11.14创建Hibernate 5的DAO类445
    • 11.15创建UserinfoService.java服务对象445
    • 11.16新建一个操作userinfo表数据的Controller控制层446
    • 11.17测试成功的结果447
    • 11.18测试回滚的结果448
    • 第12章 Spring 4 MVC核心技能450
    • 12.1Spring 4 MVC介绍450
    • 12.1.1Spring 4 MVC核心控制器451
    • 12.1.2基于注解的Spring 4 MVC开发452
    • 12.2Spring 4 MVC第一个登录测试452
    • 12.2.1添加Spring 4 MVC的依赖jar文件452
    • 12.2.2在web.xml中配置核心控制器453
    • 12.2.3新建springMVC-servlet.xml配置文件453
    • 12.2.4新建相关的JSP文件453
    • 12.2.5新建控制层Java类文件454
    • 12.2.6部署项目并运行455
    • 12.2.7第一个示例的总结456
    • 12.2.8Spring MVC取参还能更加方便456
    • 12.3执行控制层与限制提交的方式457
    • 12.3.1新建控制层ListUsername.java文件457
    • 12.3.2新建登录及显示数据的JSP文件458
    • 12.3.3部署项目并测试458
    • 12.4解决多人开发路径可能重复问题460
    • 12.4.1错误的情况460
    • 12.4.2解决办法461
    • 12.5在控制层中处理指定的提交get或post方式463
    • 12.5.1控制层代码463
    • 12.5.2新建JSP文件并运行464
    • 12.6控制层重定向到控制层——无参数传递465
    • 12.6.1新建控制层Java文件465
    • 12.6.2创建JSP文件并运行项目466
    • 12.7控制层重定向到控制层——有参数传递467
    • 12.7.1创建两个控制层Java文件467
    • 12.7.2部署项目并运行468
    • 12.8匹配URL路径执行指定控制层468
    • 12.8.1新建控制层文件468
    • 12.8.2部署项目并运行469
    • 12.9在服务器端取得JSON字符串并解析——方式1470
    • 12.9.1在web.xml中配置字符编码过滤器470
    • 12.9.2新建JSP文件471
    • 12.9.3新建控制层Java文件472
    • 12.9.4添加依赖的jar包文件472
    • 12.9.5运行项目473
    • 12.10在服务器端取得JSON字符串并解析——方式2473
    • 12.10.1新建封装JSON对象属性的实体类473
    • 12.10.2新建控制层474
    • 12.10.3在配置文件中添加注解474
    • 12.10.4新建JSP文件475
    • 12.10.5添加jacksonJSON解析处理类库并运行475
    • 12.10.6解析不同格式的JSON字符串示例476
    • 12.11将URL中的参数转成实体的示例478
    • 12.11.1新建控制层文件478
    • 12.11.2新建登录用途的JSP文件479
    • 12.11.3在web.xml中注册编码过滤器479
    • 12.11.4运行结果479
    • 12.12在控制层返回JSON对象示例479
    • 12.12.1新建控制层文件480
    • 12.12.2新建JSP文件480
    • 12.12.3部署项目并运行481
    • 12.13在控制层传回JSON字符串示例482
    • 12.13.1新建控制层文件482
    • 12.13.2新建JSP文件及在配置文件中注册utf-8编码处理482
    • 12.13.3运行项目483
    • 12.14在控制层取得HttpServletRequest和HttpServletResponse对象483
    • 12.14.1新建控制层484
    • 12.14.2JSP文件中的EL代码及运行结果484
    • 12.14.3直接使用HttpServletResopnse对象输出响应字符484
    • 12.15通过URL参数访问指定的业务方法486
    • 12.15.1新建控制层文件List.java486
    • 12.15.2运行结果487
    • 12.16Spring 4 MVC单文件上传——写法1487
    • 12.16.1新建控制层487
    • 12.16.2在配置文件springMVC-servlet.xml中声明上传请求488
    • 12.16.3创建前台JSP文件489
    • 12.16.4程序运行结果489
    • 12.17Spring 4 MVC单文件上传——写法2489
    • 12.18Spring 4 MVC多文件上传490
    • 12.18.1新建控制层及JSP文件490
    • 12.18.2运行结果491
    • 12.19Spring 4 MVC支持中文文件名的文件下载491
    • 12.20控制层返回List对象及实体的效果493
    • 12.20.1新建控制层文件493
    • 12.20.2新建JSP文件493
    • 12.20.3更改springMVC-servlet.xml配置文件494
    • 12.20.4程序运行结果494
    • 12.21控制层ModelMap对象495
    • 12.21.1新建控制层495
    • 12.21.2JSP文件代码496
    • 12.21.3运行效果496
    • 12.22Spring 4 MVC提交的表单进行手动数据验证497
    • 12.22.1创建控制层文件497
    • 12.22.2创建JSP文件497
    • 12.22.3运行结果498
    • 第13章 Spring 4 MVC必备知识499
    • 13.1web.xml中的不同配置方法499
    • 13.1.1存放于src资源路径中499
    • 13.1.2指定存放路径500
    • 13.1.3指定多个配置文件500
    • 13.2路径中添加通配符的功能501
    • 13.3业务逻辑层在控制层中进行注入502
    • 13.3.1新建业务逻辑层502
    • 13.3.2创建控制层文件502
    • 13.3.3设计springMVC-servlet.xml配置文件502
    • 13.3.4程序运行结果503
    • 13.3.5多个实现类的情况503
    • 13.4对象ModelAndView的使用504
    • 13.4.1创建控制层及JSP文件504
    • 13.4.2程序运行结果505
    • 13.5控制层返回void数据的情况505
    • 13.5.1创建控制层及index.jsp文件505
    • 13.5.2更改配置文件506
    • 13.5.3部署项目运行程序506
    • 13.6使用Spring 4 MVC中的注解来操作HttpSession中的对象507
    • 13.6.1创建控制层文件PutGetSession.java507
    • 13.6.2创建显示不同作用域中的值的JSP文件508
    • 13.6.3部署项目并运行程序508
    • 第14章 Spring 4 MVC+MyBatis 3+Spring 4整合509
    • 14.1准备Spring 4的JAR包文件509
    • 14.2准备MyBatis的JAR包文件510
    • 14.3准备MyBatis 3与Spring 4整合的JAR文件510
    • 14.4创建Web项目510
    • 14.5配置web.xml文件511
    • 14.6配置springMVC-servlet.xml文件512
    • 14.7配置MyBatis配置文件513
    • 14.8创建MyBatis与映射有关文件513
    • 14.9配置applicationContext.xml文件514
    • 14.10创建Service对象516
    • 14.11创建Controller对象516
    • 14.12测试正常的效果517
    • 14.13测试回滚的效果517
       
    展开阅读
    精选笔记:Java源码解析之可重入锁ReentrantLock

    13小时15分钟前回答

    本文基于jdk1.8进行分析。

    ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock。

    首先看一下源码中对ReentrantLock的介绍。如下图。ReentrantLock是一个可重入的排他锁,它和synchronized的方法和代码有着相同的行为和语义,但有更多的功能。ReentrantLock是被最后一个成功lock锁并且还没有unlock的线程拥有着。如果锁没有被别的线程拥有,那么一个线程调用lock方法,就会成功获取锁并返回。如果当前线程已经拥有该锁,那么lock方法会立刻返回。这个可以通过isHeldByCurrentThread方法和getHoldCount方法进行验证。除了这部分介绍外,类前面的javadoc文档很长,就不在这里全部展开。随着后面介绍源码,会一一涉及到。

    /**
     * A reentrant mutual exclusion {@link Lock} with the same basic
     * behavior and semantics as the implicit monitor lock accessed using
     * {@code synchronized} methods and statements, but with extended
     * capabilities.
     * <p>A {@code ReentrantLock} is <em>owned</em> by the thread last
     * successfully locking, but not yet unlocking it. A thread invoking
     * {@code lock} will return, successfully acquiring the lock, when
     * the lock is not owned by another thread. The method will return
     * immediately if the current thread already owns the lock. This can
     * be checked using methods {@link #isHeldByCurrentThread}, and {@link
     * #getHoldCount}.

    首先看一下成员变量,如下图。ReentrantLock只有一个成员变量sync,即同步器,这个同步器提供所有的机制。Sync是AbstractQueuedSynchronizer的子类,同时,Sync有2个子类,NonfairSync和FairSync,分别是非公平锁和公平锁。Sync,NonfaireSync和FairSync的具体实现后面再讲。

      /** Synchronizer providing all implementation mechanics **/
      private final Sync sync;

    下面看一下构造函数。如下图。可以看到,ReentrantLock默认是非公平锁,它可以通过参数,指定初始化为公平锁或非公平锁。

      /**
       * Creates an instance of {@code ReentrantLock}.
       * This is equivalent to using {@code ReentrantLock(false)}.
       **/
      public ReentrantLock() {
        sync = new NonfairSync();
      }
      /**
       * Creates an instance of {@code ReentrantLock} with the
       * given fairness policy.
       * @param fair {@code true} if this lock should use a fair ordering policy
       **/
      public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
      }

    下面看一下ReentrantLock的主要方法。首先是lock方法。如下图。lock方法的实现很简单,就是调用Sync的lock方法。而Sync的lock方法是个抽象的,具体实现在NonfairSync和FairSync中。这里我们先不展开讲,而是先读一下lock方法的注释,看看它的作用。lock方法的作用是获取该锁。分为3种情况。

    1,如果锁没有被别的线程占有,那么当前线程就可以获取到锁并立刻返回,并把锁计数设置为1。

    2,如果当前线程已经占有该锁了,那么就会把锁计数加1,立刻返回。

    3,如果锁被另一个线程占有了,那么当前线程就无法再被线程调度,并且开始睡眠,直到获取到锁,在获取到到锁时,会把锁计数设置为1。

    lockInterruptibly方法与lock功能类似,但lockInterruptibly方法在等待的过程中,可以响应中断。

      /**
       * Acquires the lock.
       * <p>Acquires the lock if it is not held by another thread and returns
       * immediately, setting the lock hold count to one.
       * <p>If the current thread already holds the lock then the hold
       * count is incremented by one and the method returns immediately.
       * <p>If the lock is held by another thread then the
       * current thread becomes disabled for thread scheduling
       * purposes and lies dormant until the lock has been acquired,
       * at which time the lock hold count is set to one.
       **/
      public void lock() {
        sync.lock();
      }
      public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
      }

    下面,详细看一下非公平锁和公平锁中对lock函数的实现。如下图。下图同时列出了公平锁和非公平锁中lock的实现逻辑。从注释和代码逻辑中,都可以看出,非公平锁进行lock时,先尝试立刻闯入(抢占),如果成功,则获取到锁,如果失败,再执行通常的获取锁的行为,即acquire(1)。

        /**
         * 非公平锁中的lock
         * Performs lock. Try immediate barge, backing up to normal
         * acquire on failure.
         **/
        final void lock() {
          if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
          else
            acquire(1);
        }
        //公平锁中的lock
        final void lock() {
          acquire(1);
        }

    那么,我们首先了解下,非公平锁“尝试立刻闯入”,究竟做了什么。稍后再继续讲解通常的获取锁的行为。下图是立即闯入行为compareAndSetState(0, 1)的实现。从compareAndSetState函数的注释中,可以知道,如果同步状态值与期望值相等,那么就把它的值设置为updated值。否则同步状态值与期望值不相等,则返回false。这个操作和volatile有着相同的内存语义,也就是说,这个操作对其他线程是可见的。compareAndSetState函数注释里描述的功能,是通过unsafe.compareAndSwapInt方法实现的,而unsafe.compareAndSwapInt是一个native方法,是用c++实现的。那么继续追问,c++底层是怎么实现的?C++底层是通过CAS指令来实现的。什么是CAS指令呢?来自维基百科的解释是,CAS,比较和交换,Compare and Swap,是用用于实现多线程原子同步的指令。它将内存位置的内容和给定值比较,只有在相同的情况下,将该内存的值设置为新的给定值。这个操作是原子操作。那么继续追问,CAS指令的原子性,是如何实现的呢?我们都知道指令时CPU来执行的,在多CPU系统中,内存是共享的,内存和多个cpu都挂在总线上,当一个CPU执行CAS指令时,它会先将总线LOCK位点设置为高电平。如果别的CPU也要执行CAS执行,它会发现总线LOCK位点已经是高电平了,则无法执行CAS执行。CPU通过LOCK保证了指令的原子执行。

    现在来看一下非公平锁的lock行为,compareAndSetState(0, 1),它期望锁状态为0,即没有别的线程占用,并把新状态设置为1,即标记为占用状态。如果成功,则非公平锁成功抢到锁,之后setExclusiveOwnerThread,把自己设置为排他线程。非公平锁这小子太坏了。如果抢占失败,则执行与公平锁相同的操作。

      /**
       * Atomically sets synchronization state to the given updated
       * value if the current state value equals the expected value.
       * This operation has memory semantics of a {@code volatile} read
       * and write.
       * @param expect the expected value
       * @param update the new value
       * @return {@code true} if successful. False return indicates that the actual
       *     value was not equal to the expected value.
       **/
      protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
      }
      public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    下面看一下公平锁获取锁时的行为。如下图。这部分的逻辑有些多,请阅读代码中的注释进行理解。

      /**
       * 公平锁的lock
       **/
      final void lock() {
        acquire(1);
      }
      /**
       * Acquires in exclusive mode, ignoring interrupts. Implemented
       * by invoking at least once {@link #tryAcquire},
       * returning on success. Otherwise the thread is queued, possibly
       * repeatedly blocking and unblocking, invoking {@link
       * #tryAcquire} until success. This method can be used
       * to implement method {@link Lock#lock}.
       * @param arg the acquire argument. This value is conveyed to
       *    {@link #tryAcquire} but is otherwise uninterpreted and
       *    can represent anything you like.
       **/
      public final void acquire(int arg) {
        /**
         * acquire首先进行tryAcquire()操作。如果tryAcquire()成功时则获取到锁,即刻返回。
         * 如果tryAcquire()false时,会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
         * 操作。如果acquireQueued(addWaiter(Node.EXCLUSIVE), arg)true时,则当前线程中断自己。
         * 如果acquireQueued(addWaiter(Node.EXCLUSIVE), arg)false,则返回。
         * 其中tryAcquire()操作在NonfairSync中和FairSync中实现又有所区别。
         **/
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
          selfInterrupt();
      }
      /**
       * NonfairSync中的tryAcquire。
       * @param acquires
       * @return
       **/
      protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
      }
      /**
       * Performs non-fair tryLock. tryAcquire is implemented in
       * subclasses, but both need nonfair try for trylock method.
       **/
      final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        //首先获取当前同步状态值
        int c = getState();
        if (c == 0) {
          //c为0,表示目前没有线程占用锁。没有线程占用锁时,当前线程尝试抢锁,如果抢锁成功,则返回true。
          if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
          }
        }
        else if (current == getExclusiveOwnerThread()) {
          //c不等于0时表示锁被线程占用。如果是当前线程占用了,则将锁计数加上acquires,并返回true。
          int nextc = c + acquires;
          if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
          setState(nextc);
          return true;
        }
        //以上情况都不是时,返回false,表示非公平抢锁失败。
        return false;
      }
      /**
       * Fair version of tryAcquire. Don't grant access unless
       * recursive call or no waiters or is first.
       * 这个是公平版本的tryAcquire
       **/
      protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
          //c=0时表示锁未被占用。这里是先判断队列中前面是否有别的线程。没有别的线程时,才进行CAS操作。
          //公平锁之所以公平,正是因为这里。它发现锁未被占用时,首先判断等待队列中是否有别的线程已经在等待了。
          //而非公平锁,发现锁未被占用时,根本不管队列中的排队情况,上来就抢。
          if (!hasQueuedPredecessors() &&
              compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
          }
        }
        else if (current == getExclusiveOwnerThread()) {
          int nextc = c + acquires;
          if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
          setState(nextc);
          return true;
        }
        return false;
      }
      /**
       * Acquires in exclusive uninterruptible mode for thread already in
       * queue. Used by condition wait methods as well as acquire.
       * 当抢锁失败时,先执行addWaiter(Node.EXCLUSIVE),将当前线程加入等待队列,再执行该方法。
       * 该方法的作用是中断当前线程,并进行检查,知道当前线程是队列中的第一个线程,并且抢锁成功时,
       * 该方法返回。
       * @param node the node
       * @param arg the acquire argument
       * @return {@code true} if interrupted while waiting
       **/
      final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
          boolean interrupted = false;
          for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
              setHead(node);
              p.next = null; // help GC
              failed = false;
              return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
              interrupted = true;
          }
        } finally {
          if (failed)
            cancelAcquire(node);
        }
      }

    接下来是tryLock方法。代码如下。从注释中我们可以理解到,只有当调用tryLock时锁没有被别的线程占用,tryLock才会获取锁。如果锁没有被另一个线程占用,那么就获取锁,并立刻返回true,并把锁计数设置为1. 甚至在锁被设置为公平排序的情况下,若果锁可用,调用tryLock会立刻获取锁,而不管有没有别的线程在等待锁了。从这里我们总结出,不管可重入锁是公平锁还是非公平锁,tryLock方法只会是非公平的。

    /**
       * Acquires the lock only if it is not held by another thread at the time
       * of invocation.
       * <p>Acquires the lock if it is not held by another thread and
       * returns immediately with the value {@code true}, setting the
       * lock hold count to one. Even when this lock has been set to use a
       * fair ordering policy, a call to {@code tryLock()} <em>will</em>
       * immediately acquire the lock if it is available, whether or not
       * other threads are currently waiting for the lock.
       * This "barging" behavior can be useful in certain
       * circumstances, even though it breaks fairness. If you want to honor
       * the fairness setting for this lock, then use
       * {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) }
       * which is almost equivalent (it also detects interruption).
       * <p>If the current thread already holds this lock then the hold
       * count is incremented by one and the method returns {@code true}.
       * <p>If the lock is held by another thread then this method will return
       * immediately with the value {@code false}.
       * @return {@code true} if the lock was free and was acquired by the
       *     current thread, or the lock was already held by the current
       *     thread; and {@code false} otherwise
       **/
      public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
      }
      public boolean tryLock(long timeout, TimeUnit unit)
          throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
      }

    接下来是释放锁的方法unlock。代码如下。unlock方式的实现,是以参数1来调用sync.release方法。而release方法是如何实现的呢?release方法首先会调用tryRelease方法,如果tryRelease成功,则唤醒后继者线程。而tryRelease的实现过程十分清晰,首先获取锁状态,锁状态减去参数(放锁次数),得到新状态。然后判断持有锁的线程是否为当前线程,如果不是当前线程,则抛出IllegalMonitorStateException。然后判断,如果新状态为0,说明放锁成功,则把持有锁的线程设置为null,并返回true。如果新状态不为0,则返回false。从tryRelease的返回值来看,它返回的true或false,指的是否成功的释放了该锁。成功的释放该锁的意思是彻底释放锁,别的线程就可以获取锁了。这里要认识到,即便tryRelease返回false,它也只是说明了锁没有完全释放,本次调用的这个释放次数值,依然是释放成功的。

    
      /**
       * Attempts to release this lock.
       * <p>If the current thread is the holder of this lock then the hold
       * count is decremented. If the hold count is now zero then the lock
       * is released. If the current thread is not the holder of this
       * lock then {@link IllegalMonitorStateException} is thrown.
       * @throws IllegalMonitorStateException if the current thread does not
       *     hold this lock
       **/
      public void unlock() {
        sync.release(1);
      }
      /**
       * Releases in exclusive mode. Implemented by unblocking one or
       * more threads if {@link #tryRelease} returns true.
       * This method can be used to implement method {@link Lock#unlock}.
       * @param arg the release argument. This value is conveyed to
       *    {@link #tryRelease} but is otherwise uninterpreted and
       *    can represent anything you like.
       * @return the value returned from {@link #tryRelease}
       **/
      public final boolean release(int arg) {
        if (tryRelease(arg)) {
          Node h = head;
          if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
          return true;
        }
        return false;
      }
      protected final boolean tryRelease(int releases) {
          int c = getState() - releases;
          if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
          boolean free = false;
          if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
          }
          setState(c);
          return free;
        }
      /**
       * Wakes up node's successor, if one exists.
       * @param node the node
       **/
      private void unparkSuccessor(Node node) {
        /**
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling. It is OK if this
         * fails or if status is changed by waiting thread.
         **/
        int ws = node.waitStatus;
        if (ws < 0)
          compareAndSetWaitStatus(node, ws, 0);
        /**
         * Thread to unpark is held in successor, which is normally
         * just the next node. But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         **/
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
          s = null;
          for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
              s = t;
        }
        if (s != null)
          LockSupport.unpark(s.thread);
      }

    接下来是newCondition方法。关于Condition这里不展开介绍,只是了解下该方法的作用。如下图。该方法返回一个和这个锁实例一起使用的Condition实例。返回的Condition实例支持和Object的监控方法例如wait-notify和notifyAll相同的用法。

    • 1,如果没有获取锁,调用Condition的await,signal,signalAll方法的任何一个时,会抛出IllegalMonitorStateException异常。
    • 2,调用Condition的await方法时,锁也会释放,在await返回之前,锁会被重新获取,并且锁计数会恢复到调用await方法时的值。
    • 3,如果一个线程在等待的过程中被中断了,那么等待就会结束,并抛出InterruptedException异常,线程的中断标志位会被清理。
    • 4,等待的线程以FIFO的顺序被唤醒。
    • 5,从await方法返回的线程们的获取到锁的顺序,和线程最开始获取锁的顺序相同,这是未指定情况下的默认实现。但是,公平锁更钟爱那些已经等待了最长时间的线程。
      /**
       * Returns a {@link Condition} instance for use with this
       * {@link Lock} instance.
       * <p>The returned {@link Condition} instance supports the same
       * usages as do the {@link Object} monitor methods ({@link
       * Object#wait() wait}, {@link Object#notify notify}, and {@link
       * Object#notifyAll notifyAll}) when used with the built-in
       * monitor lock.
       * <ul>
       * <li>If this lock is not held when any of the {@link Condition}
       * {@linkplain Condition#await() waiting} or {@linkplain
       * Condition#signal signalling} methods are called, then an {@link
       * IllegalMonitorStateException} is thrown.
       * <li>When the condition {@linkplain Condition#await() waiting}
       * methods are called the lock is released and, before they
       * return, the lock is reacquired and the lock hold count restored
       * to what it was when the method was called.
       * <li>If a thread is {@linkplain Thread#interrupt interrupted}
       * while waiting then the wait will terminate, an {@link
       * InterruptedException} will be thrown, and the thread's
       * interrupted status will be cleared.
       * <li> Waiting threads are signalled in FIFO order.
       * <li>The ordering of lock reacquisition for threads returning
       * from waiting methods is the same as for threads initially
       * acquiring the lock, which is in the default case not specified,
       * but for <em>fair</em> locks favors those threads that have been
       * waiting the longest.
       * </ul>
       * @return the Condition object
       **/
      public Condition newCondition() {
        return sync.newCondition();
      }

    可重入锁还有一些其他的方法,这里就不一一介绍了。This is the end.

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对码农之家的支持。如果你想了解更多相关内容请查看下面相关链接

    展开阅读

    资源下载

    相关资源

    • Java面试手册(350道面试题)

      Java面试手册(350道面试题)

      如何就业?如何找到一份满意的工作?如何升职加薪?这些一直是我们java程序员老生常谈的问题!我们从企业的招聘需求已经面试题来分析一下,当下java程序员应该选择往那方面进阶 收集100家知名企业近年来java面试题合集总汇! 你要的这里都有;企业要的这里也有;面向底层、架构,单刀直入!这些全部是题目跟类目,每个题目都会有相对的答案。350道java面试题收集整理,包含了上百家企业近年的人才计划,需要的朋友可下载试试! 目录 一、性能

      大小:2.4 MBJava面试

      立即下载
    • Java EE企业级应用开发教程

      Java EE企业级应用开发教程

      这本书详细讲解了Java EE中Spring、Spring MVC和MyBatis三大框架,采用了大量案例,帮助读者学习和理解SSM的核心技术,是一本J2EE程序设计的轻量级开发教程

      大小:127.1 MBJava开发

      立即下载
    • HTML5+CSS3+JavaScript 前端开发基础

      HTML5+CSS3+JavaScript 前端开发基础

      大小:510 MB前端开发

      立即下载
    • HTML CSS JavaScript网页制作从入门到精通

      HTML CSS JavaScript网页制作从入门到精通

      HTML CSS JavaScript网页制作从入门到精通 第3版 共分为19章和4个附录,重点介绍使用HTML进行网页制作的方方面面,同时讲解了目前流行的Web标准与CSS网页布局实例,以及基于JavaScript语言的网页特

      大小:65.2 MB网页制作

      立即下载
    • Java List提取某一列、分组排序操作

      Java List提取某一列、分组排序操作

      【实例截图】 【核心代码】 package com.llf.list;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.stream.Collectors;/*** @Author llf* @Date 2019/11/13 14:36**/public class Test {public static void main(String[] args) {ListPerson pe

      大小:0.1 MBJava

      立即下载

    学习笔记

    21小时47分钟前回答

    JAVA HashSet和TreeSet 保证存入元素不会重复的操作

    Set是一种数据集合。它与List同样继承与Collection接口。 它与Collection接口中的方法基本一致,并没有对Collection接口进行功能进行功能上的扩充,只是比Collection接口更严格了。与List不同的是,Set中的元素是无无需的,并且都以某种规则保证存入的元素不会出现重复。 它的特点也就是: 1. 元素不会出现重复。 2. 元素是无序的。(存取无序) 3. 元素可以为空。 每种类型的Set所使用的避免元素重复的规则都是不同的,今天我们主要还是看HashSet和TreeSet: 第一种是HashSet: HashSet 我们先来看看HashSet的构造器是怎么样的: static final long serialVersionUID = -5024744406713321676L; private transient HashMapE,Object map; // Dummy value to……

    23小时17分钟前回答

    Java中EasyPoi多sheet导出功能实现

    EasyPoi 多sheet导出 序言:之前一直想开始写博客,都没有时间行动起来,今天终于开始了我的第一篇博客… 最近接到一个导出excel功能的需求,该功能主要难点是 多sheet页 导出合并单元格(跨行、跨列) 多表头合并 我开始的想法是如果采用poi来实现这个功能,业务逻辑可能会有点复杂,于是我使用了easyPoi——一个so easy的工具,它的特点就是非常方便,用jQuery的一句来说就是:write Less,Do More。 话不多说,接下来分享一下我的代码(我使用的是SSH框架搭建环境) 一、引入maven jar包 dependency groupIdcn.afterturn/groupId artifactIdeasypoi-base/artifactId version3.2.0/version/dependencydependency groupIdcn.afterturn/groupId artifactIdeasyp……

    17小时8分钟前回答

    java中treemap和treeset实现红黑树

    TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。 TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 TreeSet 之间的关系,下面先看 TreeSet 类的部分源代码: public class TreeSetE extends AbstractSetE implements NavigableSetE, Cloneable, java.io.Serializable { // 使用 NavigableMap 的 key 来保存 Set 集合的元素 private transient NavigableMapE,Object m; // 使用一个 PRESENT 作为 Map 集合的所有 value。 private static final Object PRESENT = new Object(); // 包访问权限的构造器,以指定的 NavigableMap 对象创建 Set 集合 TreeSet(NavigableMapE,Object m) { this.m = m; } public TreeSet() // ① { // 以自然排序方式创建一个新……