├── 00-01.md ├── img ├── 00-03 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ └── 04.png ├── 00-04 │ ├── 01.png │ └── 02.png ├── 00-06 │ ├── 01.png │ └── 02.png ├── 00-07 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ └── 04.png ├── 01-02 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ └── 06.png ├── 01-03 │ ├── 01.png │ └── 02.png ├── 01-04 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ ├── 06.png │ ├── 07.png │ ├── 08.png │ └── 09.png ├── 01-06 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ ├── 06.png │ ├── 07.png │ ├── 08.png │ └── 09.png ├── 01-07 │ ├── 01.png │ ├── 02.png │ └── 03.png └── 01-09 │ ├── 01.png │ ├── 02.png │ ├── 03.png │ └── 04.png ├── 01-01.md ├── 00-00.md ├── temp.md ├── 01-05.md ├── 01-00.md ├── 01-08.md ├── 01-10.md ├── 00-08.md ├── 00-05.md ├── README.md ├── supplement.md ├── 00-02.md ├── 01-07.md ├── 00-06.md ├── 00-03.md ├── 01-09.md ├── 01-04.md ├── 00-07.md ├── 01-03.md ├── 00-04.md ├── 01-06.md └── 01-02.md /00-01.md: -------------------------------------------------------------------------------- 1 | 00-01、相关软件的安装 2 | --- 3 | 4 | jdk 1.8 5 | netbeans 8.0 6 | tomcat 8.0 7 | -------------------------------------------------------------------------------- /img/00-03/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-03/01.png -------------------------------------------------------------------------------- /img/00-03/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-03/02.png -------------------------------------------------------------------------------- /img/00-03/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-03/03.png -------------------------------------------------------------------------------- /img/00-03/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-03/04.png -------------------------------------------------------------------------------- /img/00-04/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-04/01.png -------------------------------------------------------------------------------- /img/00-04/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-04/02.png -------------------------------------------------------------------------------- /img/00-06/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-06/01.png -------------------------------------------------------------------------------- /img/00-06/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-06/02.png -------------------------------------------------------------------------------- /img/00-07/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-07/01.png -------------------------------------------------------------------------------- /img/00-07/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-07/02.png -------------------------------------------------------------------------------- /img/00-07/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-07/03.png -------------------------------------------------------------------------------- /img/00-07/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/00-07/04.png -------------------------------------------------------------------------------- /img/01-02/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/01.png -------------------------------------------------------------------------------- /img/01-02/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/02.png -------------------------------------------------------------------------------- /img/01-02/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/03.png -------------------------------------------------------------------------------- /img/01-02/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/04.png -------------------------------------------------------------------------------- /img/01-02/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/05.png -------------------------------------------------------------------------------- /img/01-02/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-02/06.png -------------------------------------------------------------------------------- /img/01-03/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-03/01.png -------------------------------------------------------------------------------- /img/01-03/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-03/02.png -------------------------------------------------------------------------------- /img/01-04/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/01.png -------------------------------------------------------------------------------- /img/01-04/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/02.png -------------------------------------------------------------------------------- /img/01-04/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/03.png -------------------------------------------------------------------------------- /img/01-04/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/04.png -------------------------------------------------------------------------------- /img/01-04/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/05.png -------------------------------------------------------------------------------- /img/01-04/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/06.png -------------------------------------------------------------------------------- /img/01-04/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/07.png -------------------------------------------------------------------------------- /img/01-04/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/08.png -------------------------------------------------------------------------------- /img/01-04/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-04/09.png -------------------------------------------------------------------------------- /img/01-06/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/01.png -------------------------------------------------------------------------------- /img/01-06/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/02.png -------------------------------------------------------------------------------- /img/01-06/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/03.png -------------------------------------------------------------------------------- /img/01-06/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/04.png -------------------------------------------------------------------------------- /img/01-06/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/05.png -------------------------------------------------------------------------------- /img/01-06/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/06.png -------------------------------------------------------------------------------- /img/01-06/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/07.png -------------------------------------------------------------------------------- /img/01-06/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/08.png -------------------------------------------------------------------------------- /img/01-06/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-06/09.png -------------------------------------------------------------------------------- /img/01-07/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-07/01.png -------------------------------------------------------------------------------- /img/01-07/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-07/02.png -------------------------------------------------------------------------------- /img/01-07/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-07/03.png -------------------------------------------------------------------------------- /img/01-09/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-09/01.png -------------------------------------------------------------------------------- /img/01-09/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-09/02.png -------------------------------------------------------------------------------- /img/01-09/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-09/03.png -------------------------------------------------------------------------------- /img/01-09/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letiantian/another-tutorial-about-java-web/HEAD/img/01-09/04.png -------------------------------------------------------------------------------- /01-01.md: -------------------------------------------------------------------------------- 1 | 01-01、Spring与面向切面编程 2 | --- 3 | 4 | 面向切面编程是一种编程模式。 5 | 6 | 使用动态代理可以实现面向切面编程。 7 | 8 | google:`java 设计模式 代理`、`java 动态代理`、`Spring AOP`、`Spring 面向切面`。 9 | -------------------------------------------------------------------------------- /00-00.md: -------------------------------------------------------------------------------- 1 | 00-00、序 2 | --- 3 | 4 | 本文会穿插大量的链接和引用。 5 | 6 | 7 | 8 | 建议先对Java、HTML、CSS、JS、ajax、HTTP、Firebug等有所了解。 9 | 10 | 推荐一下“菜鸟教程”([http://www.runoob.com/](http://www.runoob.com/))中的相关教程(利 11 | 益无关~),很适合入门。 12 | 13 | 14 | linux下编写程序。 15 | 文件均是UTF-8编码。 16 | -------------------------------------------------------------------------------- /temp.md: -------------------------------------------------------------------------------- 1 | ```shell 2 | echo "# another-tutorial-about-java-web" >> README.md 3 | git init 4 | git add README.md 5 | git commit -m "first commit" 6 | git remote add origin https://github.com/someus/another-tutorial-about-java-web.git 7 | git push -u origin master 8 | ``` 9 | -------------------------------------------------------------------------------- /01-05.md: -------------------------------------------------------------------------------- 1 | 01-05、JSON 2 | --- 3 | 4 | ## 如何响应JSON数据 5 | 6 | [Spring 3 MVC and JSON example](http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example/) 7 | [Spring MVC – Easy REST-Based JSON Services with @ResponseBody](http://codetutr.com/2013/04/09/spring-mvc-easy-rest-based-json-services-with-responsebody/) 8 | 9 | ## 如何处理HTTP请求中的JSON数据 10 | [How to pass Json object from ajax to spring mvc controller?](http://stackoverflow.com/questions/20245544/how-to-pass-json-object-from-ajax-to-spring-mvc-controller) 11 | -------------------------------------------------------------------------------- /01-00.md: -------------------------------------------------------------------------------- 1 | 01-00、Spring与依赖注入 2 | --- 3 | 4 | 5 | 依赖注入是反转控制的一种。 6 | 7 | 8 | ## 什么是反转控制? 9 | 10 | 我们平常写程序,需要什么对象,就在代码里显式地new一个出来然后使用,这是我们自己去控制对象的生成。 11 | 而反转控制是让Spring(或者类似的其他工具)帮忙去生成我们需要的对象,也就是说对象的生成的控制权交给Spring了。 12 | 13 | 当然,Spring需要依据一定的规则去生成对象,这个规则就在我们写的xml配置文件、或者代码中添加的注解之中。 14 | 换句话说,我们不要生成对象,但是要去写配置。 15 | 16 | 据说,反转控制可用于解耦。这个在小型的项目中很难看出来,项目越大越能感受得到。(我是没写过这方面的大的项目,想着xml配置就头疼) 17 | 18 | 反转控制的实现中应用了大量的反射。 19 | 20 | ## 依赖注入 21 | 22 | 声明依赖关系,Spring将对象A需要的对象B注入到对象A中。 23 | 24 | 25 | ## 建议阅读 26 | 27 | google `Spring 依赖注入`。 28 | -------------------------------------------------------------------------------- /01-08.md: -------------------------------------------------------------------------------- 1 | 01-08、拦截器 2 | --- 3 | 拦截器(interceptor),类似servlet中的过滤器。 4 | 5 | 6 | [Spring 3 MVC Interceptor tutorial with example](http://viralpatel.net/blogs/spring-mvc-interceptor-example/) 7 | [Spring MVC Interceptors Example – HandlerInterceptor and HandlerInterceptorAdapter](http://www.journaldev.com/2676/spring-mvc-interceptors-example-handlerinterceptor-and-handlerinterceptoradapter) 8 | [Spring MVC handler interceptors example](http://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/) 9 | [Spring MVC Handler Interceptor](http://javapapers.com/spring/spring-mvc-handler-interceptor/) 10 | 11 | 12 | 暂不支持注解。[Is it possible to wire a Spring MVC Interceptor using annotations?](http://stackoverflow.com/questions/4389224/is-it-possible-to-wire-a-spring-mvc-interceptor-using-annotations) 13 | -------------------------------------------------------------------------------- /01-10.md: -------------------------------------------------------------------------------- 1 | 01-10、转换器与格式化 2 | --- 3 | 转换器:converter 4 | 格式化:Format 5 | 6 | 7 | 在[01-06、校验器](./01-06.md)、[01-09、文件上传](./01-09.md)中的例子中都使用了数据绑定 8 | (将表单数据绑定到bean对象中),例如: 9 | 10 | ```java 11 | @RequestMapping(value = "/output") 12 | public String output(Person person, Model model) { 13 | model.addAttribute("person", person); 14 | return "hello/output"; 15 | } 16 | ``` 17 | 表单的数据都是String类型,如果我们的bean类中的属性是其他类型,例如Date、int,这时候就需要写一个工具,将String转换成Date、int。 18 | 这就是转换器与格式化做的事情:类型转换。字符串转换成数字类型是内置的。 19 | 20 | **资料:** 21 | [8. Validation, Data Binding, and Type Conversion](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/validation.html) 22 | [Spring MVC request parameter conversion with minimal configuration](http://unitstep.net/blog/2013/04/07/spring-mvc-request-parameter-conversion-with-minimal-configuration/) 23 | [Introduction to Spring Converters and Formatters](http://www.javabeat.net/introduction-to-spring-converters-and-formatters/) 24 | -------------------------------------------------------------------------------- /00-08.md: -------------------------------------------------------------------------------- 1 | 00-08、Tomcat的运行机制 2 | --- 3 | 4 | 在[00-02、理解HTTP](./00-02.md)中给出了一个简单的服务器代码,Tomcat的设计思路也是类似的。 5 | 6 | Tomcat是一个servlet容器。 7 | 8 | [http://www.kaifajie.cn/tomcat6/7454.html](http://www.kaifajie.cn/tomcat6/7454.html)中的内容值得参考: 9 | > 先不去关技术细节,对一个servlet容器,我觉得它首先要做以下事情: 10 | 1:实现Servlet api规范。这是最基础的一个实现,servlet api大部分都是接口规范。如request、response、session、cookie。为了我们应用端能正常使用,容器必须有一套完整实现。 11 | 2:启动Socket监听端口,等待http请求。 12 | 3:获取http请求,分发请求给不同的协议处理器,如http和https在处理上是不一样的。 13 | 4:封装请求,构造HttpServletRequest。把socket获取的用户请求字节流转换成java对象httprequest。构造httpResponse。 14 | 5:调用(若未创建,则先加载)servlet,调用init初始化,执行servlet.service()方法。 15 | 6:为httpResponse添加header等头部信息。 16 | 7:socket回写流,返回满足http协议格式的数据给浏览器。 17 | 8:实现JSP语法分析器,JSP标记解释器。JSP servlet实现和渲染引擎。 18 | 9:JNDI、JMX等服务实现。容器一般额外提供命名空间服务管理。 19 | 10:线程池管理,创建线程池,并为每个请求分配线程。 20 | 21 | Tomcat有自己的类加载机制。可以参考: 22 | [Java类加载原理解析](http://www.blogjava.net/zhuxing/archive/2008/08/08/220841.html) 23 | [深入探讨 Java 类加载器](http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html) 24 | [Tomcat类加载器体系结构](http://www.cnblogs.com/ivan-zheng/articles/1789682.html) 25 | 26 | 27 | 有一本书是讲Tomcat如何运行的:[《How Tomcat Works》](http://book.douban.com/subject/1943128/)。 28 | -------------------------------------------------------------------------------- /00-05.md: -------------------------------------------------------------------------------- 1 | 00-05、过滤器与监听器 2 | --- 3 | 4 | ## 过滤器 5 | 过滤器(Filter),并非必须,但很实用。 6 | 7 | 过滤器是一种设计模式,主要用来封装Servlet中一些通用的代码。在web.xml中配置哪些URL对应哪些过滤器。 8 | 9 | 一个过滤器的写法如下: 10 | ```java 11 | public void doFilter(ServletRequest request , ServletResponse response , FilterChain chain) { 12 | //处理 request 13 | chain.doFilter(request, response); 14 | //处理 response 15 | } 16 | ``` 17 | 18 | 假设针对一URL定义了3个过滤器,分别是MyFilter1、MyFilter2、MyFilter3,在web.xml中也是按照这个顺序设置的, 19 | 那么过滤器和Servlet的执行顺序如下: 20 | 21 | * MyFilter1中处理request的代码; 22 | * MyFilter2中处理request的代码; 23 | * MyFilter3中处理request的代码; 24 | * 相应的Servlet; 25 | * MyFilter3中处理response的代码; 26 | * MyFilter2中处理response的代码; 27 | * MyFilter1中处理response的代码; 28 | 29 | 之所以能达到这样的效果,`chain.doFilter(request, response);`起到了很大的作用。 30 | 值得注意的是,如果每个Filter没有到达`chain.doFilter`就返回了,那么后续的Filter或者Servlet也就不会执行。 31 | 32 | **相关资料:** 33 | [Tomcat 的过滤诀窍](http://www.ibm.com/developerworks/cn/java/j-tomcat/) 34 | [三个有用的过滤器](http://www.iteye.com/topic/185094) 35 | [Java Web笔记 – Servlet中的Filter过滤器的介绍和使用 编写过滤器](http://www.itzhai.com/java-web-notes-servlet-filters-in-the-filter-writing-the-introduction-and-use-of-filters.html#read-more) 36 | [Intercepting HTTP Response using Servlet Filter](https://punekaramit.wordpress.com/2010/03/16/intercepting-http-response-using-servlet-filter/) 37 | [How to write response filter?](http://stackoverflow.com/questions/5634477/how-to-write-response-filter) 38 | [责任链模式](http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html) 39 | 40 | ## 监听器 41 | 当某个事件发生时候,监听器里的方法会被调用。例如Tomcat容器启动时、销毁时,session创建时、销毁时。 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 浅入浅出Java Web 2 | 3 | 4 | ## JSP & Servlet 5 | 6 | [00-00、序](./00-00.md) 7 | [00-01、相关软件的安装](./00-01.md) 8 | [00-02、理解HTTP](./00-02.md) 9 | [00-03、从JSP开始](./00-03.md) 10 | [00-04、理解Servlet](./00-04.md) 11 | [00-05、过滤器与监听器](./00-05.md) 12 | [00-06、使用velocity模板引擎](./00-06.md) 13 | [00-07、使用数据库连接池](./00-07.md) 14 | [00-08、Tomcat的运行机制](./00-08.md) 15 | 16 | ## Spring MVC 17 | 18 | 使用Spring 4.0 。 19 | 20 | [01-00、Spring与依赖注入](./01-00.md) 21 | [01-01、Spring与面向切面编程](./01-01.md) 22 | [01-02、使用Spring MVC构建Hello World](./01-02.md) 23 | [01-03、JdbcTemplate](./01-03.md) 24 | [01-04、基于注解的URL映射](./01-04.md) 25 | [01-05、JSON](./01-05.md) 26 | [01-06、校验器](./01-06.md) 27 | [01-07、国际化](./01-07.md) 28 | [01-08、拦截器](./01-08.md) 29 | [01-09、文件上传](./01-09.md) 30 | [01-10、转换器与格式化](./01-10.md) 31 | 32 | 33 | ## Book 34 | [Spring MVC学习指南](http://book.douban.com/subject/26411275/) 35 | [JSP大学实用教程](http://book.douban.com/subject/7052099/) 36 | [Spring MVC Beginner's Guide](https://www.packtpub.com/application-development/spring-mvc-beginner%E2%80%99s-guide) 37 | [轻量级Java EE企业应用实战](http://book.douban.com/subject/10582495/) 38 | 39 | ## Online Tutorial 40 | [JSP教程 - 菜鸟教程](http://www.runoob.com/jsp/jsp-tutorial.html) 41 | [Servlet教程 - 菜鸟教程](http://www.runoob.com/servlet/servlet-tutorial.html) 42 | [Spring官方文档:Spring Framework Reference Documentation](http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/index.html) 43 | [Spring MVC官方文档:Introduction to Spring Web MVC framework](http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html) 44 | [跟开涛学spring mvc](http://sishuok.com/forum/blogCategory/showByCategory.html?categories_id=101&user_id=2) 45 | 46 | ## Q & A 47 | [stackoverflow](http://stackoverflow.com/) 48 | [开源中国社区-问答](http://www.oschina.net/question) 49 | 50 | ## Learn More 51 | [Jetty](http://www.eclipse.org/jetty/) 52 | [Netty](http://netty.io/) 53 | [JUnit](http://junit.org/) 54 | [Maven](https://maven.apache.org/) 55 | [Gradle](http://gradle.org/) 56 | [MyBatis](http://mybatis.github.io/) 57 | [Hibernate](http://hibernate.org/) 58 | [Akka](http://akka.io/) 59 | 60 | **JUnit:** 61 | [慕课网:JUnit](http://www.imooc.com/learn/356) 62 | 63 | **Maven:** 64 | [慕课网:Maven](http://www.imooc.com/learn/443) 65 | 66 | **Gradle:** 67 | [Gradle User Guide](https://docs.gradle.org/current/userguide/userguide.html) 68 | [Gradle入门系列](http://blog.jobbole.com/71999/) 69 | [使用Gradle构建Java Web应用](http://www.blogjava.net/jiangshachina/archive/2014/01/23/409285.html) 70 | [Simple Gradle Web Application](http://codetutr.com/2013/03/23/simple-gradle-web-application/) 71 | 72 | 73 | ## Supplement 74 | [补充](./supplement.md) 75 | -------------------------------------------------------------------------------- /supplement.md: -------------------------------------------------------------------------------- 1 | 补充 2 | --- 3 | 4 | ## 使用redis存储session 5 | [tomcat-redis-session-manager](https://github.com/jcoleman/tomcat-redis-session-manager) 6 | 7 | ## Tomcat与HTTP 1.1 8 | [Why tomcat reply HTTP 1.1 respose with an HTTP 1.0 request?](http://stackoverflow.com/questions/19461312/why-tomcat-reply-http-1-1-respose-with-an-http-1-0-request) 9 | 10 | ## 视图技术 11 | [View technologies](http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/view.html) 12 | 13 | ## JSTL 14 | [JSP - Standard Tag Library (JSTL) Tutorial](http://www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm) 15 | 16 | ## Spring WebSocket 17 | [Using Spring 4 WebSocket, sockJS and Stomp support to implement two way server client communication](https://raymondhlee.wordpress.com/2014/01/19/using-spring-4-websocket-sockjs-and-stomp-support-to-implement-two-way-server-client-communication/) 18 | [Spring WebSocket Support](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html) 19 | [Using WebSocket to build an interactive web application](https://spring.io/guides/gs/messaging-stomp-websocket/) 20 | 21 | ## Jetty WebSocket 22 | [Jetty WebSocket Server API](http://www.eclipse.org/jetty/documentation/current/jetty-websocket-server-api.html) 23 | [Jetty 9 – Updated WebSocket API](https://webtide.com/jetty-9-updated-websocket-api/) 24 | 25 | ## Servlet、Spring异步 26 | 个人理解: 这里的异步其实就是servlet把请求交给一个线程池去处理,然后servlet线程结束。线程池慢慢地去处理这些任务。 27 | 如此,可以及时地释放servlet线程,防止线程数量太多造成性能下降。 28 | [How to use Asynchronous Servlets to improve performance](https://plumbr.eu/blog/java/how-to-use-asynchronous-servlets-to-improve-performance) 29 | [Asynchronous processing support in Servlet 3.0](http://www.javaworld.com/article/2077995/java-concurrency/asynchronous-processing-support-in-servlet-3-0.html) 30 | [Servlet 3.0 实战:异步 Servlet 与 Comet 风格应用程序](http://www.ibm.com/developerworks/cn/java/j-lo-comet/index.html) 31 | [Async Servlet Feature of Servlet 3](http://www.javacodegeeks.com/2013/08/async-servlet-feature-of-servlet-3.html) 32 | 33 | [使用spring的@Async异步执行方法](http://www.cnblogs.com/yangzhilong/p/3725071.html) 34 | [spring mvc对异步请求的处理](http://www.cnblogs.com/yangzhilong/p/3725128.html) 35 | 36 | ## 安全 37 | [jstl转义显示html标签](http://blog.csdn.net/stone5751/article/details/6579728) 38 | [XSS prevention in JSP/Servlet web application](http://stackoverflow.com/questions/2658922/xss-prevention-in-jsp-servlet-web-application) 39 | [Simple Cross Site Scripting (XSS) Servlet Filter](http://greatwebguy.com/programming/java/simple-cross-site-scripting-xss-servlet-filter/) 40 | [Spring MVC防御CSRF、XSS和SQL注入攻击](http://www.cnblogs.com/Mainz/archive/2012/11/01/2749874.html) 41 | [Spring Security 3.2.0.RC1 Highlights: CSRF Protection](http://spring.io/blog/2013/08/21/spring-security-3-2-0-rc1-highlights-csrf-protection/) 42 | 43 | ## 缓存Ehcache 44 | [Java Web Application: How to implement caching techniques?](http://stackoverflow.com/questions/699996/java-web-application-how-to-implement-caching-techniques) 45 | [Ehcache文档](http://www.ehcache.org/documentation/) 46 | [注释驱动的 Spring cache 缓存介绍](http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/) 47 | 48 | 49 | ## war格式 50 | [How to deploy a war file in Tomcat 7](http://stackoverflow.com/questions/5109112/how-to-deploy-a-war-file-in-tomcat-7) 51 | [如何将netbeans生成的项目文件打包发布到其他的Tomcat服务器上?](http://asdcm2008.blog.163.com/blog/static/17412758520119249517732/) 52 | [Deploying on tomcat](http://portofino.manydesigns.com/en/docs/portofino3/3_1_x/installation-guide/deploying-on-tomcat) 53 | -------------------------------------------------------------------------------- /00-02.md: -------------------------------------------------------------------------------- 1 | 00-02、理解HTTP 2 | --- 3 | 4 | HTTP是基于TCP协议的。TCP负责数据传输,而HTTP只是规范了TCP传输的数据的格式,而这个具体的格式,请见后面给出的资料。 5 | 6 | HTTP服务的底层实现就是socket编程。 7 | 8 | 9 | 下面基于socket编写一个简单的HTTP server。 10 | 11 | ```java 12 | import java.io.BufferedReader; 13 | import java.io.BufferedWriter; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | import java.io.OutputStreamWriter; 17 | import java.io.PrintWriter; 18 | import java.net.ServerSocket; 19 | import java.net.Socket; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.ExecutorService; 22 | 23 | class SocketHandler implements Runnable { 24 | 25 | final static String CRLF = "\r\n"; // 1 26 | 27 | private Socket clientSocket; 28 | 29 | public SocketHandler(Socket clientSocket) { 30 | this.clientSocket = clientSocket; 31 | } 32 | 33 | public void handleSocket(Socket clientSocket) throws IOException { 34 | 35 | BufferedReader in = new BufferedReader( 36 | new InputStreamReader(clientSocket.getInputStream()) 37 | ); 38 | PrintWriter out = new PrintWriter( 39 | new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream())), 40 | true 41 | ); 42 | 43 | String requestHeader = ""; 44 | String s; 45 | while ((s = in.readLine()) != null) { 46 | s += CRLF; // 2 很重要,默认情况下in.readLine的结果中`\r\n`被去掉了 47 | requestHeader = requestHeader + s; 48 | if (s.equals(CRLF)){ // 3 此处HTTP请求头我们都得到了;如果从请求头中判断有请求正文,则还需要继续获取数据 49 | break; 50 | } 51 | } 52 | System.out.println("客户端请求头:"); 53 | System.out.println(requestHeader); 54 | 55 | String responseBody = "客户端的请求头是:\n"+requestHeader; 56 | 57 | String responseHeader = "HTTP/1.0 200 OK\r\n" + 58 | "Content-Type: text/plain; charset=UTF-8\r\n" + 59 | "Content-Length: "+responseBody.getBytes().length+"\r\n" + 60 | "\r\n"; 61 | // 4 问题来了:1、浏览器如何探测编码 2、浏览器受到content-length后会按照什么方式判断?汉字的个数?字节数? 62 | 63 | System.out.println("响应头:"); 64 | System.out.println(responseHeader); 65 | 66 | 67 | 68 | out.write(responseHeader); 69 | out.write(responseBody); 70 | out.flush(); 71 | 72 | out.close(); 73 | in.close(); 74 | clientSocket.close(); 75 | 76 | } 77 | 78 | @Override 79 | public void run() { 80 | try { 81 | handleSocket(clientSocket); 82 | } catch(Exception ex) { 83 | ex.printStackTrace(); 84 | } 85 | } 86 | 87 | } 88 | 89 | public class MyHTTPServer { 90 | 91 | public static void main(String[] args) throws Exception { 92 | 93 | int port = 8000; 94 | ServerSocket serverSocket = new ServerSocket(port); 95 | System.out.println("启动服务,绑定端口: " + port); 96 | 97 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(30); // 5 98 | 99 | while (true) { // 6 100 | Socket clientSocket = serverSocket.accept(); 101 | System.out.println("新的连接" 102 | + clientSocket.getInetAddress() + ":" + clientSocket.getPort()); 103 | try { 104 | fixedThreadPool.execute(new SocketHandler(clientSocket)); 105 | } catch (Exception e) { 106 | System.out.println(e); 107 | } 108 | } 109 | } 110 | } 111 | ``` 112 | 113 | 这是一个实现HTTP 1.0的服务器,对于所有的HTTP请求,会把HTTP请求头响应回去。 114 | 这个程序说明了web服务器处理请求的基本流程,JSP、Servlet、Spring MVC等只是在 115 | 这个基础上嫁了许多方法,以让我们更方面的编写web应用。web服务器不仅可以基于多线程, 116 | 也可以基于多进程、Reactor模型等。 117 | 118 | **测试程序:** 119 | 运行上面的程序。我们使用curl访问`http://127.0.0.1`(也可以使用浏览器): 120 | ```shell 121 | $ curl -i http://127.0.0.1:8000 122 | HTTP/1.0 200 OK 123 | Content-Type: text/plain; charset=UTF-8 124 | Content-Length: 106 125 | 126 | 客户端的请求头是: 127 | GET / HTTP/1.1 128 | User-Agent: curl/7.35.0 129 | Host: 127.0.0.1:8000 130 | Accept: */* 131 | ``` 132 | 133 | Java程序输出: 134 | ```shell 135 | 启动服务,绑定端口: 8000 136 | 新的连接/127.0.0.1:36463 137 | 新的连接/127.0.0.1:36463客户端请求头: 138 | GET / HTTP/1.1 139 | User-Agent: curl/7.35.0 140 | Host: 127.0.0.1:8000 141 | Accept: */* 142 | 143 | 144 | 响应头: 145 | HTTP/1.0 200 OK 146 | Content-Type: text/plain; charset=UTF-8 147 | Content-Length: 106 148 | ``` 149 | 150 | **程序解析:** 151 | 152 | `// 1`:定义了HTTP头的换行符。 153 | 154 | `// 2`:in.readLine()的结果默认不带换行符,这里把它加上。(这不是强制的,主要看你的程序逻辑需不需要, 155 | 这个程序的目标是把HTTP请求头响应回去)。 156 | 157 | `// 3`:此时s是一个空行,根据HTTP协议,整个请求头都得到了。 158 | 159 | `// 4`:Content-Length的值是字节的数量。 160 | 161 | `// 5`:线程池。 162 | 163 | `// 6`:这个循环不停监听socket连接,使用SocketHandler处理连入的socket,而这个处理是放在线程池中的。 164 | 165 | **HTTP 1.1:** 166 | HTTP 1.1也是在这个思路的基础上实现的,即多个HTTP请求都在一个TCP连接中传输。对于HTTP 1.1,如何区分出每个HTTP请求很重要, 167 | 比较简单的可以是用过`Content-Length`判断一条请求是否结束。如果一个HTTP请求数据较多,往往采用Chunked方式, 168 | 可以参考[Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)。 169 | 170 | 171 | ## HTTP相关资料 172 | 173 | **网络教程:** 174 | [菜鸟教程-HTTP教程](http://www.runoob.com/http/http-tutorial.html) 175 | [List of HTTP header fields](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) 176 | [14 Header Field Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) 177 | [HTTP header should use what character encoding?](http://stackoverflow.com/questions/4400678/http-header-should-use-what-character-encoding) 178 | 179 | 180 | **书籍:** 181 | [HTTP权威指南]() 182 | [计算机网络]() 谢希仁 183 | -------------------------------------------------------------------------------- /01-07.md: -------------------------------------------------------------------------------- 1 | 01-07、国际化 2 | --- 3 | 4 | 本节的项目以为[01-06、校验器](./01-06.md)创建的项目`Project_0106`为基础。 5 | 6 | 所谓国际化,是指根据浏览器HTTP请求头中`Accept-Language`中指定的语言、或者用户指定的语言(Cookie、session中指定), 7 | 将web页面中的一些文本使用该语言展示出来。 8 | 9 | ## 根据浏览器HTTP请求头中`Accept-Language`指定的语言进行国际化 10 | 11 | 项目结构如下: 12 | ![](./img/01-07/01.png) 13 | 14 | ### 源码 15 | 这里只展示改动或者新增的文件。 16 | 17 | **dispatcher-servlet.xml** 18 | ```xml 19 | 20 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | indexController 42 | 43 | 44 | 45 | 46 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | /WEB-INF/resource/message00 60 | /WEB-INF/resource/message01 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ``` 70 | 71 | **message00.properties** 72 | ```plain 73 | welcome=hello 74 | person.firstName.notempty=firstName can not be empty 75 | person.secondName.notempty=secondName can not be empty 76 | person.firstName.tooshort=firstName is too short 77 | ``` 78 | 79 | **message00_en_US.properties** 80 | ```plain 81 | welcome=hello 82 | person.firstName.notempty=firstName can not be empty 83 | person.secondName.notempty=secondName can not be empty 84 | person.firstName.tooshort=firstName is too short 85 | ``` 86 | **message00_zh_CN.properties** 87 | ```plain 88 | welcome=你好 89 | person.firstName.notempty=firstName不能为空 90 | person.secondName.notempty=secondName不能为空 91 | person.firstName.tooshort=firstName太短 92 | ``` 93 | **message01\*.properties** 94 | 这三个文件为空。 95 | 96 | **PersonValidator.java** 97 | ```java 98 | package me.letiantian.validator; 99 | 100 | import me.letiantian.form.Person; 101 | import org.springframework.validation.Errors; 102 | import org.springframework.validation.Validator; 103 | import org.springframework.validation.ValidationUtils; 104 | 105 | public class PersonValidator implements Validator{ 106 | 107 | @Override 108 | public boolean supports(Class type) { 109 | return Person.class.isAssignableFrom(type); 110 | } 111 | 112 | @Override 113 | public void validate(Object o, Errors errors) { 114 | Person person = (Person) o; 115 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "person.firstName.notempty"); 116 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "secondName", "person.secondName.notempty"); 117 | if (person.getFirstName().length() < 2) { 118 | errors.rejectValue("firstName", "person.firstName.tooshort"); 119 | } 120 | } 121 | 122 | } 123 | ``` 124 | **hello/input.jsp** 125 | ```html 126 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 127 | <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 128 | <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 129 | <%@taglib prefix="spring" uri="http://www.springframework.org/tags"%> 130 | 131 | 132 | 133 | 134 | JSP Page 135 | 138 | 139 | 140 |

141 | 142 | firstName:
143 | secondName:
144 | 145 |
146 | 147 | 148 | 149 | ``` 150 | 151 | ### 效果 152 | 可以参考[Change Mozilla Firefox language settings](http://pchelp.ricmedia.com/change-mozilla-firefox-language-settings/)修改火狐浏览器的Accept-Language。 153 | 如果没有效果,可以使用netbeans重启项目,再查看效果。 154 | 155 | ![](./img/01-07/02.png) 156 | 157 | ![](./img/01-07/03.png) 158 | 159 | ## 其他方式的国际化 160 | 161 | 上面的程序中`localeResolver`使用的`AcceptHeaderLocaleResolver`(见配置文件`dispatcher-servlet.xml`)。 162 | 另外,Spring还给出`SessionLocaleResolver`、`CookieLocaleResolver`来实现国际化。也可以根据URL中的指定的Locale进行国际化。 163 | 164 | 可以参考: 165 | [SpringMVC学习系列(8) 之 国际化](http://www.cnblogs.com/liukemng/p/3750117.html) 166 | [Spring MVC internationalization example](http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example) 167 | -------------------------------------------------------------------------------- /00-06.md: -------------------------------------------------------------------------------- 1 | 00-06、使用velocity模板引擎 2 | --- 3 | 4 | > Velocity is a Java-based template engine. It permits anyone to use a simple yet powerful template language to reference objects defined in Java code. 5 | 6 | > When Velocity is used for web development, Web designers can work in parallel with Java programmers to develop web sites according to the Model-View-Controller (MVC) model, meaning that web page designers can focus solely on creating a site that looks good, and programmers can focus solely on writing top-notch code. Velocity separates Java code from the web pages, making the web site more maintainable over its lifespan and providing a viable alternative to Java Server Pages (JSPs) or PHP. 7 | 8 | 以上内容摘自[velocity的官方首页](http://velocity.apache.org/engine/devel/)。 9 | 10 | 以下通过示例来说明velocity的使用。 11 | 12 | ## 项目结构 13 | 在[http://velocity.apache.org/download.cgi](http://velocity.apache.org/download.cgi)中下载velocity-1.7、velocity-tools-2.0。 14 | 15 | 参考[00-03、从JSP开始](./00-03.md)所述,创建项目`Project_0006_Velocity`,导入相关的jar,编写代码。 16 | 17 | 项目结构如下: 18 | ![项目结构](./img/00-06/01.png) 19 | 20 | 对于新增的jar,放到/WEB-INF/lib目录即可。但当多个webApp要使用时,放入CLASSPATH或Servlet容器(如Tomcat)的顶层lib是最好的选择. 21 | 22 | ## 代码 23 | web.xml(在这一节,该文件可以忽略): 24 | ```xml 25 | 26 | 27 | 28 | 29 | default 30 | *.jpg 31 | 32 | 33 | 34 | default 35 | *.png 36 | 37 | 38 | 39 | default 40 | *.js 41 | 42 | 43 | 44 | default 45 | *.css 46 | 47 | 48 | 49 | 50 | 30 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | hello.vm: 58 | ```html 59 | 60 | 61 | 62 | 63 | 64 | #set( $this = "Velocity") 65 | $this is great!
66 | $name
67 | hi , i am letian 68 |

你好

69 | 70 | 71 | ``` 72 | 73 | HelloServlet.java: 74 | ```java 75 | package me.letiantian.servlet; 76 | 77 | import java.io.IOException; 78 | import java.io.PrintWriter; 79 | import javax.servlet.ServletException; 80 | import javax.servlet.annotation.WebServlet; 81 | import javax.servlet.http.HttpServlet; 82 | import javax.servlet.http.HttpServletRequest; 83 | import javax.servlet.http.HttpServletResponse; 84 | 85 | import java.util.Properties; 86 | import java.io.StringWriter; 87 | import org.apache.velocity.app.Velocity; 88 | import org.apache.velocity.app.VelocityEngine; 89 | import org.apache.velocity.VelocityContext; 90 | 91 | 92 | @WebServlet(name = "HelloServlet", urlPatterns = {"/hello"}) 93 | public class HelloServlet extends HttpServlet { 94 | 95 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 96 | throws ServletException, IOException { 97 | response.setContentType("text/html;charset=UTF-8"); 98 | PrintWriter out = response.getWriter(); 99 | 100 | Properties properties=new Properties(); 101 | properties.setProperty("resource.loader", "webapp"); 102 | properties.setProperty("webapp.resource.loader.class", "org.apache.velocity.tools.view.servlet.WebappLoader"); 103 | properties.setProperty("webapp.resource.loader.path", "/WEB-INF/template"); 104 | properties.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8"); 105 | properties.setProperty(Velocity.INPUT_ENCODING, "UTF-8"); 106 | properties.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8"); 107 | VelocityEngine velocityEngine = new VelocityEngine(properties); 108 | velocityEngine.setApplicationAttribute("javax.servlet.ServletContext", request.getServletContext()); 109 | 110 | VelocityContext context=new VelocityContext(); 111 | context.put("name", "user01"); 112 | StringWriter sw = new StringWriter(); 113 | velocityEngine.mergeTemplate("hello.vm", "utf-8", context, sw); 114 | // velocityEngine.mergeTemplate("hello.vm", "utf-8", context, sw); //如果这行不注释,hello.vm的内容会出现两次 115 | out.println(sw.toString()); 116 | 117 | } 118 | 119 | @Override 120 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 121 | throws ServletException, IOException { 122 | processRequest(request, response); 123 | } 124 | 125 | @Override 126 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 127 | throws ServletException, IOException { 128 | processRequest(request, response); 129 | } 130 | 131 | } 132 | ``` 133 | 134 | 运行项目,用浏览器访问`http://127.0.0.1:8084/Project_0006_Velocity/hello`: 135 | ![](./img/00-06/02.png) 136 | 137 | 138 | ## 改进上面的代码 139 | 140 | 在WEB-INF目录下创建`velocity.properties`文件,其内容如下: 141 | ```plain 142 | resource.loader=webapp 143 | webapp.resource.loader.class=org.apache.velocity.tools.view.servlet.WebappLoader 144 | webapp.resource.loader.path=/WEB-INF/template/ 145 | input.encoding=utf-8 146 | output.encoding=utf-8 147 | ``` 148 | 149 | 修改HelloServlet.java: 150 | ```java 151 | package me.letiantian.servlet; 152 | 153 | import java.io.IOException; 154 | import java.io.PrintWriter; 155 | import javax.servlet.ServletException; 156 | import javax.servlet.annotation.WebServlet; 157 | import javax.servlet.http.HttpServlet; 158 | import javax.servlet.http.HttpServletRequest; 159 | import javax.servlet.http.HttpServletResponse; 160 | 161 | import java.util.Properties; 162 | import java.io.StringWriter; 163 | import org.apache.velocity.app.VelocityEngine; 164 | import org.apache.velocity.VelocityContext; 165 | 166 | 167 | @WebServlet(name = "HelloServlet", urlPatterns = {"/hello"}) 168 | public class HelloServlet extends HttpServlet { 169 | 170 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 171 | throws ServletException, IOException { 172 | response.setContentType("text/html;charset=UTF-8"); 173 | PrintWriter out = response.getWriter(); 174 | 175 | Properties properties=new Properties(); 176 | properties.load(getServletContext().getResourceAsStream("/WEB-INF/velocity.properties")); 177 | 178 | VelocityEngine velocityEngine = new VelocityEngine(properties); 179 | velocityEngine.setApplicationAttribute("javax.servlet.ServletContext", request.getServletContext()); 180 | 181 | VelocityContext context=new VelocityContext(); 182 | context.put("name", "user01"); 183 | StringWriter sw = new StringWriter(); 184 | velocityEngine.mergeTemplate("hello.vm", "utf-8", context, sw); 185 | 186 | out.println(sw.toString()); 187 | 188 | } 189 | 190 | @Override 191 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 192 | throws ServletException, IOException { 193 | processRequest(request, response); 194 | } 195 | 196 | @Override 197 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 198 | throws ServletException, IOException { 199 | processRequest(request, response); 200 | } 201 | 202 | } 203 | ``` 204 | 205 | ## 资料 206 | 207 | [velocity模板加载](http://www.blogjava.net/sxyx2008/archive/2010/11/11/337799.html) 208 | [velocity整合servlet](http://www.blogjava.net/sxyx2008/archive/2010/11/11/337819.html) 209 | [Velocity三——基于Servlet+Velocity的web应用](http://bit1129.iteye.com/blog/2106142) 210 | [使用 Velocity 实现客户端和服务器端模板](http://www.ibm.com/developerworks/cn/java/j-velocity/) 211 | [Servlet 知识详解(一)之 —— ServletContext对象 和 ServletConfig对象 学习笔记](http://even2012.iteye.com/blog/1838063) 212 | -------------------------------------------------------------------------------- /00-03.md: -------------------------------------------------------------------------------- 1 | 00-03、从JSP开始 2 | --- 3 | 4 | ## 创建web项目 5 | 打开netbeans,在菜单栏依次选择“File”、“New Project...”,然后 6 | 7 | 1、选择java web: 8 | ![](./img/00-03/01.png) 9 | 10 | 2、项目名称、路径: 11 | ![](./img/00-03/02.png) 12 | 13 | 3、选择使用的web容器,编写ContextPath: 14 | ![](./img/00-03/03.png) 15 | 16 | 生成的web项目结构如下: 17 | ![](./img/00-03/04.png) 18 | 19 | index.html的内容如下: 20 | ```html 21 | 22 | 27 | 28 | 29 | TODO supply a title 30 | 31 | 32 | 33 | 34 |
TODO write content
35 | 36 | 37 | ``` 38 | 39 | `context.xml`的内容如下: 40 | ```xml 41 | 42 | 43 | ``` 44 | 45 | `apache-tomcat-8.0.15/conf/server.xml`中关于端口的配置如下: 46 | ```xml 47 | 50 | ``` 51 | 52 | 运行项目后,netbeans的输出信息如下: 53 | ``` 54 | ant -f /data/Code/netbeans/HelloJSP -Dnb.internal.action.name=run -Ddirectory.deployment.supported=true -DforceRedeploy=false -Dnb.wait.for.caches=true -Dbrowser.context=/data/Code/netbeans/HelloJSP run 55 | init: 56 | deps-module-jar: 57 | deps-ear-jar: 58 | deps-jar: 59 | library-inclusion-in-archive: 60 | library-inclusion-in-manifest: 61 | compile: 62 | compile-jsps: 63 | Incrementally deploying http://localhost:8084/HelloJSP 64 | Completed incremental distribution of http://localhost:8084/HelloJSP 65 | run-deploy: 66 | Browsing: http://localhost:8084/HelloJSP 67 | run-display-browser: 68 | run: 69 | ``` 70 | 71 | 使用浏览器访问`http://localhost:8084/HelloJSP/`,能看到`TODO write content`。 72 | 73 | **那么问题来了,为什么配置的是8080,访问时却用8084端口?** 74 | 答案是:这个端口是netbeans启动tomcat时配置的。 75 | 76 | 看一下启动命令: 77 | ```shell 78 | $ ps -ef | grep 'tomcat' | grep -v grep 79 | letian 390 24314 0 09:24 ? 00:00:08 /data/Apps/jdk1.8.0_20/bin/java -Djava.util.logging.config.file=/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dhttp.nonProxyHosts=localhost|127.0.0.1|myhost -Djava.endorsed.dirs=/data/Apps/apache-tomcat-8.0.15/endorsed -classpath /data/Apps/apache-tomcat-8.0.15/bin/bootstrap.jar:/data/Apps/apache-tomcat-8.0.15/bin/tomcat-juli.jar -Dcatalina.base=/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base -Dcatalina.home=/data/Apps/apache-tomcat-8.0.15 -Djava.io.tmpdir=/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base/temp org.apache.catalina.startup.Bootstrap start 80 | 81 | $ grep -r '8084' /home/letian/.netbeans/8.0.2 # 可以看到很多结果 82 | ... 83 | ``` 84 | 85 | 也就是说,实际使用的是`/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base/conf` 86 | 下的配置。在``/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base/conf/Catalina/localhost/HelloJSP.xml`中有以下内容: 87 | ```xml 88 | 89 | ``` 90 | 这意味着访问`http://localhost:8084/HelloJSP/`时对应的web应用部署在HelloJSP项目 91 | 的`build/web/`目录下。 92 | 93 | 94 | ## 基于JSP的hello world 95 | 删除index.html,新建index.jsp,内容如下: 96 | ```html 97 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 98 | 99 | 100 | 101 | 102 | JSP Page 103 | 104 | 105 | <% 106 | String data="hello world"; 107 | boolean flag=true; 108 | if (flag==true) { 109 | out.println("

" +data.toUpperCase()+ "

"); 110 | } 111 | %> 112 | 113 | 114 | ``` 115 | 运行项目,访问`http://localhost:8084/HelloJSP/`,可以看到`HELLO WORLD`。 116 | 117 | JSP在部署时会被转换成servlet,`/home/letian/.netbeans/8.0.2/apache-tomcat-8.0.15.0_base/work/Catalina/localhost/HelloJSP/org/apache/jsp`中的`index_jsp.java`就是对应的servlet。其内容如下: 118 | ```java 119 | /* 120 | * Generated by the Jasper component of Apache Tomcat 121 | * Version: Apache Tomcat/8.0.15 122 | * Generated at: 2015-09-18 02:43:43 UTC 123 | * Note: The last modified time of this file was set to 124 | * the last modified time of the source file after 125 | * generation to assist with modification tracking. 126 | */ 127 | package org.apache.jsp; 128 | 129 | import javax.servlet.*; 130 | import javax.servlet.http.*; 131 | import javax.servlet.jsp.*; 132 | 133 | public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase 134 | implements org.apache.jasper.runtime.JspSourceDependent { 135 | 136 | private static final javax.servlet.jsp.JspFactory _jspxFactory = 137 | javax.servlet.jsp.JspFactory.getDefaultFactory(); 138 | 139 | private static java.util.Map _jspx_dependants; 140 | 141 | private javax.el.ExpressionFactory _el_expressionfactory; 142 | private org.apache.tomcat.InstanceManager _jsp_instancemanager; 143 | 144 | public java.util.Map getDependants() { 145 | return _jspx_dependants; 146 | } 147 | 148 | public void _jspInit() { 149 | _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); 150 | _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); 151 | } 152 | 153 | public void _jspDestroy() { 154 | } 155 | 156 | public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) 157 | throws java.io.IOException, javax.servlet.ServletException { 158 | 159 | final java.lang.String _jspx_method = request.getMethod(); 160 | if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { 161 | response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD"); 162 | return; 163 | } 164 | 165 | final javax.servlet.jsp.PageContext pageContext; 166 | javax.servlet.http.HttpSession session = null; 167 | final javax.servlet.ServletContext application; 168 | final javax.servlet.ServletConfig config; 169 | javax.servlet.jsp.JspWriter out = null; 170 | final java.lang.Object page = this; 171 | javax.servlet.jsp.JspWriter _jspx_out = null; 172 | javax.servlet.jsp.PageContext _jspx_page_context = null; 173 | 174 | 175 | try { 176 | response.setContentType("text/html;charset=UTF-8"); 177 | pageContext = _jspxFactory.getPageContext(this, request, response, 178 | null, true, 8192, true); 179 | _jspx_page_context = pageContext; 180 | application = pageContext.getServletContext(); 181 | config = pageContext.getServletConfig(); 182 | session = pageContext.getSession(); 183 | out = pageContext.getOut(); 184 | _jspx_out = out; 185 | 186 | out.write("\n"); 187 | out.write("\n"); 188 | out.write("\n"); 189 | out.write(" \n"); 190 | out.write(" \n"); 191 | out.write(" JSP Page\n"); 192 | out.write(" \n"); 193 | out.write(" \n"); 194 | out.write(" "); 195 | 196 | String data="hello world"; 197 | boolean flag=true; 198 | if (flag==true) { 199 | out.println("

" +data.toUpperCase()+ "

"); 200 | } 201 | 202 | out.write("\n"); 203 | out.write(" \n"); 204 | out.write("\n"); 205 | } catch (java.lang.Throwable t) { 206 | if (!(t instanceof javax.servlet.jsp.SkipPageException)){ 207 | out = _jspx_out; 208 | if (out != null && out.getBufferSize() != 0) 209 | try { 210 | if (response.isCommitted()) { 211 | out.flush(); 212 | } else { 213 | out.clearBuffer(); 214 | } 215 | } catch (java.io.IOException e) {} 216 | if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); 217 | else throw new ServletException(t); 218 | } 219 | } finally { 220 | _jspxFactory.releasePageContext(_jspx_page_context); 221 | } 222 | } 223 | } 224 | ``` 225 | 感受一下这段代码吧~ 226 | 227 | 关于JSP就介绍这么多。**需要记住的是:JSP最合适的用途是用作MVC中的视图,而不是和HTML一起混合编码 228 | (例如把从数据库拉取数据也放入JSP中写)** 229 | 230 | 要了解更多,请参考下面的相关资料。 231 | 232 | ## JSP相关资料 233 | [JSP 教程](http://www.runoob.com/jsp/jsp-tutorial.html) 234 | -------------------------------------------------------------------------------- /01-09.md: -------------------------------------------------------------------------------- 1 | 01-09、文件上传 2 | --- 3 | 4 | 先看一下Servlet是如何处理文件上传的: 5 | [Servlets - File Uploading](http://www.tutorialspoint.com/servlets/servlets-file-uploading.htm) 6 | [Java File Upload Example with Servlet 3.0 API](http://www.codejava.net/java-ee/servlet/java-file-upload-example-with-servlet-30-api) 7 | 8 | Spring MVC下的处理是类似的。 9 | 10 | 下面展示一个简单的实现。 11 | 12 | ## 项目结构与源码 13 | ![](./img/01-09/01.png) 14 | 15 | ### web.xml 16 | ```xml 17 | 18 | 19 | 20 | contextConfigLocation 21 | /WEB-INF/applicationContext.xml 22 | 23 | 24 | org.springframework.web.context.ContextLoaderListener 25 | 26 | 27 | dispatcher 28 | org.springframework.web.servlet.DispatcherServlet 29 | 2 30 | 31 | 1000000 32 | 2000000 33 | 4000000 34 | 35 | 36 | 37 | dispatcher 38 | / 39 | 40 | 41 | 42 | 30 43 | 44 | 45 | 46 | redirect.jsp 47 | 48 | 49 | ``` 50 | 51 | 注意其中的限制文件大小的配置: 52 | ```xml 53 | 54 | 1000000 55 | 2000000 56 | 4000000 57 | 58 | ``` 59 | 60 | ### applicationContext.xml 61 | ```xml 62 | 63 | 64 | 72 | 73 | 74 | ``` 75 | 76 | ### dispatcher-servlet.xml 77 | ```xml 78 | 79 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | indexController 106 | 107 | 108 | 109 | 110 | 114 | 115 | 118 | 119 | 120 | 121 | 122 | ``` 123 | 124 | 注意,这里增加了一个multipart解析器`StandardServletMultipartResolver`,用来处理上传的文件。`/static`是存放静态资源的目录, 125 | 我们也准备将上传的文件放到这个目录里。 126 | 127 | ### UploadedFile.java 128 | ```java 129 | package me.letiantian.form; 130 | 131 | import org.springframework.web.multipart.MultipartFile; 132 | 133 | public class UploadedFile { 134 | private String fileName; 135 | private MultipartFile multipartFile; 136 | 137 | public String getFileName() { 138 | return fileName; 139 | } 140 | 141 | public void setFileName(String fileName) { 142 | this.fileName = fileName; 143 | } 144 | 145 | public MultipartFile getMultipartFile() { 146 | return multipartFile; 147 | } 148 | 149 | public void setMultipartFile(MultipartFile multipartFile) { 150 | this.multipartFile = multipartFile; 151 | } 152 | 153 | } 154 | ``` 155 | 156 | ### HelloController.java 157 | 158 | ```java 159 | package me.letiantian.controller; 160 | 161 | import java.io.File; 162 | import java.io.IOException; 163 | import java.io.PrintWriter; 164 | import javax.servlet.http.HttpServletRequest; 165 | import javax.servlet.http.HttpServletResponse; 166 | import org.springframework.stereotype.Controller; 167 | import org.springframework.web.bind.annotation.RequestMapping; 168 | import me.letiantian.form.UploadedFile; 169 | import org.springframework.web.bind.annotation.ModelAttribute; 170 | 171 | import org.springframework.web.multipart.MultipartFile; 172 | 173 | @Controller 174 | @RequestMapping("/hello") 175 | public class HelloController{ 176 | 177 | 178 | @RequestMapping(value = "") 179 | public String index() { 180 | return "hello/index"; 181 | } 182 | 183 | @RequestMapping(value = "/upload") 184 | public void output(@ModelAttribute UploadedFile uploadedFile, 185 | HttpServletRequest request, 186 | HttpServletResponse response) throws IOException { 187 | response.setContentType("text/html;charset=UTF-8"); 188 | PrintWriter out = response.getWriter(); 189 | MultipartFile multiPartFile = uploadedFile.getMultipartFile(); 190 | System.out.println("文件原始名称:"+multiPartFile.getOriginalFilename()); 191 | System.out.println("表单给定的文件名称:"+uploadedFile.getFileName()); 192 | 193 | try{ 194 | System.out.println("上传目录:"+request.getServletContext().getRealPath("/static")); 195 | File file = new File(request.getServletContext().getRealPath("/static"), 196 | uploadedFile.getFileName()); 197 | multiPartFile.transferTo(file); // 将文件写入本地 198 | out.println("

上传成功

"); 199 | } catch (Exception ex) { 200 | System.out.println(""+ex.getMessage()); 201 | out.println("

上传失败

"); 202 | } 203 | } 204 | 205 | } 206 | ``` 207 | 注意,表单数据被绑定到了`uploadedFile`对象中。 208 | 209 | ### hello/index.jsp 210 | ```html 211 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 212 | 213 | 214 | 215 | 216 | JSP Page 217 | 218 | 219 |
220 | 指定文件名:
221 | 选择文件:
222 | 223 |
224 | 225 | 226 | ``` 227 | 228 | ## 测试程序 229 | 选择文件,指定名称: 230 | ![](./img/01-09/02.png) 231 | 232 | 上传成功: 233 | ![](./img/01-09/03.png) 234 | 235 | 查看上传的文件: 236 | ![](./img/01-09/04.png) 237 | 238 | Tomcat输出: 239 | ```plain 240 | 文件原始名称:01.png 241 | 表单给定的文件名称:1.png 242 | 上传目录:/data/Code/netbeans/Project_0109/build/web/static 243 | ``` 244 | 245 | ## 资料 246 | 《Spring MVC学习指南》 第11章 247 | -------------------------------------------------------------------------------- /01-04.md: -------------------------------------------------------------------------------- 1 | 01-04、基于注解的URL映射 2 | --- 3 | 4 | 5 | ## 项目结构 6 | 创建项目`Pcrojet_0104`,最终结构如下: 7 | ![](./img/01-04/01.png) 8 | 9 | ## 源码 10 | 11 | ### applicationContext.xml 12 | ```xml 13 | 14 | 27 | 28 | 29 | ``` 30 | 该配置文件什么都没做。 31 | 32 | ### dispatcher-servlet.xml 33 | ```xml 34 | 35 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | indexController 57 | 58 | 59 | 60 | 61 | 65 | 66 | 69 | 70 | 71 | ``` 72 | 73 | ``中增加了属性`xmlns:context`、`xmlns:mvc`,对应的在`xsi:schemaLocation`增加了: 74 | ```plain 75 | http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 76 | http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 77 | ``` 78 | 79 | 关于``的意义,可参考[What's the difference between and in servlet?](http://stackoverflow.com/questions/3977973/whats-the-difference-between-mvcannotation-driven-and-contextannotation)。 80 | 81 | ### web.xml 82 | ```xml 83 | 84 | 85 | 86 | contextConfigLocation 87 | /WEB-INF/applicationContext.xml 88 | 89 | 90 | org.springframework.web.context.ContextLoaderListener 91 | 92 | 93 | dispatcher 94 | org.springframework.web.servlet.DispatcherServlet 95 | 2 96 | 97 | 98 | dispatcher 99 | / 100 | 101 | 102 | 103 | 30 104 | 105 | 106 | 107 | redirect.jsp 108 | 109 | 110 | ``` 111 | 112 | ### 模板文件 113 | 114 | **index.jsp:** 115 | ```html 116 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 117 | 119 | 120 | 121 | 122 | 123 | Welcome to Spring Web MVC project 124 | 125 | 126 | 127 |

Hello! This is the default welcome page for a Spring Web MVC project.

128 |

To display a different welcome page for this project, modify 129 | index.jsp , or create your own welcome page then change 130 | the redirection in redirect.jsp to point to the new 131 | welcome page and also update the welcome-file setting in 132 | web.xml.

133 | 134 | 135 | ``` 136 | 137 | **hello/method01.jsp:** 138 | ```html 139 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 140 | 141 | 142 | 143 | 144 | JSP Page 145 | 146 | 147 |

Hello Method01

148 | 149 | 150 | ``` 151 | 152 | **hello/method04.jsp:** 153 | ```plain 154 | <%@page contentType="text/plain" pageEncoding="UTF-8"%> 155 | 156 | name: ${name} 157 | ``` 158 | 159 | ### HelloController.java 160 | ```java 161 | package me.letiantian.controller; 162 | 163 | import java.io.PrintWriter; 164 | import javax.servlet.http.HttpServletRequest; 165 | import javax.servlet.http.HttpServletResponse; 166 | import org.springframework.stereotype.Controller; 167 | import org.springframework.ui.Model; 168 | import org.springframework.web.bind.annotation.PathVariable; 169 | import org.springframework.web.bind.annotation.RequestMapping; 170 | import org.springframework.web.bind.annotation.RequestMethod; 171 | 172 | @Controller 173 | @RequestMapping("/hello") 174 | public class HelloController{ 175 | 176 | @RequestMapping(value = "") 177 | public String index() { 178 | return "index"; 179 | } 180 | 181 | @RequestMapping(value = "/method01") 182 | public String method01() { 183 | return "hello/method01"; 184 | } 185 | 186 | // 仅支持HTTP POST方法 187 | @RequestMapping(value = "/method02", method = {RequestMethod.POST}) 188 | public String method02() { 189 | return "hello/method01"; 190 | } 191 | 192 | @RequestMapping(value = "/method03") 193 | public void method03(HttpServletRequest request, HttpServletResponse response) { 194 | response.setContentType("text/plain;charset=UTF-8"); 195 | try (PrintWriter out = response.getWriter()) { 196 | out.println("contextPath: " + request.getContextPath()); 197 | out.println("name: " + request.getParameter("name")); 198 | } catch (Exception ex) { 199 | System.out.println(""+ex.getMessage()); 200 | } 201 | } 202 | 203 | @RequestMapping(value = "/method04/{name}") 204 | public String method04(@PathVariable String name, Model model) { 205 | model.addAttribute("name", name); 206 | return "hello/method04"; 207 | } 208 | 209 | @RequestMapping(value = "/method05/{id}") 210 | public String method05(@PathVariable int id, Model model) { 211 | model.addAttribute("name", id); 212 | return "hello/method04"; 213 | } 214 | } 215 | ``` 216 | 217 | ## 测试 218 | 219 | ![](./img/01-04/02.png) 220 | ![](./img/01-04/03.png) 221 | ![](./img/01-04/04.png) 222 | ![](./img/01-04/05.png) 223 | ![](./img/01-04/06.png) 224 | ![](./img/01-04/07.png) 225 | ![](./img/01-04/08.png) 226 | ![](./img/01-04/09.png) 227 | 228 | ## 其他 229 | 230 | **如何让一个方法映射多个URL?** 231 | 很简单,例如`@RequestMapping(value = {"/hello", "/hi"})`。可参考[Spring MVC: Mapping Multiple URLs to Same Controller](http://stackoverflow.com/questions/3898442/spring-mvc-mapping-multiple-urls-to-same-controller)。 232 | 233 | **如何自定义错误页面?** 234 | [Spring MVC : How To Return Custom 404 Error Pages](http://www.javabeat.net/spring-mvc-404-error-page/) 235 | [Spring MVC Exception Handling Example](http://www.mkyong.com/spring-mvc/spring-mvc-exception-handling-example/) 236 | [Exception Handling in Spring MVC](https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc) 237 | 238 | **@PathVariable是绑定数据的其中一种方法,还有绑定Cookie中数据,将表单数据绑定到对象中等方法:** 239 | [Spring MVC Cookie example](http://viralpatel.net/blogs/spring-mvc-cookie-example/) 240 | [Injecting and Binding Objects to Spring MVC Controllers](http://www.cnblogs.com/davidwang456/p/4019595.html) 241 | -------------------------------------------------------------------------------- /00-07.md: -------------------------------------------------------------------------------- 1 | 00-07、使用数据库连接池 2 | --- 3 | 4 | 目前比较常见的连接池实现有DBCP、C3P0,Tomcat_JDBC等。 5 | 6 | 本文使用的连接池是DBCP。 7 | 8 | 进入[http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi](http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi)下载`Apache Commons DBCP for JDBC `,[http://commons.apache.org/proper/commons-pool/download_pool.cgi](http://commons.apache.org/proper/commons-pool/download_pool.cgi)中下载`Apache Commons Pool`,[http://dev.mysql.com/downloads/connector/j/](http://dev.mysql.com/downloads/connector/j/)下载MySQL的JDBC驱动。 9 | 10 | 若下载出现问题,可以到一些Maven仓库中下载。例如[http://mvnrepository.com/](http://mvnrepository.com/)、[http://maven.oschina.net](http://maven.oschina.net)。 11 | 12 | 13 | ## 数据库准备 14 | MySQL 5.6。 15 | 16 | ```sql 17 | --创建数据库 18 | CREATE DATABASE IF NOT EXISTS `test` DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 19 | USE `test`; 20 | --创建table 21 | CREATE TABLE IF NOT EXISTS user 22 | ( 23 | `id` int AUTO_INCREMENT, 24 | `name` varchar(255), 25 | `email` varchar(255), 26 | `age` varchar(255), 27 | `passwd` varchar(255), 28 | PRIMARY KEY (`id`), 29 | UNIQUE KEY (`name`), 30 | UNIQUE KEY (`email`) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 32 | ---插入若干数据 33 | INSERT INTO user (`name`, `email`, `age`, `passwd`) 34 | VALUES ('user01', 'user01@163.com', 20, password('123')); 35 | 36 | INSERT INTO user (`name`, `email`, `age`, `passwd`) 37 | VALUES ('user02', 'user02@163.com', 20, password('456')); 38 | ``` 39 | 40 | ## 示例1 41 | 42 | 目录结构如下: 43 | ![](./img/00-07/01.png) 44 | 45 | **web.xml源码:** 46 | ```xml 47 | 48 | 49 | 50 | 51 | default 52 | *.jpg 53 | 54 | 55 | 56 | default 57 | *.png 58 | 59 | 60 | 61 | default 62 | *.js 63 | 64 | 65 | 66 | default 67 | *.css 68 | 69 | 70 | 71 | 72 | 30 73 | 74 | 75 | 76 | 77 | ``` 78 | 79 | **dbcp.properties源码:** 80 | ```plain 81 | driverClassName=com.mysql.jdbc.Driver 82 | url=jdbc:mysql://localhost:3306/test 83 | username=root 84 | password=123456 85 | initialSize=2 86 | maxActive=15 87 | maxIdle=2 88 | minIdle=1 89 | maxWait=30000 90 | ``` 91 | 这些配置的解释请见[BasicDataSource Configuration Parameters](http://commons.apache.org/proper/commons-dbcp/configuration.html)。 92 | 93 | **HelloServlet.java源码:** 94 | ```java 95 | package me.letiantian.servlet; 96 | 97 | import java.io.IOException; 98 | import java.io.PrintWriter; 99 | import java.sql.Connection; 100 | import java.sql.PreparedStatement; 101 | import java.sql.ResultSet; 102 | import java.util.Properties; 103 | import javax.servlet.annotation.WebServlet; 104 | import javax.servlet.http.HttpServlet; 105 | import javax.servlet.http.HttpServletRequest; 106 | import javax.servlet.http.HttpServletResponse; 107 | import javax.sql.DataSource; 108 | 109 | import org.apache.commons.dbcp.BasicDataSourceFactory; 110 | 111 | 112 | @WebServlet(name = "HelloServlet", urlPatterns = {"/hello"}) 113 | public class HelloServlet extends HttpServlet { 114 | 115 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { 116 | response.setContentType("text/html;charset=UTF-8"); 117 | PrintWriter out = response.getWriter(); 118 | try{ 119 | Properties properties=new Properties(); 120 | properties.load(getServletContext().getResourceAsStream("/WEB-INF/dbcp.properties")); 121 | DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); 122 | Connection conn = dataSource.getConnection(); 123 | String sql = "select 1+1 as result;"; 124 | PreparedStatement pstmt = conn.prepareStatement(sql); 125 | ResultSet rs = pstmt.executeQuery(); 126 | 127 | if (rs.next()) { 128 | int result = rs.getInt("result"); 129 | out.println("result: " + result); 130 | } 131 | 132 | rs.close(); 133 | pstmt.close(); 134 | conn.close(); 135 | 136 | } catch (Exception ex) { 137 | out.println(ex.getMessage()); 138 | } 139 | } 140 | 141 | @Override 142 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 143 | processRequest(request, response); 144 | } 145 | 146 | 147 | @Override 148 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 149 | processRequest(request, response); 150 | } 151 | 152 | } 153 | ``` 154 | 155 | 运行项目,浏览器访问`http://localhost:8084/Project_0007_DBCP/hello`: 156 | ![](./img/00-07/02.png) 157 | 158 | 159 | ## 改进:将初始化的连接池放到Servlet上下文中 160 | 上面代码中是再servlet中初始化连接池,更好的方法是再Listener中初始化,并将连接池作为属性放入servlet上下文中。 161 | 162 | 源文件以及代码有所变化,项目结构如下: 163 | ![](./img/00-07/03.png) 164 | 165 | DBCPListener.java内容如下: 166 | ```java 167 | package me.letiantian.listener; 168 | 169 | import java.util.Properties; 170 | import javax.servlet.ServletContext; 171 | import javax.servlet.ServletContextEvent; 172 | import javax.servlet.ServletContextListener; 173 | import javax.servlet.annotation.WebListener; 174 | import javax.sql.DataSource; 175 | import org.apache.commons.dbcp.BasicDataSourceFactory; 176 | 177 | @WebListener 178 | public class DBCPListener implements ServletContextListener{ 179 | 180 | // 应用启动时,该方法被调用 181 | @Override 182 | public void contextInitialized(ServletContextEvent sce) { 183 | try { 184 | System.out.println("设置数据库连接池"); 185 | ServletContext application = sce.getServletContext(); 186 | Properties properties=new Properties(); 187 | properties.load(application.getResourceAsStream("/WEB-INF/dbcp.properties")); 188 | DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); 189 | application.setAttribute("dataSource", dataSource); 190 | } 191 | catch(Exception ex) { 192 | System.err.println("数据库连接池设置出现异常:" + ex.getMessage()); 193 | } 194 | } 195 | 196 | // 应用关闭时,该方法被调用 197 | @Override 198 | public void contextDestroyed(ServletContextEvent sce) { 199 | 200 | } 201 | 202 | } 203 | ``` 204 | 205 | HelloServlet.java内容如下: 206 | ```java 207 | package me.letiantian.servlet; 208 | 209 | import java.io.IOException; 210 | import java.io.PrintWriter; 211 | import java.sql.Connection; 212 | import java.sql.PreparedStatement; 213 | import java.sql.ResultSet; 214 | import javax.servlet.annotation.WebServlet; 215 | import javax.servlet.http.HttpServlet; 216 | import javax.servlet.http.HttpServletRequest; 217 | import javax.servlet.http.HttpServletResponse; 218 | import javax.sql.DataSource; 219 | 220 | @WebServlet(name = "HelloServlet", urlPatterns = {"/hello"}) 221 | public class HelloServlet extends HttpServlet { 222 | 223 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { 224 | response.setContentType("text/html;charset=UTF-8"); 225 | PrintWriter out = response.getWriter(); 226 | try { 227 | DataSource dataSource = (DataSource) getServletContext().getAttribute("dataSource"); 228 | Connection conn = dataSource.getConnection(); 229 | String sql = "select name from user;"; 230 | PreparedStatement pstmt = conn.prepareStatement(sql); 231 | ResultSet rs = pstmt.executeQuery(); 232 | 233 | while (rs.next()) { 234 | String name = rs.getString("name"); 235 | out.println("result: " + name + "
"); 236 | } 237 | 238 | rs.close(); 239 | pstmt.close(); 240 | conn.close(); 241 | 242 | } catch (Exception ex) { 243 | out.println(ex.getMessage()); 244 | } 245 | } 246 | 247 | @Override 248 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 249 | processRequest(request, response); 250 | } 251 | 252 | 253 | @Override 254 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 255 | processRequest(request, response); 256 | } 257 | 258 | } 259 | ``` 260 | 261 | 启动项目,可以看到Tomcat输出: 262 | ```plain 263 | 设置数据库连接池 264 | ``` 265 | 266 | 浏览器输出: 267 | ![](./img/00-07/04.png) 268 | 269 | 查看一下mysql的连接: 270 | ```plain 271 | mysql> show processlist; 272 | +----+------+-----------------+------+---------+------+-------+------------------+ 273 | | Id | User | Host | db | Command | Time | State | Info | 274 | +----+------+-----------------+------+---------+------+-------+------------------+ 275 | | 45 | root | localhost | test | Query | 0 | init | show processlist | 276 | | 77 | root | localhost:41770 | test | Sleep | 300 | | NULL | 277 | | 78 | root | localhost:41771 | test | Sleep | 300 | | NULL | 278 | | 83 | root | localhost:41790 | test | Sleep | 274 | | NULL | 279 | | 84 | root | localhost:41791 | test | Sleep | 69 | | NULL | 280 | +----+------+-----------------+------+---------+------+-------+------------------+ 281 | 5 rows in set (0.00 sec) 282 | ``` 283 | 284 | 关闭Tomcat,查看数据库连接: 285 | ```plain 286 | mysql> show processlist; 287 | +----+------+-----------+------+---------+------+-------+------------------+ 288 | | Id | User | Host | db | Command | Time | State | Info | 289 | +----+------+-----------+------+---------+------+-------+------------------+ 290 | | 45 | root | localhost | test | Query | 0 | init | show processlist | 291 | +----+------+-----------+------+---------+------+-------+------------------+ 292 | 1 row in set (0.00 sec) 293 | ``` 294 | 295 | ## DBUtils 296 | 使用DBUtils可以更加方便的操作数据库,可以参考[DBUtils简明教程](http://www.letiantian.me/2015-03-09-apache-dbutils/)。 297 | 298 | 299 | ## 资料 300 | 官网 301 | [DBCP,C3P0,Tomcat_JDBC 性能及稳定性测试](http://www.open-open.com/lib/view/open1329182303124.html) 302 | [数据连接池DBCP参数介绍](http://my.oschina.net/robinsonlu/blog/77759) 303 | [DBCP数据库连接池的使用](http://my.oschina.net/donghongyu/blog/190494) 304 | -------------------------------------------------------------------------------- /01-03.md: -------------------------------------------------------------------------------- 1 | 01-03、JdbcTemplate 2 | --- 3 | 4 | JdbcTemplate是Spring MVC内置的对JDBC的一个封装。 5 | 6 | ## 数据库准备 7 | MySQL 5.6。 8 | 9 | ```sql 10 | --创建数据库 11 | CREATE DATABASE IF NOT EXISTS `test` DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 12 | USE `test`; 13 | --创建table 14 | CREATE TABLE IF NOT EXISTS user 15 | ( 16 | `id` int AUTO_INCREMENT, 17 | `name` varchar(255), 18 | `email` varchar(255), 19 | `age` varchar(255), 20 | `passwd` varchar(255), 21 | PRIMARY KEY (`id`), 22 | UNIQUE KEY (`name`), 23 | UNIQUE KEY (`email`) 24 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 25 | ---插入若干数据 26 | INSERT INTO user (`name`, `email`, `age`, `passwd`) 27 | VALUES ('user01', 'user01@163.com', 20, password('123')); 28 | 29 | INSERT INTO user (`name`, `email`, `age`, `passwd`) 30 | VALUES ('user02', 'user02@163.com', 20, password('456')); 31 | 32 | INSERT INTO user (`name`, `email`, `age`, `passwd`) 33 | VALUES ('用户03', 'user03@163.com', 20, password('456')); 34 | ``` 35 | 36 | ## 示例1 37 | 继续使用的上一节[01-02、使用Spring MVC构建Hello World](./01-02.md)中创建的项目。 38 | 39 | 项目结构如下: 40 | ![](./img/01-03/01.png) 41 | 42 | 图中红线下的文件是新增或者修改的文件。 43 | 44 | MySQL的JDBC封装`mysql-connector-java-**.jar`别忘了放到Libraries里。 45 | 46 | ### 源码 47 | 48 | **SelectController.java源码:** 49 | ```java 50 | package me.letiantian.controller; 51 | 52 | import java.util.List; 53 | import java.util.Map; 54 | import javax.servlet.http.HttpServletRequest; 55 | import javax.servlet.http.HttpServletResponse; 56 | import org.springframework.beans.factory.annotation.Autowired; 57 | import org.springframework.web.servlet.ModelAndView; 58 | import org.springframework.web.servlet.mvc.Controller; 59 | import org.springframework.jdbc.core.JdbcTemplate; 60 | 61 | import me.letiantian.service.UserService; 62 | 63 | public class SelectController implements Controller{ 64 | 65 | @Autowired 66 | private UserService userDao; 67 | 68 | @Autowired 69 | private JdbcTemplate jdbcTemplate; 70 | 71 | @Override 72 | public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { 73 | response.setContentType("text/html;charset=UTF-8"); 74 | ModelAndView mv = new ModelAndView(); 75 | 76 | List users = jdbcTemplate.queryForList("SELECT * FROM user"); 77 | mv.addObject("users", users); 78 | 79 | Map user1 = userDao.getUserById(1); 80 | mv.addObject("user1", user1); 81 | 82 | Map user2 = jdbcTemplate.queryForMap("SELECT * FROM user WHERE id=2"); 83 | mv.addObject("user2", user2); 84 | 85 | mv.addObject("message", "无错误信息"); 86 | mv.setViewName("select"); 87 | return mv; 88 | } 89 | 90 | } 91 | ``` 92 | 93 | **UserService.java源码: ** 94 | ```java 95 | package me.letiantian.service; 96 | 97 | import java.util.Map; 98 | import org.springframework.beans.factory.annotation.Autowired; 99 | import org.springframework.jdbc.core.JdbcTemplate; 100 | import org.springframework.stereotype.Service; 101 | 102 | @Service 103 | public class UserService { 104 | 105 | @Autowired 106 | private JdbcTemplate jdbcTemplate; 107 | 108 | public Map getUserById(int id) { 109 | 110 | Map user = jdbcTemplate.queryForMap("SELECT * FROM user WHERE id=?", new Object[] {id}); 111 | return user; 112 | 113 | } 114 | 115 | } 116 | ``` 117 | 118 | **select.jsp源码: ** 119 | ```html 120 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 121 | <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 122 | 123 | 124 | 125 | 126 | JSP Page 127 | 128 | 129 |

Hello World!

130 | 131 |
    132 | 133 |
  • 134 | 135 | : 136 | 137 | 138 |
  • 139 |
    140 |
141 |
142 | 143 |
144 | 145 | 146 |
    147 | 148 |
  • 149 | 150 | 151 |
  • 152 |
    153 |
154 |
155 | 156 |
157 | 158 | 159 |
    160 | 161 |
  • 162 | 163 | 164 |
  • 165 |
    166 |
167 |
168 |

${message}

169 | 170 | 171 | ``` 172 | 173 | **dispatcher-servlet.xml源码:** 174 | ```xml 175 | 176 | 177 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | indexController 196 | helloController 197 | selectController 198 | 199 | 200 | 201 | 202 | 206 | 207 | 210 | 211 | 213 | 214 | 216 | 217 | 218 | 219 | 220 | ``` 221 | 222 | 该文件中新增加了`selectController`,以及``以使得`@Autowired`能够工作。 223 | 224 | **applicationContext.xml源码:** 225 | ```xml 226 | 227 | 237 | 238 | 239 | 240 | 241 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | ``` 254 | `dataSource`设置程MySQL,并注入到`jdbcTemplate`。 255 | 256 | **运行项目,浏览器访问:** 257 | ![](./img/01-03/02.png) 258 | 259 | 260 | ## 资料 261 | **JdbcTemplate中的有多种查询方法,可以参考:** 262 | [JdbcTemplate 查询](http://www.cnblogs.com/shitianzeng/articles/2318995.html) 263 | [Spring JdbcTemplate方法详解](http://blog.csdn.net/dyllove98/article/details/7772463) 264 | 265 | **上面的JSP中用到了JSTL,以下几篇文件可以看一下:** 266 | [在JSTL EL中处理java.util.Map,及嵌套List的情况](http://my.oschina.net/letiantian/blog/511920) 267 | [JSP 标准标签库(JSTL)](http://www.runoob.com/jsp/jsp-jstl.html) 268 | 269 | **JdbcTemplate也可以使用事务,有声明式和编程式两种方法:** 270 | [Spring Declarative Transactions](http://www.simplespringtutorial.com/springDeclarativeTransactions.html) 271 | [Spring Programmatic Transactions](http://www.simplespringtutorial.com/springProgrammaticTransactions.html) 272 | [Spring Programmatic Transaction Management](http://www.tutorialspoint.com/spring/programmatic_management.htm) 273 | [Spring JdbcTemplate 与 事务管理](http://blog.sina.com.cn/s/blog_63f08ebf0100orev.html) 274 | [Transactions with JdbcTemplate](http://www.javacreed.com/transactions-with-jdbctemplate/) 275 | 276 | 277 | **如何使用连接池?** 278 | [JDBC Database connection pool in Spring FrameWork - How to SetUp Example](http://javarevisited.blogspot.jp/2012/06/jdbc-database-connection-pool-in-spring.html) 279 | [Setup Connection Pooling in Spring MVC](http://stackoverflow.com/questions/3552831/setup-connection-pooling-in-spring-mvc) 280 | 281 | **其他:** 282 | [Spring MVC with JdbcTemplate Example](http://www.codejava.net/frameworks/spring/spring-mvc-with-jdbctemplate-example) 283 | [Spring MVC and List Example](http://www.mkyong.com/spring-mvc/spring-mvc-and-list-example/) 284 | -------------------------------------------------------------------------------- /00-04.md: -------------------------------------------------------------------------------- 1 | 00-04、理解Servlet 2 | --- 3 | 4 | 本文依然使用[00-03、从JSP开始](./00-03.md)中创建的项目HelloJSP。 5 | 6 | 本文主要有以下内容: 7 | 8 | * 如何使用Servlet编写Hello Servlet 9 | * 如何将Servlet与URL对应起来 10 | * Servlet如何调用JSP 11 | * Servlet如何返回JSON数据 12 | * 如何编写一个Dispatcher 13 | 14 | 15 | ## Hello Servlet 16 | 项目结构如下: 17 | 18 | ![](./img/00-04/01.png) 19 | 20 | `HelloServlet.java`内容如下: 21 | ```java 22 | package me.letiantian.servlet; 23 | 24 | import java.io.IOException; 25 | import java.io.PrintWriter; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.http.HttpServlet; 28 | import javax.servlet.http.HttpServletRequest; 29 | import javax.servlet.http.HttpServletResponse; 30 | 31 | public class HelloServlet extends HttpServlet { 32 | 33 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 34 | throws ServletException, IOException { 35 | response.setContentType("text/html;charset=UTF-8"); 36 | try (PrintWriter out = response.getWriter()) { 37 | out.println(""); 38 | out.println(""); 39 | out.println(""); 40 | out.println("Servlet HelloServlet"); 41 | out.println(""); 42 | out.println(""); 43 | out.println("

Servlet HelloServlet at " + request.getContextPath() + "

"); 44 | out.println(""); 45 | out.println(""); 46 | } 47 | } 48 | 49 | @Override 50 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 51 | throws ServletException, IOException { 52 | processRequest(request, response); 53 | } 54 | 55 | @Override 56 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 57 | throws ServletException, IOException { 58 | processRequest(request, response); 59 | } 60 | 61 | } 62 | 63 | ``` 64 | HTTP最常见的方法是GET和POST,在一个Servlet中对应的处理方法分别是doGet()和doPost()。 65 | `response.setContentType("text/html;charset=UTF-8");` 用来设置HTTP响应头中的Content-Type。 66 | PrintWriter对象out的输出内容则是响应正文。 67 | 68 | 69 | `web.xml`内容如下: 70 | ```xml 71 | 72 | 73 | 74 | HelloServlet 75 | me.letiantian.servlet.HelloServlet 76 | 77 | 78 | HelloServlet 79 | /HelloServlet 80 | 81 | 82 | 83 | 30 84 | 85 | 86 | 87 | ``` 88 | 在这个配置中,`me.letiantian.servlet.HelloServlet`与URL`/HelloServlet`对应。 89 | `session-timeout`设置了session的有效时间,单位是分钟(不过目前的程序里还没用过session)。 90 | 91 | 浏览器访问`http://127.0.0.1:8084/HelloJSP`会显示404;访问`http://127.0.0.1:8084/HelloJSP/HelloServlet`会显示`Servlet HelloServlet at /HelloJSP`,这也正是`me.letiantian.servlet.HelloServlet`输出的HTML的渲染结果。 92 | 93 | 94 | ### 也可以使用注解将Servlet和URL对应起来 95 | 96 | 首先清空web.xml中关于URL的配置,web.xml最终内容如下: 97 | ```xml 98 | 99 | 100 | 101 | 102 | 103 | 30 104 | 105 | 106 | 107 | 108 | 109 | ``` 110 | 然后对`me.letiantian.servlet.HelloServlet`类略做修改: 111 | ```java 112 | package me.letiantian.servlet; 113 | 114 | import java.io.IOException; 115 | import java.io.PrintWriter; 116 | import javax.servlet.ServletException; 117 | import javax.servlet.http.HttpServlet; 118 | import javax.servlet.http.HttpServletRequest; 119 | import javax.servlet.http.HttpServletResponse; 120 | 121 | import javax.servlet.annotation.WebServlet; 122 | 123 | @WebServlet("/HelloServlet") 124 | public class HelloServlet extends HttpServlet { 125 | // ...... 126 | } 127 | ``` 128 | 129 | 重新启动项目,浏览器访问效果和之前是相同的。 130 | 131 | 132 | ## Servlet调用JSP 133 | 改写me.letiantian.servlet.HelloServlet类,内容如下: 134 | 135 | ```java 136 | package me.letiantian.servlet; 137 | 138 | import java.io.IOException; 139 | import java.io.PrintWriter; 140 | import javax.servlet.RequestDispatcher; 141 | import javax.servlet.ServletException; 142 | import javax.servlet.http.HttpServlet; 143 | import javax.servlet.http.HttpServletRequest; 144 | import javax.servlet.http.HttpServletResponse; 145 | 146 | import javax.servlet.annotation.WebServlet; 147 | 148 | @WebServlet("/HelloServlet") 149 | public class HelloServlet extends HttpServlet { 150 | 151 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 152 | throws ServletException, IOException { 153 | response.setContentType("text/html;charset=UTF-8"); 154 | request.setAttribute("title", "Hello Servlet"); 155 | request.setAttribute("content", "你好"); 156 | RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp"); 157 | rd.forward(request, response); 158 | } 159 | 160 | @Override 161 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 162 | throws ServletException, IOException { 163 | processRequest(request, response); 164 | } 165 | 166 | @Override 167 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 168 | throws ServletException, IOException { 169 | processRequest(request, response); 170 | } 171 | 172 | } 173 | ``` 174 | 175 | 在`WEB-INF/`下创建目录`jsp`,然后在`jsp`目录下新建`hello.jsp`,内容如下: 176 | ```html 177 | 178 | 179 | 180 | 181 | ${title} 182 | 183 | 184 |

${content}

185 | 186 | 187 | ``` 188 | 189 | 重启该项目,访问`http://127.0.0.1:8084/HelloJSP/HelloServlet`: 190 | ```plain 191 | $ curl -i http://127.0.0.1:8084/HelloJSP/HelloServlet 192 | HTTP/1.1 200 OK 193 | Server: Apache-Coyote/1.1 194 | Set-Cookie: JSESSIONID=7CCCFD5467F8330066F827623802FB23; Path=/HelloJSP/; HttpOnly 195 | Content-Type: text/html;charset=UTF-8 196 | Content-Length: 215 197 | Date: Fri, 18 Sep 2015 08:09:58 GMT 198 | 199 | 200 | 201 | 202 | 203 | 204 | Hello Servlet 205 | 206 | 207 |

你好

208 | 209 | 210 | ``` 211 | 212 | ## CSS等静态文件放在什么地方 213 | 在项目下建立static目录,再这个目录下添加`test.js`,内容如下: 214 | ``` 215 | console.log("hello world"); 216 | ``` 217 | 在`web.xml`添加以下内容: 218 | ```xml 219 | 220 | default 221 | *.jpg 222 | 223 | 224 | 225 | default 226 | *.png 227 | 228 | 229 | 230 | default 231 | *.js 232 | 233 | 234 | 235 | default 236 | *.css 237 | 238 | ``` 239 | 此时,项目结构如下: 240 | ![](./img/00-04/02.png) 241 | 242 | 启动项目,访问 243 | ```plain 244 | $ curl -i http://localhost:8084/HelloJSP/static/test.js 245 | HTTP/1.1 200 OK 246 | Server: Apache-Coyote/1.1 247 | Accept-Ranges: bytes 248 | ETag: W/"27-1442566151000" 249 | Last-Modified: Fri, 18 Sep 2015 08:49:11 GMT 250 | Content-Type: application/javascript 251 | Content-Length: 27 252 | Date: Fri, 18 Sep 2015 08:58:00 GMT 253 | 254 | console.log("hello world"); 255 | ``` 256 | 257 | ```plain 258 | $ curl -i http://localhost:8084/HelloJSP/static/test.js?time=123 259 | HTTP/1.1 200 OK 260 | Server: Apache-Coyote/1.1 261 | Accept-Ranges: bytes 262 | ETag: W/"27-1442566151000" 263 | Last-Modified: Fri, 18 Sep 2015 08:49:11 GMT 264 | Content-Type: application/javascript 265 | Content-Length: 27 266 | Date: Fri, 18 Sep 2015 08:58:09 GMT 267 | 268 | console.log("hello world"); 269 | ``` 270 | 271 | ## Servlet如何返回JSON数据 272 | 273 | 将 274 | ``` 275 | response.setContentType("text/html;charset=UTF-8"); 276 | ``` 277 | 修改为 278 | ``` 279 | response.setContentType("application/json;charset=UTF-8"); 280 | ``` 281 | 。 282 | 283 | `out.println`输出JSON格式的字符串即可。 284 | 285 | 286 | ## 编写Dispatcher 287 | 基于以上的学习,已经可以编写一个分发器了。 288 | 将HelloServlet.java修改为DispatcherServlet.java,内容修改为: 289 | ```java 290 | package me.letiantian.servlet; 291 | 292 | import java.io.IOException; 293 | import java.io.PrintWriter; 294 | import javax.servlet.ServletException; 295 | import javax.servlet.http.HttpServlet; 296 | import javax.servlet.http.HttpServletRequest; 297 | import javax.servlet.http.HttpServletResponse; 298 | 299 | import javax.servlet.annotation.WebServlet; 300 | 301 | @WebServlet("/") 302 | public class HelloServlet extends HttpServlet { 303 | 304 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 305 | throws ServletException, IOException { 306 | response.setContentType("text/plain;charset=UTF-8"); 307 | try (PrintWriter out = response.getWriter()) { 308 | out.println("context: " + request.getContextPath()); 309 | out.println("request uri: " + request.getRequestURI()); 310 | out.println("params: " + request.getParameterMap()); 311 | } 312 | } 313 | 314 | @Override 315 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 316 | throws ServletException, IOException { 317 | processRequest(request, response); 318 | } 319 | 320 | @Override 321 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 322 | throws ServletException, IOException { 323 | processRequest(request, response); 324 | } 325 | 326 | } 327 | ``` 328 | 运行项目,访问结果如下: 329 | ``` 330 | $ curl http://localhost:8084/HelloJSP/user 331 | context: /HelloJSP 332 | request uri: /HelloJSP/user 333 | params: {} 334 | ``` 335 | ``` 336 | $ curl http://localhost:8084/HelloJSP/user?name=letian 337 | context: /HelloJSP 338 | request uri: /HelloJSP/user 339 | params: {name=[Ljava.lang.String;@49ea47b4} 340 | ``` 341 | ``` 342 | $ curl http://localhost:8084/HelloJSP/static/test.js 343 | console.log("hello world"); 344 | ``` 345 | (这个代码并没什么用~) 346 | 347 | 从这段代码中可以看到,我们可以通过request对象得到HTTP请求信息,特别是request URI。在这个程序的基础上,我们 348 | 可以继续扩充它,使得其遇到某个URI,就调用指定的处理函数。慢慢地补充,一个框架就出来了。 349 | 350 | 351 | ## 资料 352 | 353 | 本节中,JSP使用了表达式语言,可以参考: 354 | [JSTL 入门: 表达式语言](http://www.ibm.com/developerworks/cn/java/j-jstl0211/index.html) 355 | [JSP 表达式语言](http://www.runoob.com/jsp/jsp-expression-language.html) 356 | -------------------------------------------------------------------------------- /01-06.md: -------------------------------------------------------------------------------- 1 | 01-06、校验器 2 | --- 3 | 4 | 校验器,Validator。 5 | 6 | 在处理带有表单数据的HTTP请求时,通常这样做: 7 | 8 | ```plain 9 | if (表单数据符合要求) { 10 | 处理数据,返回结果; 11 | } else { 12 | 返回结果,提示用户重新输入数据; 13 | } 14 | ``` 15 | 16 | 判断表单数据是否符合要求这就是校验器该做的事情。我们可以自己编写校验类,也可以使用Spring MVC自带的相关类。 17 | 18 | ## 将表单数据绑定到对象中 19 | 项目结构如下: 20 | ![](./img/01-06/01.png) 21 | 22 | ### 源码 23 | **web.xml** 24 | ```xml 25 | 26 | 27 | 28 | contextConfigLocation 29 | /WEB-INF/applicationContext.xml 30 | 31 | 32 | org.springframework.web.context.ContextLoaderListener 33 | 34 | 35 | dispatcher 36 | org.springframework.web.servlet.DispatcherServlet 37 | 2 38 | 39 | 40 | dispatcher 41 | / 42 | 43 | 44 | 45 | 30 46 | 47 | 48 | 49 | redirect.jsp 50 | 51 | 52 | ``` 53 | 54 | **applicationContext.xml** 55 | ```xml 56 | 57 | 65 | 66 | 67 | ``` 68 | 69 | **dispatcher-servlet.xml** 70 | ```xml 71 | 72 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | indexController 94 | 95 | 96 | 97 | 98 | 102 | 103 | 106 | 107 | 108 | ``` 109 | 110 | **hello/input.jsp** 111 | ```html 112 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 113 | 114 | 115 | 116 | 117 | JSP Page 118 | 119 | 120 |
121 | First Name:
122 | Second Name:
123 | 124 | 125 |
126 | 127 | 128 | ``` 129 | 130 | **hello/output.jsp** 131 | ```plain 132 | <%@page contentType="text/plain" pageEncoding="UTF-8"%> 133 | first name: ${person.firstName} 134 | second name: ${person.secondName} 135 | ``` 136 | 137 | **Person.java** 138 | ```java 139 | package me.letiantian.form; 140 | 141 | public class Person { 142 | 143 | private String firstName; 144 | private String secondName; 145 | 146 | public String getFirstName() { 147 | return firstName; 148 | } 149 | 150 | public void setFirstName(String firstName) { 151 | this.firstName = firstName; 152 | } 153 | 154 | public String getSecondName() { 155 | return secondName; 156 | } 157 | 158 | public void setSecondName(String secondName) { 159 | this.secondName = secondName; 160 | } 161 | 162 | } 163 | ``` 164 | 165 | **HelloController.java** 166 | ```java 167 | package me.letiantian.controller; 168 | 169 | import java.io.PrintWriter; 170 | import javax.servlet.http.HttpServletRequest; 171 | import javax.servlet.http.HttpServletResponse; 172 | import org.springframework.stereotype.Controller; 173 | import org.springframework.ui.Model; 174 | import org.springframework.web.bind.annotation.PathVariable; 175 | import org.springframework.web.bind.annotation.RequestMapping; 176 | import org.springframework.web.bind.annotation.RequestMethod; 177 | 178 | import me.letiantian.form.Person; 179 | import org.springframework.web.bind.annotation.ModelAttribute; 180 | 181 | @Controller 182 | @RequestMapping("/hello") 183 | public class HelloController{ 184 | 185 | @RequestMapping(value = "") 186 | public String index() { 187 | return "index"; 188 | } 189 | 190 | @RequestMapping(value = "/input") 191 | public String input() { 192 | return "hello/input"; 193 | } 194 | 195 | @RequestMapping(value = "/output") 196 | public String output(Person person, Model model) { 197 | model.addAttribute("person", person); 198 | return "hello/output"; 199 | } 200 | 201 | // @RequestMapping(value = "/output") 202 | // public String output(@ModelAttribute(value="person") Person person, Model model) { 203 | // return "hello/output"; 204 | // } 205 | 206 | } 207 | ``` 208 | 209 | 注意, 210 | ```java 211 | @RequestMapping(value = "/output") 212 | public String output(Person person, Model model) { 213 | model.addAttribute("person", person); 214 | return "hello/output"; 215 | } 216 | ``` 217 | 和 218 | ```java 219 | @RequestMapping(value = "/output") 220 | public String output(@ModelAttribute(value="person") Person person, Model model) { 221 | return "hello/output"; 222 | } 223 | ``` 224 | 225 | 是一样的。 226 | 227 | ### 浏览器访问 228 | 229 | 表单填入信息: 230 | ![](./img/01-06/02.png) 231 | 提交表单后: 232 | ![](./img/01-06/03.png) 233 | 234 | 235 | ## 校验数据 236 | 237 | ### hello/input.jsp修改如下 238 | ```html 239 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 240 | <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 241 | <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 242 | 243 | 244 | 245 | 246 | JSP Page 247 | 250 | 251 | 252 | 253 | 254 | firstName:
255 | secondName:
256 | 257 |
258 | 259 | 260 | 261 | ``` 262 | 263 | 注意` type) { 325 | return Person.class.isAssignableFrom(type); 326 | } 327 | 328 | @Override 329 | public void validate(Object o, Errors errors) { 330 | Person person = (Person) o; 331 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", null, "firstName不能为空"); 332 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "secondName", null, "secondName不能为空"); 333 | if (person.getFirstName().length() < 2) { 334 | errors.rejectValue("firstName", null, "firstName太短"); 335 | } 336 | } 337 | } 338 | ``` 339 | 340 | ### 测试 341 | **第1组:** 342 | 343 | ![](./img/01-06/04.png) 344 | ![](./img/01-06/05.png) 345 | 346 | **第2组:** 347 | 348 | ![](./img/01-06/06.png) 349 | ![](./img/01-06/07.png) 350 | 351 | **第3组:** 352 | 353 | ![](./img/01-06/08.png) 354 | ![](./img/01-06/09.png) 355 | -------------------------------------------------------------------------------- /01-02.md: -------------------------------------------------------------------------------- 1 | 01-02、使用Spring MVC构建Hello World 2 | --- 3 | 4 | 本文演示如何使用Spring MVC做出最简单的Hello World应用。 5 | 6 | ## 示例1 7 | 项目创建和之前一样,不过在最后一步要选择Spring Web MVC: 8 | ![](./img/01-02/01.png) 9 | 10 | 项目结构如下: 11 | ![](./img/01-02/02.png) 12 | 13 | ### web.xml源码: 14 | ```xml 15 | 16 | 17 | 18 | contextConfigLocation 19 | /WEB-INF/applicationContext.xml 20 | 21 | 22 | 23 | org.springframework.web.context.ContextLoaderListener 24 | 25 | 26 | 27 | dispatcher 28 | org.springframework.web.servlet.DispatcherServlet 29 | 2 30 | 31 | 32 | 33 | dispatcher 34 | *.htm 35 | 36 | 37 | 38 | 39 | 30 40 | 41 | 42 | 43 | redirect.jsp 44 | 45 | 46 | ``` 47 | 如果遇到匹配*.htm的URL,会使用`org.springframework.web.servlet.DispatcherServlet`来处理。 48 | 49 | ### applicationContext.xml源码: 50 | ```xml 51 | 52 | 53 | 61 | 62 | 72 | 73 | 74 | 75 | 76 | ``` 77 | applicationContext.xml是Spring的配置文件。 78 | 79 | ### dispatcher-servlet.xml源码: 80 | ```xml 81 | 82 | 83 | 91 | 92 | 93 | 94 | 99 | 100 | 101 | 102 | indexController 103 | 104 | 105 | 106 | 107 | 111 | 112 | 115 | 118 | 119 | 120 | ``` 121 | 122 | 在``定义了JSP模板文件的位置和后缀(这样其他地方就可以省略后缀了)。 123 | 124 | URL为`index.htm`时,对应的控制器是`indexController`,其调用了`/WEB-INF/jsp/`下的模板`index.jsp`。 125 | 126 | ### redirect.jsp源码 127 | ```jsp 128 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 129 | <% response.sendRedirect("index.htm"); %> 130 | ``` 131 | 132 | ### index.jsp源码 133 | 134 | ```html 135 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 136 | 138 | 139 | 140 | 141 | 142 | Welcome to Spring Web MVC project 143 | 144 | 145 | 146 |

Hello! This is the default welcome page for a Spring Web MVC project.

147 |

To display a different welcome page for this project, modify 148 | index.jsp , or create your own welcome page then change 149 | the redirection in redirect.jsp to point to the new 150 | welcome page and also update the welcome-file setting in 151 | web.xml.

152 | 153 | 154 | ``` 155 | 156 | 运行项目,打开浏览器访问`http://localhost:8084/Project_0102/`,会自动跳转到 157 | `http://localhost:8084/Project_0102/index.htm`,并显示`index.jsp`的内容。 158 | 159 | ### Hello World 160 | 修改dispatcher-servlet.xml,将``修改为: 161 | ```xml 162 | 163 | 164 | 165 | indexController 166 | helloController 167 | 168 | 169 | 170 | ``` 171 | 172 | 并添加: 173 | ```xml 174 | 176 | ``` 177 | 178 | HelloController.java的源码如下: 179 | ```java 180 | package me.letiantian.controller; 181 | 182 | import javax.servlet.http.HttpServletRequest; 183 | import javax.servlet.http.HttpServletResponse; 184 | import org.springframework.web.servlet.ModelAndView; 185 | import org.springframework.web.servlet.mvc.Controller; 186 | 187 | public class HelloController implements Controller{ 188 | 189 | @Override 190 | public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { 191 | 192 | ModelAndView mv = new ModelAndView(); 193 | mv.addObject("message", "Hello World!你好"); 194 | mv.setViewName("hello"); 195 | return mv; 196 | } 197 | 198 | } 199 | ``` 200 | 模板`hello.jsp`的源码如下: 201 | ```html 202 | 203 | 204 | Hello world 205 | 206 | 207 |

${message}

208 | 209 | 210 | ``` 211 | 212 | 创建static目录,在static目录下创建test.js,内容如下: 213 | ```js 214 | console.log("hello world"); 215 | ``` 216 | 217 | 在`web.xml`中添加: 218 | ```xml 219 | 220 | default 221 | *.jpg 222 | 223 | 224 | 225 | default 226 | *.png 227 | 228 | 229 | 230 | default 231 | *.js 232 | 233 | 234 | 235 | default 236 | *.css 237 | 238 | ``` 239 | 240 | 好了,现在的项目结构如下: 241 | ![](./img/01-02/03.png) 242 | 243 | 浏览器访问结果: 244 | ![](./img/01-02/04.png) 245 | ![](./img/01-02/05.png) 246 | 247 | 乱码了~囧~ 248 | 249 | **解决方法:** 250 | 在`HelloController.java`加入`response.setContentType("text/html;charset=UTF-8");`: 251 | ```java 252 | package me.letiantian.controller; 253 | 254 | import javax.servlet.http.HttpServletRequest; 255 | import javax.servlet.http.HttpServletResponse; 256 | import org.springframework.web.servlet.ModelAndView; 257 | import org.springframework.web.servlet.mvc.Controller; 258 | 259 | public class HelloController implements Controller{ 260 | 261 | @Override 262 | public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { 263 | response.setContentType("text/html;charset=UTF-8"); // 新加入的内容 264 | ModelAndView mv = new ModelAndView(); 265 | mv.addObject("message", "Hello World!你好"); 266 | mv.setViewName("hello"); 267 | return mv; 268 | } 269 | 270 | } 271 | ``` 272 | 273 | 274 | ## 示例2 275 | 276 | ### 换种方法配置静态资源 277 | 278 | 删掉在`web.xml`中的: 279 | ```xml 280 | 281 | default 282 | *.jpg 283 | 284 | 285 | 286 | default 287 | *.png 288 | 289 | 290 | 291 | default 292 | *.js 293 | 294 | 295 | 296 | default 297 | *.css 298 | 299 | ``` 300 | 在`dispatcher-servlet.xml`中增加以下内容: 301 | ```xml 302 | 303 | 304 | 314 | 315 | 316 | 317 | 318 | 319 | ``` 320 | 321 | 注意,在beans的属性中增加了`xmlns:mvc="http://www.springframework.org/schema/mvc"`,属性`xsi:schemaLocation`中增加了`http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd`。 322 | 323 | 324 | ### 不再使用任何后缀(例如.html,.jsp) 325 | 326 | 将`redirect.jsp`修改为: 327 | ```jsp 328 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 329 | <% response.sendRedirect("index"); %> 330 | ``` 331 | 332 | 将web.xml中的: 333 | ```xml 334 | 335 | dispatcher 336 | *.htm 337 | 338 | ``` 339 | 340 | 修改为: 341 | ```xml 342 | 343 | dispatcher 344 | / 345 | 346 | ``` 347 | 348 | 将`dispatcher-servlet.xml`中的: 349 | ```xml 350 | 351 | 352 | 353 | indexController 354 | helloController 355 | 356 | 357 | 358 | ``` 359 | 360 | 修改为: 361 | ```xml 362 | 363 | 364 | 365 | indexController 366 | helloController 367 | 368 | 369 | 370 | ``` 371 | 372 | 然后,浏览器访问`http://localhost:8084/Project_0102/hello`。 373 | 374 | 375 | ## 示例3 376 | 377 | 这个示例展示如何获取URL中的数据。 378 | 379 | 修改`HelloController.java`: 380 | ```java 381 | package me.letiantian.controller; 382 | 383 | import javax.servlet.http.HttpServletRequest; 384 | import javax.servlet.http.HttpServletResponse; 385 | import org.springframework.web.servlet.ModelAndView; 386 | import org.springframework.web.servlet.mvc.Controller; 387 | 388 | public class HelloController implements Controller{ 389 | 390 | @Override 391 | public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { 392 | response.setContentType("text/html;charset=UTF-8"); 393 | ModelAndView mv = new ModelAndView(); 394 | mv.addObject("name", request.getParameter("name")); 395 | mv.setViewName("hello"); 396 | return mv; 397 | } 398 | 399 | } 400 | ``` 401 | 402 | 修改`hello.jsp`: 403 | ```html 404 | <%@page contentType="text/html" pageEncoding="UTF-8"%> 405 | 406 | 407 | Hello world 408 | 409 | 410 |

${pageContext.request.contextPath}

411 | 412 |
413 | 414 | 415 |
416 | 417 |

提交的数据: ${name}

418 | 419 | 420 | 421 | ``` 422 | `${pageContext.request.contextPath}`的输出是`/Project_0102`。 423 | 浏览器访问: 424 | ![](./img/01-02/06.png) 425 | 426 | 再编辑JSP文件时候遇到了这样的问题: 427 | 428 | > The header.jspf contains characters which will probably be damaged during 429 | conversion to the ISO-8859-1 character set. 430 | Do you want to save the file using this character set? 431 | 432 | 解决办法见[http://stackoverflow.com/questions/15499182/netbeans-forces-me-to-save-in-specific-encoding](http://stackoverflow.com/questions/15499182/netbeans-forces-me-to-save-in-specific-encoding)。 433 | 434 | ## 资料 435 | - [Chapter 13. Web MVC framework](http://docs.spring.io/spring/docs/2.5.x/reference/mvc.html) 436 | - [Spring MVC – How to include JS or CSS files in a JSP page](http://www.mkyong.com/spring-mvc/spring-mvc-how-to-include-js-or-css-files-in-a-jsp-page/) 437 | 438 | - [dispatcher-servlet.xml and application-context.xml的区别](http://stackoverflow.com/questions/4549034/dispatcher-servlet-xml-and-application-context-xml) 439 | 440 | - [Spring MVC SimpleUrlHandlerMapping example](http://www.mkyong.com/spring-mvc/spring-mvc-simpleurlhandlermapping-example/) 441 | - [springMVC中文乱码问题](http://blog.csdn.net/xuechongyang/article/details/8283924) 442 | - [SpringMVC 基于注解的Controller @RequestMapping @RequestParam.. ](http://blog.csdn.net/lufeng20/article/details/7598801) 443 | 444 | - urlMapping也可以通过注解来定义,例如[Spring 4 MVC Hello World Tutorial – Full Example](http://javahash.com/spring-4-mvc-hello-world-tutorial-full-example/)。 445 | --------------------------------------------------------------------------------