文章目录
- Spring
- SpringMVC
- 1、概述
- 2、DispatcherServlet
- 2.1 Servlet
- 2.1.1 简介
- 2.1.2 Servlet生命周期
- 2.1.3 Servlet实例
- 2.1.4 Servlet表单数据
- 2.1.5 Servlet 客户端 HTTP 请求
- 2.1.6 Servlet HTTP 状态码
- 2.1.7 Servlet 编写过滤器
- 2.1.8 Servlet 过滤器方法
- 2.1.9 Servlet Cookies 处理
- 2.1.10 Servlet Session 跟踪
- 2.1.11 Servlet 处理日期
- 2.1.12 Servlet 网页重定向
- 2.1.13 Servlet 点击计数器
- 2.1.15 Servlet 自动刷新页面
- 2.1.16 Servlet 发送电子邮件
- 2.1.17 Servlet 包
- 2.1.18 Servlet 调试
- 2.1.19 Servlet 国际化
- 2.1.20 Servlet容器
- 2.2 DispatcherServlet
- 3、模板引擎
- 4、组件整合
- 5、SSO单点登录
- SpringBoot
- SpringCloud
- RPC框架
- ZooKeeper
- Eurka
- Web服务器
- 开源项目
- 参考
Spring
1、概述
2、IoC
2.1 概述
控制反转(Inversion of Control,IoC),是面向对象编程中的一种设计原则,可用来减低代码间的耦合度。其中最常见的方式叫做依赖注入(Dependendcy Injection, DI),还有一种方式叫“依赖查找”(Dependency Lookup, DL)。
- DI是Spring框架使用的方式,容器负责组件的装配;
- DL已经被抛弃。
![](https://i-blog.csdnimg.cn/blog_migrate/95dab8779e3c24f2e589e455a894802d.png)
2.2 IoC容器原理
IoC容器本质上是一个工厂,Spring用其管理所有的对象以及依赖关系。
- IoC容器原理是依靠Java反射实现的,通过反射可以获取类的所有信息(成员变量、类名等);
- 通过配置文件(xml)或者注解来描述类与类之间的关系;
- 通过这些配置信息和反射技术来构建出对应的对象和依赖关系。
Spring IoC容器实现对象的创建和依赖的过程:
- 根据 Bean 配置信息在容器内部创建 Bean 定义注册表;
- 根据注册表加载、实例化 bean、建立 Bean 与 Bean 之间的依赖关系;
- 将准备就绪的 Bean 放到 Map 缓存池中,等待应用程序调用
Spring IoC 容器(Bean 工厂)可简单分成两种:
- BeanFactory(提供了OC容器最基本的形式);
- ApplicationContext(Spring中的高级容器,可以获取容器中的各种bean组件,注册监听事件,加载资源文件等功能)。
2.3 注入方式
2.3.1 构造器注入
2.3.2 setter注入
2.4 循环依赖问题
Spring 为了解决单例的循环依赖问题,使用了三级缓存。其中一级缓存为单例池(singletonObjects),二级缓存为提前曝光对象(earlySingletonObjects),三级缓存为提前曝光对象工厂(singletonFactories)。 假设A、B循环引用,实例化 A 的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了 B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖 A,这时候从缓存中查找到早期暴露的 A,没有 AOP 代理的话,直接将 A 的原始对象注入 B,完成 B 的初始化后,进行属性填充和初始化,这时候 B 完成后,就去完成剩下的 A 的步骤,如果有 AOP 代理,就进行 AOP 处理获取代理后的对象 A,注入 B,走剩下的流程。
3、AOP
3.1 概述
AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。
-
AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;
-
而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
一般来说,编译时增强的 AOP 框架在性能上更有优势,因为运行时动态增强的 AOP 框架需要每次运行时都进行动态增强。
3.2 AOP框架
3.2.1 AspectJ
3.2.2 Spring AOP
4、事务管理
5、常用注解
5.1 核心注解
@Require
@Autowired
@Qualifier
@Configuration
@Bean
@ComponentScan
@Lazy
@Value
5.2 SpringMVC和RestFul注解
@Controller
@RequestMapping
@GetMapping
@PostMapping
@PutMapping
@PatchMapping
@DeleteMapping
@CookieValue
@CrossOrigin
@ExceptionHandler
@InitBinder
@MatrixVariable
@PathVariable
@RequestBody
@RequestParam
@RequestPart
@ResponseBody
@ResponseStatus
@ControllerAdvice
@RestController
@SessionAttribute
@SessionAttributes
5.3 SpringBoot注解
@EnableAutoConfiguration
@SpringBootApplication
@Configuration
@EnableAutoConfiguration
@ComponentScan
5.4 stereotype注解
@Component
@Controller
@Service
@Repository
5.5 数据访问注解
@Transactional
5.6 任务执行和调度注解
@Scheduled
@Async
5.7 测试注解
@ContextConfiguration
SpringMVC
1、概述
1.1 简介
1.2 SpringMVC项目结构介绍
1.3、创建SpringMVC项目
1.3.1 选择Maven,以maven-archtype-webapp为模板,创建项目
![image-20211010105836650](https://gitee.com/wuyang556/pic-bed/raw/master//bookwarm/avatar/image-20211010105836650.png)
1.3.2 POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wuy</groupId>
<artifactId>myName</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>myName Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!--j2ee相关包 servlet、jsp、jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 辅助工具类-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.7</version>
</dependency>
<!-- 日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>myName</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
1.3.3 添加项目文件目录,并标记
1.3.4 右键项目,添加SpringMVC框架
![image-20211010110424453](https://gitee.com/wuyang556/pic-bed/raw/master//bookwarm/avatar/image-20211010110424453.png)
![在这里插入图片描述](https://gitee.com/wuyang556/pic-bed/raw/master//bookwarm/avatar/20190610151238811.png)
完成上述操作后,会在webapp/WEB-INF目录下,自动生成applicationContext.xml和dispatcher-servlet.xml配置文件。
1.3.5 配置文件
先修改web.xml文件中的关键信息
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Application</display-name>
<!--welcome pages-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!--配置springmvc DispatcherServlet-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--配置dispatcher.xml作为mvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<!--<servlet>-->
<!--<servlet-name>EmpServlet</servlet-name>-->
<!--<servlet-class>com.gx.filter.EmpServlet</servlet-class>-->
<!--</servlet>-->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--把applicationContext.xml加入到配置文件中-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
修改applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.wuy"/>
</beans>
再修改dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--此文件负责整个mvc中的配置-->
<!--启用spring的一些annotation -->
<context:annotation-config/>
<!-- 配置注解驱动 可以将request参数与绑定到controller参数上 -->
<mvc:annotation-driven/>
<!--静态资源映射-->
<!--本项目把静态资源放在了webapp的statics目录下,资源映射如下-->
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/image/**" location="/static/images/"/>
<mvc:default-servlet-handler /> <!--这句要加上,要不然可能会访问不到静态资源,具体作用自行百度-->
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP-->
<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/><!--设置JSP文件的目录位置-->
<property name="suffix" value=".jsp"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>
<!-- 自动扫描装配 -->
<context:component-scan base-package="com.wuy"/>
</beans>
1.3.6 配置Tomcat,然后启动项目,测试是否成功
2、DispatcherServlet
2.1 Servlet
2.1.1 简介
Servlet 是运行在带有支持 Java Servlet 规范的解释器的 web 服务器上的 Java 类。Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建。
Servlet环境配置
开发环境指的是开发、测试、运行 Servlet程序 的地方。和任何其他的 Java 程序一样,需要通过 Java 编译器 javac 编译 Servlet。在编译 Servlet 应用程序后,可将其部署在配置的环境中以便测试和运行。
设置Web服务器
市面上有许多 Web 服务器支持 Servlet。有些 Web 服务器可以免费使用,Apache Tomcat 就是其中之一。Apache Tomcat 基于 Java Servlet 和 JavaServer Pages 实现,可以作为测试 Servlet 的独立服务器,且可以被集成到 Apache Web 服务器这种。
下面是在电脑上安装 Tomcat 的步骤:
- 从 http://tomcat.apache.org/ 网站下载特定版本的 Tomcat;
- 将下载的压缩包,解压缩到一个方便的位置。例如,如果使用操作系统的是 Windows,可将Tomcat解压缩到 C:\apache-tomcat-5.5.29目录下,如果使用的是 Linux/Unix,则解压缩到 /usr/local/apache-tomcat-5.5.29 目录下,并创建 CATALINA_HOME 环境变量指向这些位置。
在 Windows 上,可以通过执行下面的命令来启动 Tomcat:
%CATALINA_HOME%\bin\startup.bat
2.1.2 Servlet生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 通过调用 init () 方法完成初始化;
- Servlet 调用 service() 方法来处理客户端的请求;
- Servlet 通过调用 destroy() 方法终止(结束);
- 最后,Servlet 被 JVM 的 GC 垃圾回收的。
init() 方法
init 方法被设计成只调用一次,其在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但也可以指定 Servlet 在服务器第一次启动时被加载。
当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
init 方法的定义如下:
public void init() throws ServletException {
// 初始化代码...
}
service()方法
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
下面是该方法的特征:
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException{}
service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。因此,不需要对 service() 方法做任何修改,只需要根据来自客户端的请求类型来重载 doGet() 或 doPost() 即可。
doGet() 和 doPost() 方法是每次服务请求中最常用的方法,下面将介绍这两种方法的特征。
doGet() 方法
GET 请求来自于一个 URL 的正常请求,或者来自于一个未指定 METHOD 的 HTML 表单,它由 doGet() 方法处理。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet 代码}
doPost() 方法
POST 请求来自于一个特别指定了 METHOD 为 POST 的 HTML 表单,它由 doPost() 方法处理。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet 代码}
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让 Servlet 关闭数据库连接、停止后台线程、将Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。destroy 方法定义如下所示:
public void destroy() { // 终止化代码... }
下图显示了一个典型的 Servlet 生命周期方案。
- 第一个到达服务器的 HTTP 请求被委派到 Servlet 容器;
- Servlet 容器在调用 service() 方法之前加载 Servlet;
- 然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。
以下为生命周期的一个简单实例:
//servlet生命周期,的三个方法,1.被创建,执行且只执行一次init方法,2.提供服务,执行service方法,执行多次 3.被销毁,当Servlet服务器正常关闭时,执行destroy方法,只执行一次。
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(req, resp);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
super.destroy();
}
2.1.3 Servlet实例
Servlet 是服务 HTTP 请求并实现 javax.servlet.Servlet 接口的 Java 类。Web 应用程序开发人员通常编写 Servlet 来扩展 javax.servlet.http.HttpServlet,并实现 Servlet 接口的抽象类专门用来处理 HTTP 请求。
下面是 Servlet 输出 Hello World 的示例源代码:
// 导入必需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
// 扩展 HttpServlet 类
public class HelloWorld extends HttpServlet {
private String message;
public void init() throws ServletException
{
// 执行必需的初始化
message = "Hello World";
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 设置响应内容类型
response.setContentType("text/html");
// 实际的逻辑是在这里
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}
public void destroy()
{
// 什么也不做
}
}
将上面的代码写在 HelloWorld.java 文件中,把这个文件放在 C:\ServletDevel(在 Windows 上)或 /usr/ServletDevel(在 UNIX 上)中,还需要把这些目录添加到 CLASSPATH 中。
假设环境已经被正确设置,进入 ServletDevel 目录,并编译 HelloWorld.java,如下所示:
javac HelloWorld.java
如果 Servlet 依赖于任何其他库,则必须在 CLASSPATH 中包含那些 JAR 文件。
部署Servlet
默认情况下,Servlet 应用程序位于路径 <Tomcat-installation-directory>/webapps/ROOT 下,且类文件放在 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中。
假设有一个完全合格的类名称 com.myorg.MyServlet,那么这个 Servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中。
现在,让我们把 HelloWorld.class 复制到 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中,并在位于 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目:
<servlet> <servlet-name>HelloWorld</servlet-name> <servlet-class>HelloWorld</servlet-class></servlet><servlet-mapping> <servlet-name>HelloWorld</servlet-name> <url-pattern>/HelloWorld</url-pattern></servlet-mapping>
上面的条目要被创建在 web.xml 文件中的 <web-app>…</web-app> 标签内。在该文件中可能已经有各种可用的条目,但不要在意。
到这里,您基本上已经完成了,现在让我们使用 <Tomcat-installation-directory>\bin\startup.bat(在 Windows 上)或 <Tomcat-installation-directory>/bin/startup.sh(在 Linux/Solaris 等上)启动 tomcat 服务器,最后在浏览器的地址栏中输入 http://localhost:8080/HelloWorld。如果一切顺利,您会看到下面的结果:
2.1.4 Servlet表单数据
很多情况下,需要传递一些信息,从浏览器到 Web 服务器,最终到后台程序。浏览器使用两种方法可将这些信息传递到 Web 服务器,分别为 GET 方法和 POST 方法。
GET 方法
GET 方法向页面请求发送已编码的用户信息。页面和已编码的信息中间用 ? 字符分隔,如下所示:
http://www.test.com/hello?key1=value1&key2=value2
GET 方法是默认的从浏览器向 Web 服务器传递信息的方法,它会产生一个很长的字符串,出现在浏览器的地址栏中。如果要向服务器传递的是密码或其他的敏感信息,请不要使用 GET 方法(GET 方法有大小限制,请求字符串中最多只能有 1024 个字符)。
这些信息使用 QUERY_STRING 头传递,并可以通过 QUERY_STRING 环境变量访问,Servlet 使用 doGet() 方法处理这种类型的请求。
POST 方法
另一个向后台程序传递信息的比较可靠的方法是 POST 方法。POST 方法打包信息的方式与 GET 方法基本相同,但是 POST 方法不是把信息作为 URL 中 ? 字符后的文本字符串进行发送,而是把这些信息作为一个单独的消息。消息以标准输出的形式传到后台程序,您可以解析和使用这些标准输出。Servlet 使用 doPost() 方法处理这种类型的请求。
使用 Servlet 读取表单数据
Servlet 处理表单数据,这些数据会根据不同的情况使用不同的方法自动解析:
- getParameter():可以调用 request.getParameter() 方法来获取表单参数的值;
- getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框;
- getParameterNames():若想要得到当前请求中的所有参数的完整列表,则调用该方法。
使用 URL 的 GET 方法实例
下面是一个简单的 URL,将使用 GET 方法向 HelloForm 程序传递两个值。
http://localhost:8080/HelloForm?first_name=ZARA&last_name=ALI
下面是处理 Web 浏览器输入的 HelloForm.java Servlet 程序。使用 getParameter() 方法,可以很容易地访问传递的信息:
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class HelloForm extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "使用 GET 方法读取表单数据"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<ul>\n" + " <li><b>名字</b>:" + request.getParameter("first_name") + "\n" + " <li><b>姓氏</b>:" + request.getParameter("last_name") + "\n" + "</ul>\n" + "</body></html>"); }}
假设环境已被正确设置,则只需编译 HelloForm.java,如下所示:
javac HelloForm.java
如果一切顺利,上述编译会产生 HelloForm.class 文件。接下来,您就必须把该类文件复制到 /webapps/ROOT/WEB-INF/classes 中,并在位于 /webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目
<servlet> <servlet-name>HelloForm</servlet-name> <servlet-class>HelloForm</servlet-class></servlet><servlet-mapping> <servlet-name>HelloForm</servlet-name> <url-pattern>/HelloForm</url-pattern></servlet-mapping>
现在在浏览器的地址栏中输入 http://localhost:8080/HelloForm?first_name=ZARA&last_name=ALI ,并在触发上述命令之前确保已经启动 Tomcat 服务器。如果一切顺利,您会得到下面的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vhQvreZh-1640940588594)(C:\Users\wuyang\AppData\Roaming\Typora\typora-user-images\image-20210829131201412.png)]
使用表单的 GET 方法实例
下面是一个简单的实例,使用 HTML 表单和提交按钮传递两个值。我们将使用相同的 Servlet HelloForm 来处理输入。
<html><body><form action="HelloForm" method="GET">名字:<input type="text" name="first_name"><br />姓氏:<input type="text" name="last_name" /><input type="submit" value="提交" /></form></body></html>
保存这个 HTML 到 hello.htm 文件中,并把它放在 /webapps/ROOT 目录下。当您访问 http://localhost:8080/Hello.htm 时,下面是上面表单的实际输出。
尝试输入名字和姓氏,然后点击"提交"按钮,在您本机上查看输出结果。基于所提供的输入,它会产生与上一个实例类似的结果。
使用表单的 POST 方法实例
让我们对上面的 Servlet 做小小的修改,以便它可以处理 GET 和 POST 方法。下面的 HelloForm.java Servlet 程序使用 GET 和 POST 方法处理由 Web 浏览器给出的输入。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class HelloForm extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Using GET Method to Read Form Data"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<ul>\n" + " <li><b>名字</b>:" + request.getParameter("first_name") + "\n" + " <li><b>姓氏</b>:" + request.getParameter("last_name") + "\n" + "</ul>\n" + "</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在,编译部署上述的 Servlet,并使用带有 POST 方法的 Hello.htm 进行测试,如下所示:
<html><body><form action="HelloForm" method="POST">名字:<input type="text" name="first_name"><br />姓氏:<input type="text" name="last_name" /><input type="submit" value="提交" /></form></body></html>
下面是上面表单的实际输出,尝试输入名字和姓氏,然后点击"提交"按钮,在您本机上查看输出结果。
基于所提供的输入,它会产生与上一个实例类似的结果。
将复选框数据传递到 Servlet 程序
当需要选择一个以上的选项时,则使用复选框。
下面是一个 HTML 代码实例 CheckBox.htm,一个带有两个复选框的表单。
<html><body><form action="CheckBox" method="POST" target="_blank"><input type="checkbox" name="maths" checked="checked" /> 数学<input type="checkbox" name="physics" /> 物理<input type="checkbox" name="chemistry" checked="checked" /> 化学<input type="submit" value="选择学科" /></form></body></html>
这段代码的结果是下面的表单:
下面是 CheckBox.java Servlet 程序,处理 Web 浏览器给出的复选框输入。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class CheckBox extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "读取复选框数据"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<ul>\n" + " <li><b>数学标识:</b>: " + request.getParameter("maths") + "\n" + " <li><b>物理标识:</b>: " + request.getParameter("physics") + "\n" + " <li><b>化学标识:</b>: " + request.getParameter("chemistry") + "\n" + "</ul>\n" + "</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
上面的实例将显示下面的结果:
读取所有的表单参数
以下是通用的实例,使用 HttpServletRequest 的 getParameterNames() 方法读取所有可用的表单参数。该方法返回一个枚举,其中包含未指定顺序的参数名。
一旦我们有一个枚举,我们可以以标准方式循环枚举,使用 hasMoreElements() 方法来确定何时停止,使用 nextElement() 方法来获取每个参数的名称。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class ReadParams extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "读取所有的表单数据"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<table width=\"100%\" border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + "<th>参数名称</th><th>参数值</th>\n"+ "</tr>\n"); Enumeration paramNames = request.getParameterNames(); while(paramNames.hasMoreElements()) { String paramName = (String)paramNames.nextElement(); out.print("<tr><td>" + paramName + "</td>\n<td>"); String[] paramValues = request.getParameterValues(paramName); // 读取单个值的数据 if (paramValues.length == 1) { String paramValue = paramValues[0]; if (paramValue.length() == 0) out.println("<i>No Value</i>"); else out.println(paramValue); } else { // 读取多个值的数据 out.println("<ul>"); for(int i=0; i < paramValues.length; i++) { out.println("<li>" + paramValues[i]); } out.println("</ul>"); } } out.println("</tr>\n</table>\n</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在,通过下面的表单尝试上面的 Servlet:
<html><body><form action="ReadParams" method="POST" target="_blank"><input type="checkbox" name="maths" checked="checked" /> 数学<input type="checkbox" name="physics" /> 物理<input type="checkbox" name="chemistry" checked="checked" /> 化学<input type="submit" value="选择学科" /></form></body></html>
现在使用上面的表单调用 Servlet,将产生以下结果:
您可以尝试使用上面的 Servlet 来读取其他的表单数据,比如文本框、单选按钮或下拉框等。
2.1.5 Servlet 客户端 HTTP 请求
当浏览器请求网页时,它会向 Web 服务器发送特定信息,这些信息不能被直接读取,因为这些信息是作为 HTTP 请求头的一部分进行传输的。可以查看 HTTP 协议 了解更多相关信息。
以下是来自于浏览器端的重要头信息,可以在 Web 编程中频繁使用:
头信息 | 描述 |
---|---|
Accept | 这个头信息指定浏览器或其他客户端可以处理的 MIME 类型。值 image/png 或 image/jpeg 是最常见的两种可能值。 |
Accept-Charset | 这个头信息指定浏览器可以用来显示信息的字符集。例如 ISO-8859-1。 |
Accept-Encoding | 这个头信息指定浏览器知道如何处理的编码类型。值 gzip 或 compress 是最常见的两种可能值。 |
Accept-Language | 这个头信息指定客户端的首选语言,在这种情况下,Servlet 会产生多种语言的结果。例如,en、en-us、ru 等。 |
Connection | 这个头信息指示客户端是否可以处理持久 HTTP 连接。持久连接允许客户端或其他浏览器通过单个请求来检索多个文件。值 Keep-Alive 意味着使用了持续连接。 |
Content-Length | 这个头信息只适用于 POST 请求,并给出 POST 数据的大小(以字节为单位)。 |
Cookie | 这个头信息把之前发送到浏览器的 cookies 返回到服务器。 |
Host | 这个头信息指定原始的 URL 中的主机和端口。 |
If-Modified-Since | 这个头信息表示只有当页面在指定的日期后已更改时,客户端想要的页面。如果没有新的结果可以使用,服务器会发送一个 304 代码,表示 Not Modified 头信息。 |
If-Unmodified-Since | 这个头信息是 If-Modified-Since 的对立面,它指定只有当文档早于指定日期时,操作才会成功。 |
Referer | 这个头信息指示所指向的 Web 页的 URL。例如,如果您在网页 1,点击一个链接到网页 2,当浏览器请求网页 2 时,网页 1 的 URL 就会包含在 Referer 头信息中。 |
User-Agent | 这个头信息识别发出请求的浏览器或其他客户端,并可以向不同类型的浏览器返回不同的内容。 |
读取 HTTP 头的方法
下面的方法可用在 Servlet 程序中读取 HTTP 头。这些方法通过 HttpServletRequest 对象可用。
序号 | 方法 & 描述 |
---|---|
1 | Cookie[] getCookies() 返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。 |
2 | Enumeration getAttributeNames() 返回一个枚举,包含提供给该请求可用的属性名称。 |
3 | Enumeration getHeaderNames() 返回一个枚举,包含在该请求中包含的所有的头名。 |
4 | Enumeration getParameterNames() 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。 |
5 | HttpSession getSession() 返回与该请求关联的当前 session 会话,或者如果请求没有 session 会话,则创建一个。 |
6 | HttpSession getSession(boolean create) 返回与该请求关联的当前 HttpSession,或者如果没有当前会话,且创建是真的,则返回一个新的 session 会话。 |
7 | Locale getLocale() 基于 Accept-Language 头,返回客户端接受内容的首选的区域设置。 |
8 | Object getAttribute(String name) 以对象形式返回已命名属性的值,如果没有给定名称的属性存在,则返回 null。 |
9 | ServletInputStream getInputStream() 使用 ServletInputStream,以二进制数据形式检索请求的主体。 |
10 | String getAuthType() 返回用于保护 Servlet 的身份验证方案的名称,例如,“BASIC” 或 “SSL”,如果JSP没有受到保护则返回 null。 |
11 | String getCharacterEncoding() 返回请求主体中使用的字符编码的名称。 |
12 | String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
13 | String getContextPath() 返回指示请求上下文的请求 URI 部分。 |
14 | String getHeader(String name) 以字符串形式返回指定的请求头的值。 |
15 | String getMethod() 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
16 | String getParameter(String name) 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
17 | String getPathInfo() 当请求发出时,返回与客户端发送的 URL 相关的任何额外的路径信息。 |
18 | String getProtocol() 返回请求协议的名称和版本。 |
19 | String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串。 |
20 | String getRemoteAddr() 返回发送请求的客户端的互联网协议(IP)地址。 |
21 | String getRemoteHost() 返回发送请求的客户端的完全限定名称。 |
22 | String getRemoteUser() 如果用户已通过身份验证,则返回发出请求的登录用户,或者如果用户未通过身份验证,则返回 null。 |
23 | String getRequestURI() 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。 |
24 | String getRequestedSessionId() 返回由客户端指定的 session 会话 ID。 |
25 | String getServletPath() 返回调用 JSP 的请求的 URL 的一部分。 |
26 | String[] getParameterValues(String name) 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。 |
27 | boolean isSecure() 返回一个布尔值,指示请求是否使用安全通道,如 HTTPS。 |
28 | int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。 |
29 | int getIntHeader(String name) 返回指定的请求头的值为一个 int 值。 |
30 | int getServerPort() 返回接收到这个请求的端口号。 |
HTTP Header 请求实例
下面的实例使用 HttpServletRequest 的 getHeaderNames() 方法读取 HTTP 头信息。该方法返回一个枚举,包含与当前的 HTTP 请求相关的头信息。
一旦我们有一个枚举,我们可以以标准方式循环枚举,使用 hasMoreElements() 方法来确定何时停止,使用 nextElement() 方法来获取每个参数的名称。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class DisplayHeader extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "HTTP Header 请求实例"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n"+ "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<table width=\"100%\" border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + "<th>Header 名称</th><th>Header 值</th>\n"+ "</tr>\n"); Enumeration headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String paramName = (String)headerNames.nextElement(); out.print("<tr><td>" + paramName + "</td>\n"); String paramValue = request.getHeader(paramName); out.println("<td> " + paramValue + "</td></tr>\n"); } out.println("</table>\n</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在,调用上面的 Servlet 会产生以下结果:
Servlet 服务器 HTTP 响应
正如前面的章节中讨论的那样,当一个 Web 服务器响应一个 HTTP 请求时,响应通常包括一个状态行、一些响应报头、一个空行和文档。一个典型的响应如下所示:
HTTP/1.1 200 OKContent-Type: text/htmlHeader2: ......HeaderN: ... (Blank Line)<!doctype ...><html><head>...</head><body>...</body></html>
状态行包括 HTTP 版本(在本例中为 HTTP/1.1)、一个状态码(在本例中为 200)和一个对应于状态码的短消息(在本例中为 OK)。
下表总结了从 Web 服务器端返回到浏览器的最有用的 HTTP 1.1 响应报头,您会在 Web 编程中频繁地使用它们:
头信息 | 描述 |
---|---|
Allow | 这个头信息指定服务器支持的请求方法(GET、POST 等)。 |
Cache-Control | 这个头信息指定响应文档在何种情况下可以安全地缓存。可能的值有:public、private 或 no-cache 等。Public 意味着文档是可缓存,Private 意味着文档是单个用户私用文档,且只能存储在私有(非共享)缓存中,no-cache 意味着文档不应被缓存。 |
Connection | 这个头信息指示浏览器是否使用持久 HTTP 连接。值 close 指示浏览器不使用持久 HTTP 连接,值 keep-alive 意味着使用持久连接。 |
Content-Disposition | 这个头信息可以让您请求浏览器要求用户以给定名称的文件把响应保存到磁盘。 |
Content-Encoding | 在传输过程中,这个头信息指定页面的编码方式。 |
Content-Language | 这个头信息表示文档编写所使用的语言。例如,en、en-us、ru 等。 |
Content-Length | 这个头信息指示响应中的字节数。只有当浏览器使用持久(keep-alive)HTTP 连接时才需要这些信息。 |
Content-Type | 这个头信息提供了响应文档的 MIME(Multipurpose Internet Mail Extension)类型。 |
Expires | 这个头信息指定内容过期的时间,在这之后内容不再被缓存。 |
Last-Modified | 这个头信息指示文档的最后修改时间。然后,客户端可以缓存文件,并在以后的请求中通过 If-Modified-Since 请求头信息提供一个日期。 |
Location | 这个头信息应被包含在所有的带有状态码的响应中。在 300s 内,这会通知浏览器文档的地址。浏览器会自动重新连接到这个位置,并获取新的文档。 |
Refresh | 这个头信息指定浏览器应该如何尽快请求更新的页面。您可以指定页面刷新的秒数。 |
Retry-After | 这个头信息可以与 503(Service Unavailable 服务不可用)响应配合使用,这会告诉客户端多久就可以重复它的请求。 |
Set-Cookie | 这个头信息指定一个与页面关联的 cookie。 |
设置 HTTP 响应报头的方法
下面的方法可用于在 Servlet 程序中设置 HTTP 响应报头。这些方法通过 HttpServletResponse 对象可用。
序号 | 方法 & 描述 |
---|---|
1 | String encodeRedirectURL(String url) 为 sendRedirect 方法中使用的指定的 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
2 | String encodeURL(String url) 对包含 session 会话 ID 的指定 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
3 | boolean containsHeader(String name) 返回一个布尔值,指示是否已经设置已命名的响应报头。 |
4 | boolean isCommitted() 返回一个布尔值,指示响应是否已经提交。 |
5 | void addCookie(Cookie cookie) 把指定的 cookie 添加到响应。 |
6 | void addDateHeader(String name, long date) 添加一个带有给定的名称和日期值的响应报头。 |
7 | void addHeader(String name, String value) 添加一个带有给定的名称和值的响应报头。 |
8 | void addIntHeader(String name, int value) 添加一个带有给定的名称和整数值的响应报头。 |
9 | void flushBuffer() 强制任何在缓冲区中的内容被写入到客户端。 |
10 | void reset() 清除缓冲区中存在的任何数据,包括状态码和头。 |
11 | void resetBuffer() 清除响应中基础缓冲区的内容,不清除状态码和头。 |
12 | void sendError(int sc) 使用指定的状态码发送错误响应到客户端,并清除缓冲区。 |
13 | void sendError(int sc, String msg) 使用指定的状态发送错误响应到客户端。 |
14 | void sendRedirect(String location) 使用指定的重定向位置 URL 发送临时重定向响应到客户端。 |
15 | void setBufferSize(int size) 为响应主体设置首选的缓冲区大小。 |
16 | void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。 |
17 | void setContentLength(int len) 设置在 HTTP Servlet 响应中的内容主体的长度,该方法设置 HTTP Content-Length 头。 |
18 | void setContentType(String type) 如果响应还未被提交,设置被发送到客户端的响应的内容类型。 |
19 | void setDateHeader(String name, long date) 设置一个带有给定的名称和日期值的响应报头。 |
20 | void setHeader(String name, String value) 设置一个带有给定的名称和值的响应报头。 |
21 | void setIntHeader(String name, int value) 设置一个带有给定的名称和整数值的响应报头。 |
22 | void setLocale(Locale loc) 如果响应还未被提交,设置响应的区域。 |
23 | void setStatus(int sc) 为该响应设置状态码。 |
HTTP Header 响应实例
在前面的实例中看到 setContentType() 方法,下面的实例也使用了同样的方法,此外,可以用 setIntHeader() 方法来设置 Refresh 头。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class Refresh extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置刷新自动加载时间为 5 秒 response.setIntHeader("Refresh", 5); // 设置响应内容类型 response.setContentType("text/html"); // Get current time Calendar calendar = new GregorianCalendar(); String am_pm; int hour = calendar.get(Calendar.HOUR); int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); if(calendar.get(Calendar.AM_PM) == 0) am_pm = "AM"; else am_pm = "PM"; String CT = hour+":"+ minute +":"+ second +" "+ am_pm; PrintWriter out = response.getWriter(); String title = "自动刷新 Header 设置"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n"+ "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<p>当前时间是:" + CT + "</p>\n"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在,调用上面的 Servlet,每隔 5 秒会显示当前系统时间。只要运行 Servlet 并稍等片刻,即可看到如下的结果:
2.1.6 Servlet HTTP 状态码
HTTP 请求和 HTTP 响应消息的格式是类似的,结构如下:
- 初始状态行 + 回车换行符(回车+换行)
- 零个或多个标题行+回车换行符
- 一个空白行,即回车换行符
- 一个可选的消息主体,比如文件、查询数据或查询输出
例如,服务器的响应头如下所示:
HTTP/1.1 200 OKContent-Type: text/htmlHeader2: ......HeaderN: ... (Blank Line)<!doctype ...><html><head>...</head><body>...</body></html>
状态行包括 HTTP 版本(在本例中为 HTTP/1.1)、一个状态码(在本例中为 200)和一个对应于状态码的短消息(在本例中为 OK)。
以下是可能从 Web 服务器返回的 HTTP 状态码和相关的信息列表:
代码 | 消息 | 描述 |
---|---|---|
100 | Continue | 只有请求的一部分已经被服务器接收,但只要它没有被拒绝,客户端应继续该请求。 |
101 | Switching Protocols | 服务器切换协议。 |
200 | OK | 请求成功。 |
201 | Created | 该请求是完整的,并创建一个新的资源。 |
202 | Accepted | 该请求被接受处理,但是该处理是不完整的。 |
203 | Non-authoritative Information | |
204 | No Content | |
205 | Reset Content | |
206 | Partial Content | |
300 | Multiple Choices | 链接列表。用户可以选择一个链接,进入到该位置。最多五个地址。 |
301 | Moved Permanently | 所请求的页面已经转移到一个新的 URL。 |
302 | Found | 所请求的页面已经临时转移到一个新的 URL。 |
303 | See Other | 所请求的页面可以在另一个不同的 URL 下被找到。 |
304 | Not Modified | |
305 | Use Proxy | |
306 | Unused | 在以前的版本中使用该代码。现在已不再使用它,但代码仍被保留。 |
307 | Temporary Redirect | 所请求的页面已经临时转移到一个新的 URL。 |
400 | Bad Request | 服务器不理解请求。 |
401 | Unauthorized | 所请求的页面需要用户名和密码。 |
402 | Payment Required | 您还不能使用该代码。 |
403 | Forbidden | 禁止访问所请求的页面。 |
404 | Not Found | 服务器无法找到所请求的页面。. |
405 | Method Not Allowed | 在请求中指定的方法是不允许的。 |
406 | Not Acceptable | 服务器只生成一个不被客户端接受的响应。 |
407 | Proxy Authentication Required | 在请求送达之前,您必须使用代理服务器的验证。 |
408 | Request Timeout | 请求需要的时间比服务器能够等待的时间长,超时。 |
409 | Conflict | 请求因为冲突无法完成。 |
410 | Gone | 所请求的页面不再可用。 |
411 | Length Required | “Content-Length” 未定义。服务器无法处理客户端发送的不带 Content-Length 的请求信息。 |
412 | Precondition Failed | 请求中给出的先决条件被服务器评估为 false。 |
413 | Request Entity Too Large | 服务器不接受该请求,因为请求实体过大。 |
414 | Request-url Too Long | 服务器不接受该请求,因为 URL 太长。当您转换一个 “post” 请求为一个带有长的查询信息的 “get” 请求时发生。 |
415 | Unsupported Media Type | 服务器不接受该请求,因为媒体类型不被支持。 |
417 | Expectation Failed | |
500 | Internal Server Error | 未完成的请求。服务器遇到了一个意外的情况。 |
501 | Not Implemented | 未完成的请求。服务器不支持所需的功能。 |
502 | Bad Gateway | 未完成的请求。服务器从上游服务器收到无效响应。 |
503 | Service Unavailable | 未完成的请求。服务器暂时超载或死机。 |
504 | Gateway Timeout | 网关超时。 |
505 | HTTP Version Not Supported | 服务器不支持"HTTP协议"版本。 |
设置 HTTP 状态代码的方法
下面的方法可用于在 Servlet 程序中设置 HTTP 状态码。这些方法通过 HttpServletResponse 对象可用。
序号 | 方法 & 描述 |
---|---|
1 | public void setStatus ( int statusCode ) 该方法设置一个任意的状态码。setStatus 方法接受一个 int(状态码)作为参数。如果您的反应包含了一个特殊的状态码和文档,请确保在使用 PrintWriter 实际返回任何内容之前调用 setStatus。 |
2 | public void sendRedirect(String url)该方法生成一个 302 响应,连同一个带有新文档 URL 的 Location 头。 |
3 | public void sendError(int code, String message) 该方法发送一个状态码(通常为 404),连同一个在 HTML 文档内部自动格式化并发送到客户端的短消息。 |
HTTP 状态码实例
下面的例子把 407 错误代码发送到客户端浏览器,浏览器会显示 “Need authentication!!!” 消息。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class showError extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置错误代码和原因 response.sendError(407, "Need authentication!!!" ); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在,调用上面的 Servlet 将显示以下结果:
2.1.7 Servlet 编写过滤器
Servlet 过滤器是可用于 Servlet 编程的 Java 类,有以下目的:
- 在客户端的请求访问后端资源之前,拦截这些请求。
- 在服务器的响应发送回客户端之前,处理这些响应。
根据规范建议的各种类型的过滤器:
- 身份验证过滤器(Authentication Filters)。
- 数据压缩过滤器(Data compression Filters)。
- 加密过滤器(Encryption Filters)。
- 触发资源访问事件过滤器。
- 图像转换过滤器(Image Conversion Filters)。
- 日志记录和审核过滤器(Logging and Auditing Filters)。
- MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
- 标记化过滤器(Tokenizing Filters)。
- XSL/T 过滤器(XSL/T Filters),转换 XML 内容。
过滤器被部署在部署描述符文件 web.xml 中,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。
当 Web 容器启动 Web 应用程序时,它会为您在部署描述符中声明的每一个过滤器创建一个实例。该过滤器执行的顺序是按它们在部署描述符中声明的顺序。
2.1.8 Servlet 过滤器方法
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:
序号 | 方法 & 描述 |
---|---|
1 | public void doFilter (ServletRequest, ServletResponse, FilterChain) 该方法在每次一个请求/响应对因客户端在链的末端请求资源而通过链传递时由容器调用。 |
2 | public void init(FilterConfig filterConfig) 该方法由 Web 容器调用,指示一个过滤器被放入服务。 |
3 | public void destroy() 该方法由 Web 容器调用,指示一个过滤器被取出服务。 |
FilterConfig 使用
Filter 的 init 方法中提供了一个 FilterConfig 对象。
如 web.xml 文件配置如下:
<filter> <filter-name>LoginFilter</filter-name> <filter-class>com.w3cschool.test.LogFilter</filter-class> <init-param> <param-name>Site</param-name> <param-value>w3cschool在线教程</param-value> </init-param></filter>
在 init 方法使用 FilterConfig 对象获取参数:
public void init(FilterConfig config) throws ServletException { // 获取初始化参数 String site = config.getInitParameter("Site"); // 输出初始化参数 System.out.println("网站名称: " + site); }
Servlet 过滤器实例
以下是 Servlet 过滤器的实例,将输出客户端的 IP 地址和当前的日期时间。本实例让您对 Servlet 过滤器有基本的了解,您可以使用相同的概念编写更复杂的过滤器应用程序:
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 实现 Filter 类public class LogFilter implements Filter { public void init(FilterConfig config) throws ServletException{ // 获取初始化参数 String testParam = config.getInitParameter("test-param"); // 输出初始化参数 System.out.println("Test Param: " + testParam); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException { // 获取客户机的 IP 地址 String ipAddress = request.getRemoteAddr(); // 记录 IP 地址和当前时间戳 System.out.println("IP "+ ipAddress + ", Time " + new Date().toString()); // 把请求传回过滤链 chain.doFilter(request,response); } public void destroy( ){ /* 在 Filter 实例被 Web 容器从服务移除之前调用 */ }}
这边使用前文提到的 DisplayHeader.java 为例子:
//导入必需的 java 库import java.io.IOException;import java.io.PrintWriter;import java.util.Enumeration;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/DisplayHeader")//扩展 HttpServlet 类public class DisplayHeader extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String title = "HTTP Header 请求实例 - w3cschool教程实例"; String docType = "<!DOCTYPE html> \n"; out.println(docType + "<html>\n" + "<head><meta charset=\"utf-8\"><title>" + title + "</title></head>\n"+ "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<table width=\"100%\" border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + "<th>Header 名称</th><th>Header 值</th>\n"+ "</tr>\n"); Enumeration headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String paramName = (String)headerNames.nextElement(); out.print("<tr><td>" + paramName + "</td>\n"); String paramValue = request.getHeader(paramName); out.println("<td> " + paramValue + "</td></tr>\n"); } out.println("</table>\n</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping)
定义过滤器,然后映射到一个 URL 或 Servlet,这与定义 Servlet,然后映射到一个 URL 模式方式大致相同。在部署描述符文件 web.xml 中为 filter 标签创建下面的条目:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>LogFilter</filter-name> <filter-class>com.w3cschool.test.LogFilter</filter-class> <init-param> <param-name>Site</param-name> <param-value>w3cschool在线教程</param-value> </init-param></filter><filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><servlet> <!-- 类名 --> <servlet-name>DisplayHeader</servlet-name> <!-- 所在的包 --> <servlet-class>com.w3cschool.test.DisplayHeader</servlet-class> </servlet> <servlet-mapping> <servlet-name>DisplayHeader</servlet-name> <!-- 访问的网址 --> <url-pattern>/TomcatTest/DisplayHeader</url-pattern> </servlet-mapping> </web-app>
上述过滤器适用于所有的 Servlet,因为我们在配置中指定 /* 。如果您只想在少数的 Servlet 上应用过滤器,您可以指定一个特定的 Servlet 路径。
现在试着以常用的方式调用任何 Servlet,您将会看到在 Web 服务器中生成的日志。您也可以使用 Log4J 记录器来把上面的日志记录到一个单独的文件中。
使用多个过滤器
Web 应用程序可以根据特定的目的定义若干个不同的过滤器。假设您定义了两个过滤器 AuthenFilter 和 LogFilter。您需要创建一个如下所述的不同的映射,其余的处理与上述所讲解的大致相同:
<filter> <filter-name>LogFilter</filter-name> <filter-class>com.w3cschool.test.LogFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param></filter><filter> <filter-name>AuthenFilter</filter-name> <filter-class>com.w3cschool.test.AuthenFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param></filter><filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
过滤器的应用顺序
web.xml 中的 filter-mapping 元素的顺序决定了 Web 容器应用过滤器到 Servlet 的顺序。若要反转过滤器的顺序,您只需要在 web.xml 文件中反转 filter-mapping 元素即可。
例如,上面的实例将先应用 LogFilter,然后再应用 AuthenFilter,但是下面的实例将颠倒这个顺序:
<filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
web.xml配置各节点说明
<filter>指定一个过滤器。
<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
<filter-class>元素用于指定过滤器的完整的限定类名。
<init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。
在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
<filter-name>子元素用于设置filter的注册名称。该值必须是在元素中声明过的过滤器的名字
<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
<servlet-name>指定过滤器所拦截的Servlet名称。
<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个子元素用来指定 Filter 对资源的多种调用方式进行拦截。
<dispatcher>子元素可以设置的值及其意义
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
Servlet 异常处理
当一个 Servlet 抛出一个异常时,Web 容器在使用了 exception-type 元素的 web.xml 中搜索与抛出异常类型相匹配的配置。
您必须在 web.xml 中使用 error-page 元素来指定对特定异常 或 HTTP 状态码 作出相应的 Servlet 调用。
web.xml 配置
假设,有一个 ErrorHandler 的 Servlet 在任何已定义的异常或错误出现时被调用。以下将是在 web.xml 中创建的项。
<!-- servlet 定义 --><servlet> <servlet-name>ErrorHandler</servlet-name> <servlet-class>ErrorHandler</servlet-class></servlet><!-- servlet 映射 --><servlet-mapping> <servlet-name>ErrorHandler</servlet-name> <url-pattern>/ErrorHandler</url-pattern></servlet-mapping><!-- error-code 相关的错误页面 --><error-page> <error-code>404</error-code> <location>/ErrorHandler</location></error-page><error-page> <error-code>403</error-code> <location>/ErrorHandler</location></error-page><!-- exception-type 相关的错误页面 --><error-page> <exception-type> javax.servlet.ServletException </exception-type > <location>/ErrorHandler</location></error-page><error-page> <exception-type>java.io.IOException</exception-type > <location>/ErrorHandler</location></error-page>
如果您想对所有的异常有一个通用的错误处理程序,那么应该定义下面的 error-page,而不是为每个异常定义单独的 error-page 元素:
<error-page> <exception-type>java.lang.Throwable</exception-type > <location>/ErrorHandler</location></error-page>
以下是关于上面的 web.xml 异常处理要注意的点:
- Servelt ErrorHandler 与其他的 Servelt 的定义方式一样,且在 web.xml 中进行配置。
- 如果有错误状态代码出现,不管为 404(Not Found 未找到)或 403(Forbidden 禁止),则会调用 ErrorHandler 的 Servlet。
- 如果 Web 应用程序抛出 ServletException 或 IOException,那么 Web 容器会调用 ErrorHandler 的 Servlet。
- 您可以定义不同的错误处理程序来处理不同类型的错误或异常。上面的实例是非常通用的,希望您能通过实例理解基本的概念。
请求属性 - 错误/异常
序号 | 属性 & 描述 |
---|---|
1 | javax.servlet.error.status_code 该属性给出状态码,状态码可被存储,并在存储为 java.lang.Integer 数据类型后可被分析。 |
2 | javax.servlet.error.exception_type 该属性给出异常类型的信息,异常类型可被存储,并在存储为 java.lang.Class 数据类型后可被分析。 |
3 | javax.servlet.error.message 该属性给出确切错误消息的信息,信息可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
4 | javax.servlet.error.request_uri 该属性给出有关 URL 调用 Servlet 的信息,信息可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
5 | javax.servlet.error.exception 该属性给出异常产生的信息,信息可被存储,并在存储为 java.lang.Throwable 数据类型后可被分析。 |
6 | javax.servlet.error.servlet_name 该属性给出 Servlet 的名称,名称可被存储,并在存储为 java.lang.String 数据类型后可被分析。 |
Servlet 错误处理程序实例
以下是 Servlet 实例,将应对任何您所定义的错误或异常发生时的错误处理程序。
本实例让您对 Servlet 中的异常处理有基本的了解,您可以使用相同的概念编写更复杂的异常处理应用程序:
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class ErrorHandler extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 分析 Servlet 异常 Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception"); Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); String servletName = (String) request.getAttribute("javax.servlet.error.servlet_name"); if (servletName == null){ servletName = "Unknown"; } String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri"); if (requestUri == null){ requestUri = "Unknown"; } // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Error/Exception Information"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n"); if (throwable == null && statusCode == null){ out.println("<h2>Error information is missing</h2>"); out.println("Please return to the <a href=\"" + response.encodeURL("http://localhost:8080/") + "\">Home Page</a>."); }else if (statusCode != null){ out.println("The status code : " + statusCode); }else{ out.println("<h2 class="tutheader">Error information</h2>"); out.println("Servlet Name : " + servletName + "</br></br>"); out.println("Exception Type : " + throwable.getClass( ).getName( ) + "</br></br>"); out.println("The request URI: " + requestUri + "<br><br>"); out.println("The exception message: " + throwable.getMessage( )); } out.println("</body>"); out.println("</html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
以通常的方式编译 ErrorHandler.java,把您的类文件放入<Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中。
让我们在 web.xml 文件中添加如下配置来处理异常:
<servlet> <servlet-name>ErrorHandler</servlet-name> <servlet-class>ErrorHandler</servlet-class></servlet><!-- servlet mappings --><servlet-mapping> <servlet-name>ErrorHandler</servlet-name> <url-pattern>/ErrorHandler</url-pattern></servlet-mapping><error-page> <error-code>404</error-code> <location>/ErrorHandler</location></error-page><error-page> <exception-type>java.lang.Throwable</exception-type > <location>/ErrorHandler</location></error-page>
现在,尝试使用一个会产生异常的 Servlet,或者输入一个错误的 URL,这将触发 Web 容器调用 ErrorHandler 的 Servlet,并显示适当的消息。例如,如果您输入了一个错误的 URL,那么它将显示下面的结果:
上面的代码在某些 Web 浏览器中可能无法正常工作。因此,请尽量尝试使用 Mozilla 和 Safari 浏览器,在这两种浏览器中它们应该能够正常工作。
2.1.9 Servlet Cookies 处理
Cookies 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。Java Servlet 显然支持 HTTP Cookies。
识别返回用户包括三个步骤:
- 服务器脚本向浏览器发送一组 Cookies。例如:姓名、年龄或识别号码等。
- 浏览器将这些信息存储在本地计算机上,以备将来使用。
- 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookies 信息发送到服务器,服务器将使用这些信息来识别用户。
本章将向您讲解如何设置或重置 Cookies,如何访问它们,以及如何将它们删除。
Servlet Cookie 处理需要对中文进行编码与解码,方法如下:
String str = java.net.URLEncoder.encode("中文"); //编码String str = java.net.URLDecoder.decode("编码后的字符串"); // 解码
Cookie 剖析
Cookies 通常设置在 HTTP 头信息中(虽然 JavaScript 也可以直接在浏览器上设置一个 Cookie)。设置 Cookie 的 Servlet 会发送如下的头信息:
HTTP/1.1 200 OKDate: Fri, 04 Feb 2000 21:03:38 GMTServer: Apache/1.3.9 (UNIX) PHP/4.0b3Set-Cookie: name=xyz; expires=Friday, 04-Feb-07 22:03:38 GMT; path=/; domain=w3cschool.cnConnection: closeContent-Type: text/html
正如您所看到的,Set-Cookie 头包含了一个名称值对、一个 GMT 日期、一个路径和一个域。名称和值会被 URL 编码。expires 字段是一个指令,告诉浏览器在给定的时间和日期之后"忘记"该 Cookie。
如果浏览器被配置为存储 Cookies,它将会保留此信息直到到期日期。如果用户的浏览器指向任何匹配该 Cookie 的路径和域的页面,它会重新发送 Cookie 到服务器。浏览器的头信息可能如下所示:
GET / HTTP/1.0Connection: Keep-AliveUser-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)Host: zink.demon.co.uk:1126Accept: image/gif, */*Accept-Encoding: gzipAccept-Language: enAccept-Charset: iso-8859-1,*,utf-8Cookie: name=xyz
Servlet 就能够通过请求方法 request.getCookies() 访问 Cookie,该方法将返回一个 Cookie 对象的数组。
Servlet Cookies 方法
以下是在 Servlet 中操作 Cookies 时可使用的有用的方法列表。
序号 | 方法 & 描述 |
---|---|
1 | public void setDomain(String pattern) 该方法设置 cookie 适用的域,例如 w3cschool.cn。 |
2 | public String getDomain() 该方法获取 cookie 适用的域,例如 w3cschool.cn。 |
3 | public void setMaxAge(int expiry) 该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie 只会在当前 session 会话中持续有效。 |
4 | public int getMaxAge() 该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭。 |
5 | public String getName() 该方法返回 cookie 的名称。名称在创建后不能改变。 |
6 | public void setValue(String newValue) 该方法设置与 cookie 关联的值。 |
7 | public String getValue() 该方法获取与 cookie 关联的值。 |
8 | public void setPath(String uri) 该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。 |
9 | public String getPath() 该方法获取 cookie 适用的路径。 |
10 | public void setSecure(boolean flag) 该方法设置布尔值,表示 cookie 是否应该只在加密的(即 SSL)连接上发送。 |
11 | public void setComment(String purpose) 该方法规定了描述 cookie 目的的注释。该注释在浏览器向用户呈现 cookie 时非常有用。 |
12 | public String getComment() 该方法返回了描述 cookie 目的的注释,如果 cookie 没有注释则返回 null。 |
通过 Servlet 设置 Cookies
通过 Servlet 设置 Cookies 包括三个步骤:
**(1) 创建一个 Cookie 对象:**您可以调用带有 cookie 名称和 cookie 值的 Cookie 构造函数,cookie 名称和 cookie 值都是字符串。
Cookie cookie = new Cookie("key","value");
请记住,无论是名字还是值,都不应该包含空格或以下任何字符:
[ ] ( ) = , " / ? @ : ;
**(2) 设置最大生存周期:**您可以使用 setMaxAge 方法来指定 cookie 能够保持有效的时间(以秒为单位)。下面将设置一个最长有效期为 24 小时的 cookie。
cookie.setMaxAge(60*60*24);
**(3) 发送 Cookie 到 HTTP 响应头:**您可以使用 response.addCookie 来添加 HTTP 响应头中的 Cookies,如下所示:
response.addCookie(cookie);
实例
让我们修改我们的 表单数据实例,为名字和姓氏设置 Cookies。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class HelloForm extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 为名字和姓氏创建 Cookies Cookie firstName = new Cookie("first_name", request.getParameter("first_name")); Cookie lastName = new Cookie("last_name", request.getParameter("last_name")); // 为两个 Cookies 设置过期日期为 24 小时后 firstName.setMaxAge(60*60*24); lastName.setMaxAge(60*60*24); // 在响应头中添加两个 Cookies response.addCookie( firstName ); response.addCookie( lastName ); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "设置 Cookies 实例"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<ul>\n" + " <li><b>名字</b>:" + request.getParameter("first_name") + "\n" + " <li><b>姓氏</b>:" + request.getParameter("last_name") + "\n" + "</ul>\n" + "</body></html>"); }}
编译上面的 Servlet HelloForm,并在 web.xml 文件中创建适当的条目,最后尝试下面的 HTML 页面来调用 Servlet。
<html><body><form action="HelloForm" method="GET">名字:<input type="text" name="first_name"><br />姓氏:<input type="text" name="last_name" /><input type="submit" value="提交" /></form></body></html>
保存上面的 HTML 内容到文件 hello.htm 中,并把它放在<Tomcat-installation-directory>/webapps/ROOT 目录中。当您访问 http://localhost:8080/Hello.htm 时,上面表单的实际输出如下所示:
尝试输入名字和姓氏,然后点击"提交"按钮,名字和姓氏将显示在屏幕上,同时会设置 firstName 和 lastName 这两个 Cookies,当下次您按下提交按钮时,会将这两个 Cookies 传回到服务器。
下一节会讲解如何在 Web 应用程序中访问这些 Cookies。
通过 Servlet 读取 Cookies
要读取 Cookies,您需要通过调用 HttpServletRequest 的 getCookies( ) 方法创建一个 javax.servlet.http.Cookie 对象的数组。然后循环遍历数组,并使用 getName() 和 getValue() 方法来访问每个 cookie 和关联的值。
实例
让我们读取上面的实例中设置的 Cookies
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class ReadCookies extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = null; Cookie[] cookies = null; // 获取与该域相关的 Cookies 的数组 cookies = request.getCookies(); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Reading Cookies Example"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" ); if( cookies != null ){ out.println("<h2>查找 Cookies 名称和值</h2>"); for (int i = 0; i < cookies.length; i++){ cookie = cookies[i]; out.print("名称:" + cookie.getName( ) + ","); out.print("值:" + cookie.getValue( )+" <br/>"); } }else{ out.println( "<h2 class="tutheader">未找到 Cookies</h2>"); } out.println("</body>"); out.println("</html>"); }}
编译上面的 Servlet ReadCookies,并在 web.xml 文件中创建适当的条目。如果您已经设置了 first_name cookie 为 “John”,last_name cookie 为 “Player” ,尝试运行 http://localhost:8080/ReadCookies,将显示如下结果:
通过 Servlet 删除 Cookies
删除 Cookies 是非常简单的。如果您想删除一个 cookie,那么您只需要按照以下三个步骤进行:
- 读取一个现有的 cookie,并把它存储在 Cookie 对象中。
- 使用 setMaxAge() 方法设置 cookie 的年龄为零,来删除现有的 cookie。
- 把这个 cookie 添加到响应头。
实例
下面的例子将删除现有的名为 “first_name” 的 cookie,当您下次运行 ReadCookies 的 Servlet 时,它会返回 first_name 为空值。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class DeleteCookies extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = null; Cookie[] cookies = null; // 获取与该域相关的 Cookies 的数组 cookies = request.getCookies(); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Delete Cookies Example"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" ); if( cookies != null ){ out.println("<h2>Cookies 名称和值</h2>"); for (int i = 0; i < cookies.length; i++){ cookie = cookies[i]; if((cookie.getName( )).compareTo("first_name") == 0 ){ cookie.setMaxAge(0); response.addCookie(cookie); out.print("已删除的 cookie:" + cookie.getName( ) + "<br/>"); } out.print("名称:" + cookie.getName( ) + ","); out.print("值:" + cookie.getValue( )+" <br/>"); } }else{ out.println( "<h2 class="tutheader">No cookies founds</h2>"); } out.println("</body>"); out.println("</html>"); }}
编译上面的 Servlet DeleteCookies,并在 web.xml 文件中创建适当的条目。现在运行 http://localhost:8080/DeleteCookies,将显示如下结果:
现在尝试运行 http://localhost:8080/ReadCookies,它将只显示一个 cookie,如下所示:
您可以手动在 Internet Explorer 中删除 Cookies。在"工具"菜单,选择"Internet 选项"。如果要删除所有的 Cookies,请按"删除 Cookies"。
2.1.10 Servlet Session 跟踪
HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。
但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话:
Cookies
一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。
这可能不是一个有效的方法,因为很多浏览器不支持 cookie,所以我们建议不要使用这种方式来维持 session 会话。
隐藏的表单字段
一个 Web 服务器可以发送一个隐藏的 HTML 表单字段,以及一个唯一的 session 会话 ID,如下所示:
<input type="hidden" name="sessionid" value="12345">
该条目意味着,当表单被提交时,指定的名称和值会被自动包含在 GET 或 POST 数据中。每次当 Web 浏览器发送回请求时,session_id 值可以用于保持不同的 Web 浏览器的跟踪。
这可能是一种保持 session 会话跟踪的有效方式,但是点击常规的超文本链接(
URL 重写
您可以在每个 URL 末尾追加一些额外的数据来标识 session 会话,服务器会把该 session 会话标识符与已存储的有关 session 会话的数据相关联。
例如,http://w3cschool.cn/file.htm;sessionid=12345,session 会话标识符被附加为 sessionid=12345,标识符可被 Web 服务器访问以识别客户端。
URL 重写是一种更好的维持 session 会话的方式,它在浏览器不支持 cookie 时能够很好地工作,但是它的缺点是会动态生成每个 URL 来为页面分配一个 session 会话 ID,即使是在很简单的静态 HTML 页面中也会如此。
HttpSession 对象
除了上述的三种方式,Servlet 还提供了 HttpSession 接口,该接口提供了一种跨多个页面请求或访问网站时识别用户以及存储有关用户信息的方式。
Servlet 容器使用这个接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,跨多个连接或页面请求。
可以通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象,如下所示:
HttpSession session = request.getSession();
你需要在向客户端发送任何文档内容之前调用 request.getSession()。下面总结了 HttpSession 对象中可用的几个重要的方法:
序号 | 方法 & 描述 |
---|---|
1 | public Object getAttribute(String name) 该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null。 |
2 | public Enumeration getAttributeNames() 该方法返回 String 对象的枚举,String 对象包含所有绑定到该 session 会话的对象的名称。 |
3 | public long getCreationTime() 该方法返回该 session 会话被创建的时间,自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。 |
4 | public String getId() 该方法返回一个包含分配给该 session 会话的唯一标识符的字符串。 |
5 | public long getLastAccessedTime() 该方法返回客户端最后一次发送与该 session 会话相关的请求的时间自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。 |
6 | public int getMaxInactiveInterval() 该方法返回 Servlet 容器在客户端访问时保持 session 会话打开的最大时间间隔,以秒为单位。 |
7 | public void invalidate() 该方法指示该 session 会话无效,并解除绑定到它上面的任何对象。 |
8 | public boolean isNew( 如果客户端还不知道该 session 会话,或者如果客户选择不参入该 session 会话,则该方法返回 true。 |
9 | public void removeAttribute(String name) 该方法将从该 session 会话移除指定名称的对象。 |
10 | public void setAttribute(String name, Object value) 该方法使用指定的名称绑定一个对象到该 session 会话。 |
11 | public void setMaxInactiveInterval(int interval) 该方法在 Servlet 容器指示该 session 会话无效之前,指定客户端请求之间的时间,以秒为单位。 |
Session 跟踪实例
本实例说明了如何使用 HttpSession 对象获取 session 会话创建时间和最后访问时间。如果不存在 session 会话,我们将通过请求创建一个新的 session 会话。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class SessionTrack extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 如果不存在 session 会话,则创建一个 session 对象 HttpSession session = request.getSession(true); // 获取 session 创建时间 Date createTime = new Date(session.getCreationTime()); // 获取该网页的最后一次访问时间 Date lastAccessTime = new Date(session.getLastAccessedTime()); String title = "欢迎回到我的网站"; Integer visitCount = new Integer(0); String visitCountKey = new String("visitCount"); String userIDKey = new String("userID"); String userID = new String("ABCD"); // 检查网页上是否有新的访问者 if (session.isNew()){ title = "欢迎来到我的网站"; session.setAttribute(userIDKey, userID); } else { visitCount = (Integer)session.getAttribute(visitCountKey); visitCount = visitCount + 1; userID = (String)session.getAttribute(userIDKey); } session.setAttribute(visitCountKey, visitCount); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<h2 align=\"center\">Session 信息</h2>\n" + "<table border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + " <th>Session 信息</th><th>值</th></tr>\n" + "<tr>\n" + " <td>id</td>\n" + " <td>" + session.getId() + "</td></tr>\n" + "<tr>\n" + " <td>Creation Time</td>\n" + " <td>" + createTime + " </td></tr>\n" + "<tr>\n" + " <td>Time of Last Access</td>\n" + " <td>" + lastAccessTime + " </td></tr>\n" + "<tr>\n" + " <td>User ID</td>\n" + " <td>" + userID + " </td></tr>\n" + "<tr>\n" + " <td>Number of visits</td>\n" + " <td>" + visitCount + "</td></tr>\n" + "</table>\n" + "</body></html>"); }}
编译上面的 Servlet SessionTrack,并在 web.xml 文件中创建适当的条目。在浏览器地址栏输入 http://localhost:8080/SessionTrack,当您第一次运行时将显示如下结果:
再次尝试运行相同的 Servlet,它将显示如下结果:
删除 Session 会话数据
当您完成了一个用户的 session 会话数据,您有以下几种选择:
- 移除一个特定的属性: 可以调用 public void removeAttribute(String name) 方法来删除与特定的键相关联的值。
- 删除整个 session 会话:可以调用 public void invalidate() 方法来丢弃整个 session 会话。
- 设置 session 会话过期时间:可以调用 public void setMaxInactiveInterval(int interval) 方法来单独设置 session 会话超时。
- 注销用户:如果使用的是支持 servlet 2.4 的服务器,您可以调用 logout 来注销 Web 服务器的客户端,并把属于所有用户的所有 session 会话设置为无效。
- **web.xml 配置:**如果您使用的是 Tomcat,除了上述方法,您还可以在 web.xml 文件中配置 session 会话超时,如下所示:
<session-config> <session-timeout>15</session-timeout></session-config>
上面实例中的超时时间是以分钟为单位,将覆盖 Tomcat 中默认的 30 分钟超时时间。
在一个 Servlet 中的 getMaxInactiveInterval() 方法会返回 session 会话的超时时间,以秒为单位。所以,如果在 web.xml 中配置 session 会话超时时间为 15 分钟,那么 getMaxInactiveInterval() 会返回 900。
ServletContext 对象
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。为了方便大家理解,我们将ServletContext和Cookie、Session做一个简单对比,如下图:
我们可以把ServletContext当成一个公用的空间,可以被所有的客户访问,如上图,A、B、C三个客户端都可以访问。
WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用,并且它被所有客户端共享。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。
当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁。
ServletContext使用方法
1、ServletContext对象如何得到
this.getServletContext(); this.getServletConfig().getServletContext();
2、可以把它想象成一张表,这个和Session非常相似:每一行就是一个属性,如下:
添加属性:setAttribute(String name, Object obj);
得到值:getAttribute(String name),这个方法返回Object
删除属性:removeAttribute(String name)
3、生命周期
ServletContext中的属性的生命周期从创建开始,到服务器关闭结束。
一个快速入门的案例:
我们创建Servlet1和Servlet2,分别用于在ServletContext中创建和读取属性:
Servlet1的doGet方法为:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); // 获取ServletContext对象的引用 // 第一种方法 ServletContext servletContext = this.getServletContext(); // 第二种方法 // ServletContext servletContext2 = this.getServletConfig().getServletContext(); servletContext.setAttribute("name", "小明"); out.println("将 name=小明 写入了ServletContext");}
Servlet2的doGet方法为:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); // 取出ServletContext的某个属性 //1.首先获取到ServletContext ServletContext servletContext = this.getServletContext(); //2.取出属性 String name = (String)servletContext.getAttribute("name"); out.println("name="+name);}
以此访问Servlet1和Servlet2,我们可以分别看到输出如下:
粗看之下,这个运行结果和Session,Cookie的应用似乎没什么区别,但事实上则完全不一样的。只要不关闭Tomcat或者reload该应用,当我们关闭当前的浏览器,或者是换一个浏览器,比如从360浏览器换到了IE浏览器再次访问Servlet2,我们依然可以看到这个结果!这就是和和Session,Cookie最大的不同了。之所以会造成这种不同,是因为ServletContext存在于服务器内存中的一个公共空间,它可以供所有的用户客户端访问。
ServletContext应用
1、多个Servlet可以通过ServletContext对象来实现数据间的共享
类似于Session,通过ServletContext对象我们也可以实现数据共享,但值得注意的是,Session是只能在一个客户端中共享数据,而ServletContext中的数据是在所有客户端中都可以实现数据共享的。
2、实现Servlet的请求转发
之前我们学过的请求转发是通过request对象的:
request.getRequestDispatcher("/url").forward(request, response);
这里要说明的是,ServletContext也可以实现请求转发:
this.getServletContext().getRequestDispatcher("/url").forward(request, response);
这两个转发效果是一样的。
3、获取Web应用的初始化参数
我们可以用<init-param>标签为servlet配置初始化参数,然后使用ServletConfig对象获取这些参数,假如有如下的MyServlet,它的配置为:
<servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.gavin.servlet.MyServlet</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </servlet>
可以看到它配置了一个初始化参数:encoding=utf-8,那么我们在MyServlet的源代码中需要这样去得到这个参数:
String encoding = this.getServletConfig().getInitParameter("encoding");
注意,上述的参数配置方法只针对一个特定的Servlet有效,我们可以通过ServletContext来获取全局的、整个Web应用的初始化参数,全局的初始化参数是这样配置在web.xml文件中的:
<!-- 如果希望所有的Servlet都可以使用该配置,则必须这么做 --><context-param> <param-name>name</param-name> <param-value>gavin</param-value></context-param>
然后我们可以在任意一个Servlet中使用ServletContext获取这个参数:
String name = this.getServletContext().getInitParameter("name");
4、利用ServletContext对象读取资源文件(比如properties文件)
读取资源文件要根据资源文件所在的位置来决定,一般分为以下两种情况:
4.1:文件在WebRoot文件夹下,即Web应用的根目录。这时候我们可以使用ServletContext来读取该资源文件。
假设我们Web根目录下有一个配置数据库信息的dbinfo.properties文件,里面配置了name和password属性,这时候可以通过ServletContext去读取这个文件:
// 这种方法的默认读取路径就是Web应用的根目录InputStream stream = this.getServletContext().getResourceAsStream("dbinfo.properties");// 创建属性对象Properties properties = new Properties();properties.load(stream);String name = properties.getProperty("name");String password = properties.getProperty("password");out.println("name="+name+";password="+password);
4.2:如果这个文件放在了src目录下,这时就不能用ServletContext来读取了,必须要使用类加载器去读取。
// 类加载器的默认读取路径是src根目录InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("dbinfo.properties")
如果这个文件此时还没有直接在src目录下,而是在src目录下的某个包下,比如在com.gavin包下,此时类加载器要加上包的路径,如下:
InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("com/gavin/dbinfo.properties")
补充一点,ServletContext只有在读取的文件在web应用的根目录下时,才能获取文件的全路径。比如我们在WebRoot文件夹下有一个images文件夹,images文件夹下有一个Servlet.jpg图片,为了得到这个图片的全路径,如下:
// 如何读取到一个文件的全路径,这里会得到在Tomcat的全路径String path = this.getServletContext().getRealPath("/images/Servlet.jpg");
在网站开发中,有很多功能要使用ServletContext,比如
- 网站计数器
- 网站的在线用户显示
- 简单的聊天系统
总之,如果是涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用ServletContext实现。
ServletContext使用建议
因为存在ServletContext中的数据在服务器中会长时间,这样就会占用很多内存,因此在使用ServletContext时,建议不要往里面添加过大的数据!
Servlet 数据库访问
本教程假定您已经了解了 JDBC 应用程序的工作方式。在您开始学习 Servlet 数据库访问之前,请确保您已经有适当的 JDBC 环境设置和数据库。
从基本概念下手,让我们来创建一个简单的表,并在表中创建几条记录。
略
下面的实例演示了如何使用 Servlet 访问 TEST 数据库。
// 加载必需的库import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;import java.sql.*;public class DatabaseAccess extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // JDBC 驱动器名称和数据库的 URL static final String JDBC_DRIVER="com.mysql.jdbc.Driver"; static final String DB_URL="jdbc:mysql://localhost/TEST"; // 数据库的用户名与密码,需要根据自己的设置 static final String USER = "root"; static final String PASS = "password"; // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "数据库结果"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n"); try{ // 注册 JDBC 驱动器 Class.forName("com.mysql.jdbc.Driver"); // 打开一个连接 Connection conn = DriverManager.getConnection(DB_URL,USER,PASS); // 执行 SQL 查询 Statement stmt = conn.createStatement(); String sql; sql = "SELECT id, first, last, age FROM Employees"; ResultSet rs = stmt.executeQuery(sql); // 展开结果集数据库 while(rs.next()){ // 通过字段检索 int id = rs.getInt("id"); int age = rs.getInt("age"); String first = rs.getString("first"); String last = rs.getString("last"); // 输出数据 out.println("ID: " + id + "<br>"); out.println(", Age: " + age + "<br>"); out.println(", First: " + first + "<br>"); out.println(", Last: " + last + "<br>"); } out.println("</body></html>"); // 完成后关闭 rs.close(); stmt.close(); conn.close(); }catch(SQLException se){ // 处理 JDBC 错误 se.printStackTrace(); }catch(Exception e){ // 处理 Class.forName 错误 e.printStackTrace(); }finally{ // 最后是用于关闭资源的块 try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ } try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); } } }}
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
.... <servlet> <servlet-name>DatabaseAccess</servlet-name> <servlet-class>DatabaseAccess</servlet-class> </servlet> <servlet-mapping> <servlet-name>DatabaseAccess</servlet-name> <url-pattern>/DatabaseAccess</url-pattern> </servlet-mapping>....
现在调用这个 Servlet,输入链接:http://localhost:8080/DatabaseAccess,将显示以下响应结果:
数据库结果ID: 100, Age: 18, First: Zara, Last: AliID: 101, Age: 25, First: Mahnaz, Last: FatmaID: 102, Age: 30, First: Zaid, Last: KhanID: 103, Age: 28, First: Sumit, Last: Mittal
Servlet 文件上传
Servlet 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器。上传的文件可以是文本文件或图像文件或任何文档。
下面的 HTML 代码创建了一个文件上传表单。以下几点需要注意:
- 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法。
- 表单 enctype 属性应该设置为 multipart/form-data.
- 表单 action 属性应该设置为在后端服务器上处理文件上传的 Servlet 文件。下面的实例使用了 UploadServlet Servlet 来上传文件。
- 上传单个文件,您应该使用单个带有属性 type=“file” 的 <input …/> 标签。为了允许多个文件上传,请包含多个 name 属性值不同的 input 标签。输入标签具有不同的名称属性的值。浏览器会为每个 input 标签关联一个浏览按钮。
<html><head><title>文件上传表单</title></head><body><h3>文件上传:</h3>请选择要上传的文件:<br /><form action="UploadServlet" method="post" enctype="multipart/form-data"><input type="file" name="file" size="50" /><br /><input type="submit" value="上传文件" /></form></body></html>
这将显示下面的结果,允许用户从本地计算机选择一个文件,当用户点击"上传文件"时,表单会连同从本地计算机选择的文件一起提交:
<b>文件上传:</b> 请选择要上传的文件:<br /> <input type="file" name="file" size="50" /> <br /> <input type="button" value="上传文件" /> <br /> 注:这只是虚拟的表单,不会正常工作。
编写后台 Servlet
以下是 Servlet UploadServlet,会接受上传的文件,并把它储存在目录 <Tomcat-installation-directory>/webapps/data 中。这个目录名也可以使用外部配置来添加,比如 web.xml 中的 context-param 元素,如下所示:
<web-app>....<context-param> <description>Location to store uploaded file</description> <param-name>file-upload</param-name> <param-value> c:\apache-tomcat-5.5.29\webapps\data\ </param-value> </context-param>....</web-app>
以下是 UploadServlet 的源代码,可以一次处理多个文件的上传。在继续操作之前,请确认下列各项:
- 下面的实例依赖于 FileUpload,所以一定要确保在您的 classpath 中有最新版本的 commons-fileupload.x.x.jar 文件。可以从 http://commons.apache.org/fileupload/ 下载。
- FileUpload 依赖于 Commons IO,所以一定要确保在您的 classpath 中有最新版本的 commons-io-x.x.jar 文件。可以从 http://commons.apache.org/io/ 下载。
- 在测试下面实例时,您上传的文件大小不能大于 maxFileSize,否则文件将无法上传。
- 请确保已经提前创建好目录 c:\temp and c:\apache-tomcat-5.5.29\webapps\data。
// 导入必需的 java 库import java.io.*;import java.util.*;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.output.*;public class UploadServlet extends HttpServlet { private boolean isMultipart; private String filePath; private int maxFileSize = 50 * 1024; private int maxMemSize = 4 * 1024; private File file ; public void init( ){ // 获取文件将被存储的位置 filePath = getServletContext().getInitParameter("file-upload"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { // 检查我们有一个文件上传请求 isMultipart = ServletFileUpload.isMultipartContent(request); response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter( ); if( !isMultipart ){ out.println("<html>"); out.println("<head>"); out.println("<title>Servlet upload</title>"); out.println("</head>"); out.println("<body>"); out.println("<p>No file uploaded</p>"); out.println("</body>"); out.println("</html>"); return; } DiskFileItemFactory factory = new DiskFileItemFactory(); // 文件大小的最大值将被存储在内存中 factory.setSizeThreshold(maxMemSize); // Location to save data that is larger than maxMemSize. factory.setRepository(new File("c:\\temp")); // 创建一个新的文件上传处理程序 ServletFileUpload upload = new ServletFileUpload(factory); // 允许上传的文件大小的最大值 upload.setSizeMax( maxFileSize ); try{ // 解析请求,获取文件项 List fileItems = upload.parseRequest(request); // 处理上传的文件项 Iterator i = fileItems.iterator(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet upload</title>"); out.println("</head>"); out.println("<body>"); while ( i.hasNext () ) { FileItem fi = (FileItem)i.next(); if ( !fi.isFormField () ) { // 获取上传文件的参数 String fieldName = fi.getFieldName(); String fileName = fi.getName(); String contentType = fi.getContentType(); boolean isInMemory = fi.isInMemory(); long sizeInBytes = fi.getSize(); // 写入文件 if( fileName.lastIndexOf("\\") >= 0 ){ file = new File( filePath + fileName.substring( fileName.lastIndexOf("\\"))) ; }else{ file = new File( filePath + fileName.substring(fileName.lastIndexOf("\\")+1)) ; } fi.write( file ) ; out.println("Uploaded Filename: " + fileName + "<br>"); } } out.println("</body>"); out.println("</html>"); }catch(Exception ex) { System.out.println(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { throw new ServletException("GET method used with " + getClass( ).getName( )+": POST method required."); } }
编译和运行 Servlet
编译上面的 Servlet UploadServlet,并在 web.xml 文件中创建所需的条目,如下所示:
<servlet> <servlet-name>UploadServlet</servlet-name> <servlet-class>UploadServlet</servlet-class></servlet><servlet-mapping> <servlet-name>UploadServlet</servlet-name> <url-pattern>/UploadServlet</url-pattern></servlet-mapping>
现在尝试使用您在上面创建的 HTML 表单来上传文件。当您在浏览器中访问:http://localhost:8080/UploadFile.htm 时,它会显示下面的结果,这将有助于您从本地计算机上传任何文件。
<b>文件上传:</b> 请选择要上传的文件:<br /> <input type="file" name="file" size="50" /> <br /> <input type="button" value="上传文件" />
如果您的 Servelt 脚本能正常工作,那么您的文件会被上传到 c:\apache-tomcat-5.5.29\webapps\data\ 目录中。
2.1.11 Servlet 处理日期
使用 Servlet 的最重要的优势之一是,可以使用核心 Java 中的大多数可用的方法。本章将讲解 Java 提供的 java.util 包中的 Date 类,这个类封装了当前的日期和时间。
Date 类支持两个构造函数。第一个构造函数初始化当前日期和时间的对象。
Date( )
下面的构造函数接受一个参数,该参数等于 1970 年 1 月 1 日午夜以来经过的毫秒数。
Date(long millisec)
一旦您有一个可用的 Date 对象,您可以调用下列任意支持的方法来使用日期:
序号 | 方法 & 描述 |
---|---|
1 | boolean after(Date date) 如果调用的 Date 对象中包含的日期在 date 指定的日期之后,则返回 true,否则返回 false。 |
2 | boolean before(Date date) 如果调用的 Date 对象中包含的日期在 date 指定的日期之前,则返回 true,否则返回 false。 |
3 | Object clone( ) 重复调用 Date 对象。 |
4 | int compareTo(Date date) 把调用对象的值与 date 的值进行比较。如果两个值是相等的,则返回 0。如果调用对象在 date 之前,则返回一个负值。如果调用对象在 date 之后,则返回一个正值。 |
5 | int compareTo(Object obj) 如果 obj 是 Date 类,则操作等同于 compareTo(Date)。否则,它会抛出一个 ClassCastException。 |
6 | boolean equals(Object date) 如果调用的 Date 对象中包含的时间和日期与 date 指定的相同,则返回 true,否则返回 false。 |
7 | long getTime( ) 返回 1970 年 1 月 1 日以来经过的毫秒数。 |
8 | int hashCode( ) 为调用对象返回哈希代码。 |
9 | void setTime(long time) 设置 time 指定的时间和日期,这表示从 1970 年 1 月 1 日午夜以来经过的时间(以毫秒为单位)。 |
10 | String toString( ) 转换调用的 Date 对象为一个字符串,并返回结果。 |
获取当前的日期和时间
在 Java Servlet 中获取当前的日期和时间是非常容易的。您可以使用一个简单的 Date 对象的 toString() 方法来输出当前的日期和时间,如下所示:
// 导入必需的 java 库import java.io.*;import java.util.Date;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class CurrentDate extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "显示当前的日期和时间"; Date date = new Date(); String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<h2 align=\"center\">" + date.toString() + "</h2>\n" + "</body></html>"); }}
现在,让我们来编译上面的 Servlet,并在 web.xml 文件中创建适当的条目,然后通过访问 http://localhost:8080/CurrentDate 来调用该 Servlet。这将会产生如下的结果:
尝试刷新 URL http://localhost:8080/CurrentDate,每隔几秒刷新一次您都会发现显示时间的差异。
日期比较
正如上面所提到的,您可以在 Servlet 中使用所有可用的 Java 方法。如果您需要比较两个日期,以下是方法:
- 您可以使用 getTime() 来获取两个对象自 1970 年 1 月 1 日午夜以来经过的时间(以毫秒为单位),然后对这两个值进行比较。
- 您可以使用方法 before( )、after( ) 和 equals( )。由于一个月里 12 号在 18 号之前,例如,new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回 true。
- 您可以使用 compareTo( ) 方法,该方法由 Comparable 接口定义,由 Date 实现。
使用 SimpleDateFormat 格式化日期
SimpleDateFormat 是一个以语言环境敏感的方式来格式化和解析日期的具体类。 SimpleDateFormat 允许您选择任何用户定义的日期时间格式化的模式。
让我们修改上面的实例,如下所示:
// 导入必需的 java 库import java.io.*;import java.text.*;import java.util.Date;import javax.servlet.*;import javax.servlet.http.*;// 扩展 HttpServlet 类public class CurrentDate extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "显示当前的日期和时间"; Date dNow = new Date( ); SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz"); String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<h2 align=\"center\">" + ft.format(dNow) + "</h2>\n" + "</body></html>"); }}
再次编译上面的 Servlet,然后通过访问 http://localhost:8080/CurrentDate 来调用该 Servlet。这将会产生如下的结果:
简单的日期格式的格式代码
使用事件模式字符串来指定时间格式。在这种模式下,所有的 ASCII 字母被保留为模式字母,这些字母定义如下:
字符 | 描述 | 实例 |
---|---|---|
G | Era 指示器 | AD |
y | 四位数表示的年 | 2001 |
M | 一年中的月 | July 或 07 |
d | 一月中的第几天 | 10 |
h | 带有 A.M./P.M. 的小时(1~12) | 12 |
H | 一天中的第几小时(0~23) | 22 |
m | 一小时中的第几分 | 30 |
s | 一分中的第几秒 | 55 |
S | 毫秒 | 234 |
E | 一周中的星期几 | Tuesday |
D | 一年中的第几天 | 360 |
F | 所在的周是这个月的第几周 | 2 (second Wed. in July) |
w | 一年中的第几周 | 40 |
W | 一月中的第几周 | 1 |
a | A.M./P.M. 标记 | PM |
k | 一天中的第几小时(1~24) | 24 |
K | 带有 A.M./P.M. 的小时(0~11) | 10 |
z | 时区 | Eastern Standard Time |
’ | Escape for text | Delimiter |
" | 单引号 | ` |
2.1.12 Servlet 网页重定向
当文档移动到新的位置,我们需要向客户端发送这个新位置时,我们需要用到网页重定向。当然,也可能是为了负载均衡,或者只是为了简单的随机,这些情况都有可能用到网页重定向。
重定向请求到另一个网页的最简单的方式是使用 response 对象的 sendRedirect() 方法。下面是该方法的定义: 将请求重定向到另一页的最简单的方法是,用方法的sendRedirect()的响应对象。以下是这种方法的定义:
public void HttpServletResponse.sendRedirect(String location)throws IOException
该方法把响应连同状态码和新的网页位置发送回浏览器。您也可以通过把 setStatus() 和 setHeader() 方法一起使用来达到同样的效果:
....
String site = "http://www.newpage.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
....
本实例展示 Servlet 如何将页面重定向到另一个位置:
import java.io.*;
import java.sql.Date;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PageRedirect extends HttpServlet{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 设置响应内容类型
response.setContentType("text/html");
// 要重定向的新位置
String site = new String("http://www.w3cschool.cn");
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
}
}
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
.... <servlet> <servlet-name>PageRedirect</servlet-name> <servlet-class>PageRedirect</servlet-class> </servlet> <servlet-mapping> <servlet-name>PageRedirect</servlet-name> <url-pattern>/PageRedirect</url-pattern> </servlet-mapping>....
现在通过访问 URL http://localhost:8080/PageRedirect 来调用这个 Servlet。这将把您转到给定的 URL http://www.w3cschool.cn。
2.1.13 Servlet 点击计数器
网页点击计数器
很多时候,您可能有兴趣知道网站的某个特定页面上的总点击量。使用 Servlet 来计算这些点击量是非常简单的,因为一个 Servlet 的生命周期是由它运行所在的容器控制的。
以下是实现一个简单的基于 Servlet 生命周期的网页点击计数器需要采取的步骤:
- 在 init() 方法中初始化一个全局变量。
- 每次调用 doGet() 或 doPost() 方法时,都增加全局变量。
- 如果需要,您可以使用一个数据库表来存储全局变量的值在 destroy() 中。在下次初始化 Servlet 时,该值可在 init() 方法内被读取。这一步是可选的。
- 如果您只想对一个 session 会话计数一次页面点击,那么请使用 isNew() 方法来检查该 session 会话是否已点击过相同页面。这一步是可选的。
- 您可以通过显示全局计数器的值,来在网站上展示页面的总点击量。这一步是可选的。
在这里,我们假设 Web 容器将无法重新启动。如果是重新启动或 Servlet 被销毁,计数器将被重置。
本实例演示了如何实现一个简单的网页点击计数器:
import java.io.*;import java.sql.Date;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;public class PageHitCounter extends HttpServlet{ private int hitCount; public void init() { // 重置点击计数器 hitCount = 0; } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); // 该方法在 Servlet 被点击时执行 // 增加 hitCount hitCount++; PrintWriter out = response.getWriter(); String title = "总点击量"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<h2 align=\"center\">" + hitCount + "</h2>\n" + "</body></html>"); } public void destroy() { // 这一步是可选的,但是如果需要,您可以把 hitCount 的值写入到数据库 } }
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
.... <servlet> <servlet-name>PageHitCounter</servlet-name> <servlet-class>PageHitCounter</servlet-class> </servlet> <servlet-mapping> <servlet-name>PageHitCounter</servlet-name> <url-pattern>/PageHitCounter</url-pattern> </servlet-mapping>....
现在通过访问 URL http://localhost:8080/PageHitCounter 来调用这个 Servlet。这将会在每次页面刷新时,把计数器的值增加 1,结果如下所示:
网站点击计数器
很多时候,您可能有兴趣知道整个网站的总点击量。在 Servlet 中,这也是非常简单的,我们可以使用过滤器做到这一点。
以下是实现一个简单的基于过滤器生命周期的网站点击计数器需要采取的步骤:
- 在过滤器的 init() 方法中初始化一个全局变量。
- 每次调用 doFilter 方法时,都增加全局变量。
- 如果需要,您可以使用一个数据库表来存储全局变量的值在过滤器的 destroy() 中。在下次初始化过滤器时,该值可在 init() 方法内被读取。这一步是可选的。
在这里,我们假设 Web 容器将无法重新启动。如果是重新启动或 Servlet 被销毁,点击计数器将被重置。
本实例演示了如何实现一个简单的网站点击计数器:
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;public class SiteHitCounter implements Filter{ private int hitCount; public void init(FilterConfig config) throws ServletException{ // 重置点击计数器 hitCount = 0; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException { // 把计数器的值增加 1 hitCount++; // 输出计数器 System.out.println("网站访问统计:"+ hitCount ); // 把请求传回到过滤器链 chain.doFilter(request,response); } public void destroy() { // 这一步是可选的,但是如果需要,您可以把 hitCount 的值写入到数据库 } }
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
....<filter> <filter-name>SiteHitCounter</filter-name> <filter-class>SiteHitCounter</filter-class></filter><filter-mapping> <filter-name>SiteHitCounter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>....
现在访问网站的任意页面,比如 http://localhost:8080/。这将会在每次任意页面被点击时,把计数器的值增加 1,它会在日志中显示以下消息:
网站访问统计: 1网站访问统计: 2网站访问统计: 3网站访问统计: 4网站访问统计: 5..................
2.1.15 Servlet 自动刷新页面
假设有一个网页,它是显示现场比赛成绩或股票市场状况或货币兑换率。对于所有这些类型的页面,您需要定期刷新网页。
Java Servlet 提供了一个机制,使得网页会在给定的时间间隔自动刷新。
刷新网页的最简单的方式是使用响应对象的方法 setIntHeader()。以下是这种方法的定义:
public void setIntHeader(String header, int headerValue)
此方法把头信息 “Refresh” 连同一个表示时间间隔的整数值(以秒为单位)发送回浏览器。
本实例演示了 Servlet 如何使用 setIntHeader() 方法来设置 Refresh 头信息,从而实现自动刷新页面。
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.*;// 扩展 HttpServlet 类public class Refresh extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置刷新自动加载的事件间隔为 5 秒 response.setIntHeader("Refresh", 5); // 设置响应内容类型 response.setContentType("text/html"); // 获取当前的时间 Calendar calendar = new GregorianCalendar(); String am_pm; int hour = calendar.get(Calendar.HOUR); int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); if(calendar.get(Calendar.AM_PM) == 0) am_pm = "AM"; else am_pm = "PM"; String CT = hour+":"+ minute +":"+ second +" "+ am_pm; PrintWriter out = response.getWriter(); String title = "使用 Servlet 自动刷新页面"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n"+ "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<p>当前时间是:" + CT + "</p>\n"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
.... <servlet> <servlet-name>Refresh</servlet-name> <servlet-class>Refresh</servlet-class> </servlet> <servlet-mapping> <servlet-name>Refresh</servlet-name> <url-pattern>/Refresh</url-pattern> </servlet-mapping>....
现在通过访问 URL http://localhost:8080/Refresh 来调用这个 Servlet。这将会每隔 5 秒钟显示一次当前系统时间。运行该 Servlet,并等待查看结果:
2.1.16 Servlet 发送电子邮件
使用 Servlet 发送一封电子邮件是很简单的,但首先您必须在您的计算机上安装 JavaMail API 和 Java Activation Framework)JAF)。
- 您可以从 Java 标准网站下载最新版本的 JavaMail(版本 1.2)。
- 您可以从 Java 标准网站下载最新版本的 JAF(版本 1.1.1)。
下载并解压缩这些文件,在新创建的顶层目录中,您会发现这两个应用程序的一些 jar 文件。您需要把 mail.jar 和 activation.jar 文件添加到您的 CLASSPATH 中。
下面的实例将从您的计算机上发送一封简单的电子邮件。这里假设您的本地主机已连接到互联网,并支持发送电子邮件。同时确保 Java Email API 包和 JAF 包的所有的 jar 文件在 CLASSPATH 中都是可用的。
// 文件名 SendEmail.javaimport java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;import javax.mail.*;import javax.mail.internet.*;import javax.activation.*;public class SendEmail extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 收件人的电子邮件 ID String to = "abcd@gmail.com"; // 发件人的电子邮件 ID String from = "web@gmail.com"; // 假设您是从本地主机发送电子邮件 String host = "localhost"; // 获取系统的属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认的 Session 对象 Session session = Session.getDefaultInstance(properties); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); try{ // 创建一个默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // 设置 From: header field of the header. message.setFrom(new InternetAddress(from)); // 设置 To: header field of the header. message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 设置 Subject: header field message.setSubject("This is the Subject Line!"); // 现在设置实际消息 message.setText("This is actual message"); // 发送消息 Transport.send(message); String title = "发送电子邮件"; String res = "成功发送消息..."; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<p align=\"center\">" + res + "</p>\n" + "</body></html>"); }catch (MessagingException mex) { mex.printStackTrace(); } }}
现在让我们来编译上面的 Servlet,并在 web.xml 文件中创建以下条目:
.... <servlet> <servlet-name>SendEmail</servlet-name> <servlet-class>SendEmail</servlet-class> </servlet> <servlet-mapping> <servlet-name>SendEmail</servlet-name> <url-pattern>/SendEmail</url-pattern> </servlet-mapping>....
现在通过访问 URL http://localhost:8080/SendEmail 来调用这个 Servlet。这将会发送一封电子邮件到给定的电子邮件 ID abcd@gmail.com,并将显示下面所示的响应:
如果您想要发送一封电子邮件给多个收件人,那么请使用下面的方法来指定多个电子邮件 ID:
void addRecipients(Message.RecipientType type, Address[] addresses)throws MessagingException
下面是对参数的描述:
- type:这将被设置为 TO、CC 或 BCC。在这里,CC 代表抄送,BCC 代表密件抄送。例如 Message.RecipientType.TO。
- addresses:这是电子邮件 ID 的数组。当指定电子邮件 ID 时,您需要使用 InternetAddress() 方法。
下面的实例将从您的计算机上发送一封 HTML 格式的电子邮件。这里假设本地主机**已连接到互联网,并支持发送电子邮件。同时确保 Java Email API 包和 JAF 包的所有的 jar 文件在 CLASSPATH 中都是可用的。
本实例与上一个实例很类似,但是这里我们使用 setContent() 方法来设置第二个参数为 “text/html” 的内容,该参数用来指定 HTML 内容是包含在消息中的。
使用这个实例,您可以发送内容大小不限的 HTML 内容。
// 文件名 SendEmail.javaimport java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;import javax.mail.*;import javax.mail.internet.*;import javax.activation.*;public class SendEmail extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 收件人的电子邮件 ID String to = "abcd@gmail.com"; // 发件人的电子邮件 ID String from = "web@gmail.com"; // 假设您是从本地主机发送电子邮件 String host = "localhost"; // 获取系统的属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认的 Session 对象 Session session = Session.getDefaultInstance(properties); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); try{ // 创建一个默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // 设置 From: header field of the header. message.setFrom(new InternetAddress(from)); // 设置 To: header field of the header. message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 设置 Subject: header field message.setSubject("This is the Subject Line!"); // 设置实际的 HTML 消息,内容大小不限 message.setContent("<h1>This is actual message</h1>", "text/html" ); // 发送消息 Transport.send(message); String title = "发送电子邮件"; String res = "成功发送消息..."; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<p align=\"center\">" + res + "</p>\n" + "</body></html>"); }catch (MessagingException mex) { mex.printStackTrace(); } }}
编译并运行上面的 Servlet ,在给定的电子邮件 ID 上发送 HTML 消息。
下面的实例将从计算机上发送一封带有附件的电子邮件。这里假设本地主机已连接到互联网,并支持发送电子邮件。同时确保 Java Email API 包和 JAF 包的所有的 jar 文件在 CLASSPATH 中都是可用的。
// 文件名 SendEmail.javaimport java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;import javax.mail.*;import javax.mail.internet.*;import javax.activation.*;public class SendEmail extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 收件人的电子邮件 ID String to = "abcd@gmail.com"; // 发件人的电子邮件 ID String from = "web@gmail.com"; // 假设您是从本地主机发送电子邮件 String host = "localhost"; // 获取系统的属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认的 Session 对象 Session session = Session.getDefaultInstance(properties); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); try{ // 创建一个默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // 设置 From: header field of the header. message.setFrom(new InternetAddress(from)); // 设置 To: header field of the header. message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // 设置 Subject: header field message.setSubject("This is the Subject Line!"); // 创建消息部分 BodyPart messageBodyPart = new MimeBodyPart(); // 填写消息 messageBodyPart.setText("This is message body"); // 创建一个多部分消息 Multipart multipart = new MimeMultipart(); // 设置文本消息部分 multipart.addBodyPart(messageBodyPart); // 第二部分是附件 messageBodyPart = new MimeBodyPart(); String filename = "file.txt"; DataSource source = new FileDataSource(filename); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(filename); multipart.addBodyPart(messageBodyPart); // 发送完整的消息部分 message.setContent(multipart ); // 发送消息 Transport.send(message); String title = "发送电子邮件"; String res = "成功发送电子邮件..."; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<p align=\"center\">" + res + "</p>\n" + "</body></html>"); }catch (MessagingException mex) { mex.printStackTrace(); } }}
编译并运行上面的 Servlet ,在给定的电子邮件 ID 上发送带有文件附件的消息。
如果需要向电子邮件服务器提供用户 ID 和密码进行身份认证,那么需要设置如下属性:
props.setProperty("mail.user", "myuser"); props.setProperty("mail.password", "mypwd");
电子邮件发送机制的其余部分与上面讲解的保持一致。
2.1.17 Servlet 包
涉及到 WEB-INF 子目录的 Web 应用程序结构是所有的 Java web 应用程序的标准,并由 Servlet API 规范指定。给定一个顶级目录名 myapp,目录结构如下所示:
/myapp /images /WEB-INF /classes /lib
WEB-INF 子目录中包含应用程序的部署描述符,名为 web.xml。所有的 HTML 文件都位于顶级目录 myapp 下。对于 admin 用户,您会发现 ROOT 目录是 myApp 的父目录。
创建包中的 Servlet
WEB-INF/classes 目录包含了所有的 Servlet 类和其他类文件,类文件所在的目录结构与他们的包名称匹配。例如,如果您有一个完全合格的类名称 com.myorg.MyServlet,那么这个 Servlet 类必须位于以下目录中:
/myapp/WEB-INF/classes/com/myorg/MyServlet.class
下面的例子创建包名为 com.myorg 的 MyServlet 类。
// 为包命名package com.myorg; // 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class MyServlet extends HttpServlet { private String message; public void init() throws ServletException { // 执行必需的的初始化 message = "Hello World"; } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); // 实际的逻辑是在这里 PrintWriter out = response.getWriter(); out.println("<h1>" + message + "</h1>"); } public void destroy() { // 什么也不做 }}
编译包中的类与编译其他的类没有什么大的不同。最简单的方法是让 java 文件保留完全限定路径,如上面提到的类,将被保留在 com.myorg 中。还需要在 CLASSPATH 中添加该目录。
假设环境已正确设置,进入 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 目录,并编译 MyServlet.java,如下所示:
$ javac MyServlet.java
如果 Servlet 依赖于其他库,那么您必须在 CLASSPATH 中也要引用那些 JAR 文件。这里只引用了 servlet-api.jar JAR 文件,因为在 Hello World 程序中并没有使用任何其他库。
该命令行使用内置的 javac 编译器,它是 Sun Microsystems Java 软件开发工具包(JDK,全称 Java Software Development Kit)附带的。 Microsystems的Java软件开发工具包(JDK)。为了让该命令正常工作,必须包括您在 PATH 环境变量中所使用的 Java SDK 的位置。
如果一切顺利,上述编译会在同一目录下生成 MyServlet.class 文件。下一节将解释如何把一个已编译的 Servlet 部署到生产中。
Servlet 打包部署
默认情况下,Servlet 应用程序位于路径 <Tomcat-installation-directory>/webapps/ROOT 下,且类文件放在 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中。
如果您有一个完全合格的类名称 com.myorg.MyServlet,那么这个 Servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中,您需要在位于 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目:
<servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.myorg.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping>
上面的条目要被创建在 web.xml 文件中的 <web-app>…</web-app> 标签内。在该文件中可能已经有各种可用的条目,但不要在意。
到这里,您基本上已经完成了,现在让我们使用 <Tomcat-installation-directory>\bin\startup.bat(在 Windows 上)或 <Tomcat-installation-directory>/bin/startup.sh(在 Linux/Solaris 等上)启动 tomcat 服务器,最后在浏览器的地址栏中输入 http://localhost:8080/MyServlet。如果一切顺利,您会看到下面的结果:
2.1.18 Servlet 调试
测试/调试 Servlet 始终是开发使用过程中的难点。Servlet 往往涉及大量的客户端/服务器交互,可能会出现错误但又难以重现。
这里有一些提示和建议,可以帮助调试。
System.out.println()
System.out.println() 是作为一个标记来使用的,用来测试一段特定的代码是否被执行。我们也可以打印出变量的值。此外:
- 由于 System 对象是核心 Java 对象的一部分,它可以在不需要安装任何额外类的情况下被用于任何地方。这包括 Servlet、JSP、RMI、EJB’s、普通的 Beans 和类,以及独立的应用程序。
- 与在断点处停止不同,写入到 System.out 不会干扰到应用程序的正常执行流程,这使得它在时序是至关重要的时候显得尤为有价值。
下面是使用 System.out.println() 的语法:
System.out.println("Debugging message");
通过上面的语法生成的所有消息将被记录在 Web 服务器日志文件中。
消息日志
使用适当的日志记录方法来记录所有调试、警告和错误消息,这是非常好的想法,推荐使用 log4J 来记录所有的消息。
Servlet API 还提供了一个简单的输出信息的方式,使用 log() 方法,如下所示:
// 导入必需的 java 库import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class ContextLog extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String par = request.getParameter("par1"); // 调用两个 ServletContext.log 方法 ServletContext context = getServletContext( ); if (par == null || par.equals("")) // 通过 Throwable 参数记录版本 context.log("No message received:", new IllegalStateException("Missing parameter")); else context.log("Here is the visitor's message: " + par); response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter( ); String title = "Context Log"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<h2 align=\"center\">Messages sent</h2>\n" + "</body></html>"); } //doGet}
ServletContext 把它的文本消息记录到 Servlet 容器的日志文件中。对于 Tomcat,这些日志可以在 <Tomcat-installation-directory>/logs 目录中找到。
这些日志文件确实对新出现的错误或问题的频率给出指示。正因为如此,建议在通常不会发生的异常的 catch 子句中使用 log() 函数。
使用 JDB 调试器
可以使用调试 applet 或应用程序的 jdb 命令来调试 Servlet。
为了调试一个 Servlet,我们可以调试 sun.servlet.http.HttpServer,然后把它看成是 HttpServer 执行 Servlet 来响应浏览器端的 HTTP 请求。这与调试 applet 小程序非常相似。与调试 applet 不同的是,实际被调试的程序是 sun.applet.AppletViewer。
大多数调试器会自动隐藏如何调试 applet 的细节。同样的,对于 servlet,您必须帮调试器执行以下操作:
- 设置您的调试器的类路径 classpath,以便它可以找到 sun.servlet.http.Http-Server 和相关的类。
- 设置您的调试器的类路径 classpath,以便它可以找到您的 servlet 和支持的类,通常是在 server_root/servlets 和 server_root/classes 中。
您通常不会希望 server_root/servlets 在您的 classpath 中,因为它会禁用 servlet 的重新加载。但是这种包含规则对于调试是非常有用的。它允许您的调试器在 HttpServer 中的自定义 Servlet 加载器加载 Servlet 之前在 Servlet 中设置断点。
如果您已经设置了正确的类路径 classpath,就可以开始调试 sun.servlet.http.HttpServer。可以在您想要调试的 Servlet 代码中设置断点,然后通过 Web 浏览器使用给定的 Servlet(http://localhost:8080/servlet/ServletToDebug)向 HttpServer 发出请求。您会看到程序执行到断点处会停止。
使用注释
代码中的注释有助于以各种方式进行调试。注释可用于调试过程的很多其他方式中。
该 Servlet 使用 Java 注释和单行注释(//…),多行注释(/* …*/)可用于暂时移除部分 Java 代码。如果 bug 消失,仔细看看您刚才注释的代码并找出问题所在。
客户端和服务器端头信息
有时,当一个 Servlet 并没有像预期那样时,查看原始的 HTTP 请求和响应是非常有用的。如果您熟悉 HTTP 结构,您可以阅读请求和响应,看看这些头信息究竟是什么。
重要的调试技巧
下面列出了一些 Servlet 调试的技巧:
- 请注意,server_root/classes 不会重载,而 server_root/servlets 可能会。
- 要求浏览器显示它所显示的页面的原始内容。这有助于识别格式的问题。它通常是"视图"菜单下的一个选项。
- 通过强制执行完全重新加载页面来确保浏览器还没有缓存前一个请求的输出。在 Netscape Navigator 中,请使用 Shift-Reload,在 Internet Explorer 中,请使用 Shift-Refresh。
- 请确认 servlet 的 init() 方法接受一个 ServletConfig 参数,并调用 super.init(config)。
2.1.19 Servlet 国际化
在我们开始之前,先来看看三个重要术语:
- **国际化(i18n):**这意味着一个网站提供了不同版本的翻译成访问者的语言或国籍的内容。
- **本地化(l10n):**这意味着向网站添加资源,以使其适应特定的地理或文化区域,例如网站翻译成印地文(Hindi)。
- **区域设置(locale):**这是一个特殊的文化或地理区域。它通常指语言符号后跟一个下划线和一个国家符号。例如 “en_US” 表示针对 US 的英语区域设置。
当建立一个全球性的网站时有一些注意事项。本教程不会讲解这些注意事项的完整细节,但它会通过一个很好的实例向您演示如何通过差异化定位(即区域设置)来让网页以不同语言呈现。
Servlet 可以根据请求者的区域设置拾取相应版本的网站,并根据当地的语言、文化和需求提供相应的网站版本。以下是 request 对象中返回 Locale 对象的方法。
java.util.Locale request.getLocale()
检测区域设置
下面列出了重要的区域设置方法,您可以使用它们来检测请求者的地理位置、语言和区域设置。下面所有的方法都显示了请求者浏览器中设置的国家名称和语言名称
号 | 方法 & 描述 |
---|---|
1 | String getCountry() 该方法以 2 个大写字母形式的 ISO 3166 格式返回该区域设置的国家/地区代码。 |
2 | String getDisplayCountry() 该方法返回适合向用户显示的区域设置的国家的名称。 |
3 | String getLanguage() 该方法以小写字母形式的 ISO 639 格式返回该区域设置的语言代码。 |
4 | String getDisplayLanguage() 该方法返回适合向用户显示的区域设置的语言的名称。 |
5 | String getISO3Country() 该方法返回该区域设置的国家的三个字母缩写。 |
6 | String getISO3Language() 该方法返回该区域设置的语言的三个字母的缩写。 |
本实例演示了如何显示某个请求的语言和相关的国家:
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.Locale;public class GetLocale extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取客户端的区域设置 Locale locale = request.getLocale(); String language = locale.getLanguage(); String country = locale.getCountry(); // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "检测区域设置"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + language + "</h1>\n" + "<h2 align=\"center\">" + country + "</h2>\n" + "</body></html>"); }}
语言设置
Servlet 可以输出以西欧语言(如英语、西班牙语、德语、法语、意大利语、荷兰语等)编写的页面。在这里,为了能正确显示所有的字符,设置 Content-Language 头是非常重要的。
第二点是使用 HTML 实体显示所有的特殊字符,例如,“ñ” 表示 “ñ”,"¡" 表示 “¡”,如下所示:
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.Locale;public class DisplaySpanish extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); // 设置西班牙语言代码 response.setHeader("Content-Language", "es"); String title = "En Español"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1>" + "En Español:" + "</h1>\n" + "<h1>" + "¡Hola Mundo!" + "</h1>\n" + "</body></html>"); }}
特定于区域设置的日期
可以使用 java.text.DateFormat 类及其静态方法 getDateTimeInstance() 来格式化特定于区域设置的日期和时间。下面的实例演示了如何格式化特定于某个给定的区域设置的日期:
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.Locale;import java.text.DateFormat;import java.util.Date;public class DateLocale extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); // 获取客户端的区域设置 Locale locale = request.getLocale( ); String date = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.SHORT, locale).format(new Date( )); String title = "特定于区域设置的日期"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + date + "</h1>\n" + "</body></html>"); }}
特定于区域设置的货币
您可以使用 java.text.NumberFormat 类及其静态方法 getCurrencyInstance() 来格式化数字(比如 long 类型或 double 类型)为特定于区域设置的货币。下面的实例演示了如何格式化特定于某个给定的区域设置的货币:
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.Locale;import java.text.NumberFormat;import java.util.Date;public class CurrencyLocale extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); // 获取客户端的区域设置 Locale locale = request.getLocale( ); NumberFormat nft = NumberFormat.getCurrencyInstance(locale); String formattedCurr = nft.format(1000000); String title = "特定于区域设置的货币"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + formattedCurr + "</h1>\n" + "</body></html>"); }}
特定于区域设置的百分比
您可以使用 java.text.NumberFormat 类及其静态方法 getPercentInstance() 来格式化特定于区域设置的百分比。下面的实例演示了如何格式化特定于某个给定的区域设置的百分比:
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import java.util.Locale;import java.text.NumberFormat;import java.util.Date;public class PercentageLocale extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html"); PrintWriter out = response.getWriter(); // 获取客户端的区域设置 Locale locale = request.getLocale( ); NumberFormat nft = NumberFormat.getPercentInstance(locale); String formattedPerc = nft.format(0.51); String title = "特定于区域设置的百分比"; String docType = "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + formattedPerc + "</h1>\n" + "</body></html>"); }}
2.1.20 Servlet容器
Servlet容器主要是JavaWeb应用提供运行时环境,所以也可以称之为JavaWeb应用容器,或者Servlet/JSP容器。Servlet容器主要负责管理Servlet、JSP的生命周期以及它们的共享数据。
Servlet容器有哪些:
目前最流行的Servlet容器软件包括: Tomcat、Jetty、Jboss等。
-
Tomcat
Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。
-
Jetty
Jetty 是一个开源的servlet容器,它为基于Java的web容器,例如JSP和servlet提供运行环境。Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布。开发人员可以将Jetty容器实例化成一个对象,可以迅速为一些独立运行(stand-alone)的Java应用提供网络和web连接。
-
Jboss
Jboss是一个基于J2EE的开放源代码的应用服务器。 JBoss代码遵循LGPL许可,可以在任何商业应用中免费使用。JBoss是一个管理EJB的容器和服务器,支持EJB 1.1、EJB 2.0和EJB3的规范。但JBoss核心服务不包括支持servlet/JSP的WEB容器,一般与Tomcat或Jetty绑定使用。
Servlet是和平台无关的服务器端组件(java编写的,跨平台),它运行在Servlet容器中。
Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和客户的通信采用“请求/响应”的模式 Servlet可完成如下功能:
1、创建并返回基于客户请求的动态HTML页面;
2、创建可嵌入到现有HTML 页面中的部分HTML 页面(HTML 片段);
3、与其它服务器资源(如数据库或基于Java的应用程序)进行通信。
Servlet容器响应客户请求过程:
Serlvet和JSP的区别
JSP作为Servlet技术的扩展,经常会有人将jsp和Servlet搞混。
1、Servlet在Java代码中可以通过HttpServletResponse对象动态输出HTML内容。
2、JSP是在静态HTML内容中嵌入Java代码,然后Java代码在被动态执行后生成HTML内容。
servlet和jsp的各自的特点
1、Servlet虽然能够很好地组织业务逻辑代码,但是在Java源文件中,因为是通过字符串拼接的方式生成动态HTML内容,这样就容易导致代码维护困难、可读性差。
2、JSP虽然规避了Servlet在生成HTML内容方面的劣势,但是在HTML中混入大量、复杂的业务逻辑。
通过MVC双剑合璧
JSP和Servlet都有自身的适用环境,那么有没有什么办法能够让它们发挥各自的优势呢?答案是肯有的,MVC模式就能够完美解决这一问题。
MVC模式,是Model-View-Controller的简称,是软件工程中的一种软件架构模式,分为三个基本部分,分别是:模型(Model)、视图(View)和控制器(Controller):
Controller——负责转发请求,对请求进行处理
View——负责界面显示
Model——业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现
在JSP/Servlet开发的软件系统中,这三个部分的描述如下所示:
- Web浏览器发送HTTP请求到服务端,然后被Controller(Servlet)获取并进行处理(例如参数解析、请求转发);
- Controller(Servlet)调用核心业务逻辑——Model部分,获得结果;
- Controller(Servlet)将逻辑处理结果交给View(JSP),动态输出HTML内容;
- 动态生成的HTML内容返回到浏览器显示。
MVC模式在Web开发中有很大的优势,它完美规避了JSP与Servlet各自的缺点,让Servlet只负责业务逻辑部分,而不会生成HTML代码;同时JSP中也不会充斥着大量的业务代码,这样能大提高了代码的可读性和可维护性。
2.2 DispatcherServlet
2.2.1 概述
DispatcherServlet属于servlet,所以位于Tomcat等服务器容器中,而HandleMapping、ViewResolver 属于Spring所以位于SpringMVC的配置文件中,Contrlloer以及ModeView则位于src文件中,处理具体的逻辑业务。
DispatcherServlet
这个控件是SpringMVC最核心的一个控件,顾名思义其就是一个Servlet,是Spring写好的一个 Servlet。
HandlerMapping
控件标明了路径与Controller的对应关系,不同的路径访问不同的Controller。
Controller
用来处理业务逻辑的Java类。
ModelAndView
Mode是用来绑定处理后所得的数据,而View是视图名。
ViewResolver
视图解析器明确了视图名与视图对象的关系,是调用demo.jsp还是调用demo.html,以及明确视图的位置。
2.2.2 原理
2.2.3 使用流程
-
配置DispatcherServlet
DispatcherServlet属于Servlet,所以要在web.xml文件中配置。init-param为该Servlet启动所需参数。DispatcherServlet会读取初始化contextConfigLocation参数里面的值从而获取spring的配置位置,然后自启动容器。
<!-- 配置前端控制器,配置Servlet --> <servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!--配置请求路径--> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping>
-
HandlerMapping
mvc:annotation-driven 配置mvc注解扫描 可以用注解@RequestMapping(“url”)加在方法上简化配置,prop标明路径和XXController的关系。
<!--开启mvc注解扫描--><mvc:annotation-driven/><!--创建Controller bean--><bean id="loginController" class="包名+类名"/><bean class="org.springframework.web.servlrt.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/login.form">loginController</prop> </props> </property></bean>
-
Controller
处理getData.form该路径的业务逻辑
@Controllerpublic class DataController { @RequestMapping("getData.form") public ModeAndView hello(String stationId) { System.out.println("hello"); return new ModeAndView("hello") }}
-
ModeAndView
两种ModeAndView的构造方法,其中第一个视图名,第二个需要绑定的数据。
ModeAndView(String viewName)ModeAndView(String viewName, Map data)
-
ViewResolver
前缀+视图名+后缀=映射到页面
<!-- 配置视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".html"></property></bean>
3、模板引擎
3.1 freemaker
3.2 thmyleaf
4、组件整合
4.1 MySQL
以spring-jdbc方式,连接使用MySQL
4.1.1 POM依赖
<!-- 数据库--><dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version></dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.9</version></dependency>
4.1.2 设置properties文件,并配置spring的xml文件
db.properties
jdbc.user=rootjdbc.password=wy1230jdbc.driverClass=com.mysql.jdbc.Driverjdbc.jdbcUrl=jdbc:mysql://localhost:3305/myProject
spring-db.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--加载properties资源文件 --> <context:property-placeholder location="classpath:*.properties"/> <!--配置数据源--><!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">--><!-- <property name="user" value="${jdbc.user}"/>--><!-- <property name="password" value="${jdbc.password}"/>--><!-- <property name="driverClass" value="${jdbc.driverClass}"/>--><!-- <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>--><!-- </bean>--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.jdbcUrl}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> <property name="driverClassName" value="${jdbc.driverClass}"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean></beans>
4.1.3 操作MySQL的Service类
User.java
package com.wuy.model.pojo;public class User { private Integer id; private String name; private String age; private String email; private String passwd; public User() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age='" + age + '\'' + ", email='" + email + '\'' + ", passwd='" + passwd + '\'' + '}'; }}
UserService.java
package com.wuy.service;import com.wuy.model.pojo.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.stereotype.Service;@Servicepublic class UserService { @Autowired private JdbcTemplate jdbcTemplate; public User getUserById(Integer id) { RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class); return jdbcTemplate.queryForObject("SELECT * FROM user WHERE id=?", rowMapper, id); }}
4.2 Redis
4.2.1 POM依赖
注意jedis和spring-data-redis之间的版本,否则会出现冲突!
<!-- redis--><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.1</version></dependency><dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.1.3.RELEASE</version></dependency>
4.2.2 设置properties参数文件,并配置spring的xml文件
redis.properties
redis.host=127.0.0.1redis.port=6379redis.password=""redis.maxIdle=400redis.maxTotal=6000redis.maxWaitMillis=1000redis.blockWhenExhausted=trueredis.testOnBorrow=trueredis.timeout=100000defaultCacheExpireTime=60
spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd" default-lazy-init="false"> <!-- 加载配置文件 --> <context:property-placeholder location="classpath:redis.properties" /> <!-- redis数据源 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大空闲数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!-- 最大空连接数 --> <property name="maxTotal" value="${redis.maxTotal}" /> <!-- 最大等待时间 --> <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <!-- 连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true --> <property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" /> <!-- 返回连接时,检测连接是否成功 --> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- Spring-redis连接池管理工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!-- IP地址 --> <property name="hostName" value="${redis.host}" /> <!-- 端口号 --> <property name="port" value="${redis.port}" /> <!-- 超时时间 默认2000--> <property name="timeout" value="${redis.timeout}" /> <!-- 连接池配置引用 --> <property name="poolConfig" ref="poolConfig" /> <!-- usePool:是否使用连接池 --> <property name="usePool" value="true"/> </bean> <!-- redis template definition --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <!--开启事务 --> <property name="enableTransactionSupport" value="true"></property> </bean> <!--自定义redis工具类,在需要缓存的地方注入此类 --> <bean id="redisService" class="com.wuy.service.RedisService"> <property name="redisTemplate" ref="redisTemplate" /> </bean></beans>
4.2.3 操作Redis的Service类
package com.wuy.service;import org.springframework.data.redis.core.RedisTemplate;public class RedisService { private RedisTemplate<String, Object> redisTemplate; public RedisService() { } public RedisTemplate<String, Object> getRedisTemplate() { return redisTemplate; } public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { return false; } } public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } }}
4.3 MQ
4.3.1 ActiveMQ
4.4 Log
4.5 单元测试
5、SSO单点登录
5.1 Session
5.2 Token
5.3 JWT
Json Web Token
5.4 Oauth2.0
SpringBoot
1、概述
1.1 简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9KHDOgsm-1640940588643)(https://spring.io/images/diagram-microservices-88e01c7d34c688cb49556435c130d352.svg)]
2、自动装配原理
3、常用注解
4、中间件整合
4.1 整合MyBatis
-
pom依赖
<!-- mysql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version></dependency>
-
配置application.properties
server.port=8080# databasespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/test_sqlspring.datasource.username=rootspring.datasource.password=Sn@wy1230# mybatismybatis.mapper-locations=classpath*:com/wuy/springbootdemo/model/dao/conf/*.xmlmybatis.type-aliases-package=com.wuy.springbootdemo.model.dao
-
实体类和mapper配置
- 实体类
package com.wuy.springbootdemo.model.pojo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class UserInfo { @Id private Integer id; @Column private String username; @Column private String password; @Column private String name; @Column private String nickname; @Column private Integer gender; @Column private String phone_number; @Column private String qq_number; @Column private String host_url; public UserInfo() { } public UserInfo(Integer id, String username, String password, String name, String nickname, Integer gender, String phone_number, String qq_number, String host_url) { this.id = id; this.username = username; this.password = password; this.name = name; this.nickname = nickname; this.gender = gender; this.phone_number = phone_number; this.qq_number = qq_number; this.host_url = host_url; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public String getPhone_number() { return phone_number; } public void setPhone_number(String phone_number) { this.phone_number = phone_number; } public String getQq_number() { return qq_number; } public void setQq_number(String qq_number) { this.qq_number = qq_number; } public String getHost_url() { return host_url; } public void setHost_url(String host_url) { this.host_url = host_url; } }
- mapper接口
package com.wuy.springbootdemo.model.dao; import com.wuy.springbootdemo.model.pojo.UserInfo; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @Mapper @Repository public interface UserInfoDao { UserInfo getUserInfoById(Integer id); }
- mapper.xml配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.wuy.springbootdemo.model.dao.UserInfoDao"> <resultMap id="BaseResultMap" type="com.wuy.springbootdemo.model.pojo.UserInfo"> <result column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="password" jdbcType="VARCHAR" property="password" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="nickname" jdbcType="VARCHAR" property="nickname"/> <result column="gender" jdbcType="INTEGER" property="gender"/> <result column="phone_number" jdbcType="VARCHAR" property="phone_number"/> <result column="qq_number" jdbcType="VARCHAR" property="qq_number"/> <result column="host_url" jdbcType="VARCHAR" property="host_url"/> </resultMap> <select id="getUserInfoById" resultType="com.wuy.springbootdemo.model.pojo.UserInfo" parameterType="integer"> select * from user_info where id = #{id} </select> </mapper>
4.2 分页查询,整合pagehelper
-
pom依赖
<!-- pagehelper --><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.10</version></dependency>
-
Mapper类和xxxMapper.xml配置文件
-
Mapper类
package com.wuy.springbootdemo.model.dao;import com.wuy.springbootdemo.model.pojo.UserInfo;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Repository;import java.util.List;import java.util.Map;@Mapper@Repositorypublic interface UserInfoDao { UserInfo getUserInfoById(Integer id); List<UserInfo> getUserInfos(); List<UserInfo> getUserInfoByPage(Map<String, Object> params); long count();}
-
xxxMapper.xml配置文件
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.wuy.springbootdemo.model.dao.UserInfoDao"> <resultMap id="BaseResultMap" type="com.wuy.springbootdemo.model.pojo.UserInfo"> <result column="id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="password" jdbcType="VARCHAR" property="password" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="nickname" jdbcType="VARCHAR" property="nickname"/> <result column="gender" jdbcType="INTEGER" property="gender"/> <result column="phone_number" jdbcType="VARCHAR" property="phone_number"/> <result column="qq_number" jdbcType="VARCHAR" property="qq_number"/> <result column="host_url" jdbcType="VARCHAR" property="host_url"/> </resultMap> <select id="getUserInfoById" resultType="com.wuy.springbootdemo.model.pojo.UserInfo" parameterType="integer"> select * from user_info where id = #{id} </select> <select id="getUserInfos" resultType="com.wuy.springbootdemo.model.pojo.UserInfo"> select * from user_info </select> <select id="getUserInfoByPage" resultType="com.wuy.springbootdemo.model.pojo.UserInfo"> select * from user_info limit #{page}, #{size} </select> <select id="count" resultType="long"> select count(1) from user_info </select></mapper>
-
-
service
package com.wuy.springbootdemo.service.intf;import com.wuy.springbootdemo.utils.Pager;import com.wuy.springbootdemo.model.pojo.UserInfo;public interface UserInfoService { Pager<UserInfo> findUserInfoByPage(int page, int size);}
package com.wuy.springbootdemo.service;import com.github.pagehelper.Page;import com.github.pagehelper.PageHelper;import com.wuy.springbootdemo.utils.Pager;import com.wuy.springbootdemo.model.dao.UserInfoDao;import com.wuy.springbootdemo.model.pojo.UserInfo;import com.wuy.springbootdemo.service.intf.UserInfoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserInfoServiceImpl implements UserInfoService { @Autowired UserInfoDao userInfoDao; @Override public Pager<UserInfo> findUserInfoByPage(int page, int size) { Pager<UserInfo> pager = new Pager<>(); Page<UserInfo> result = PageHelper.startPage(page, size); userInfoDao.getUserInfos(); pager.setRows(result.getResult()); pager.setTotal(result.getTotal()); return pager; }}
-
controller
package com.wuy.springbootdemo.controller;import com.alibaba.fastjson.JSONObject;import com.github.pagehelper.PageInfo;import com.wuy.springbootdemo.model.dao.UserInfoDao;import com.wuy.springbootdemo.model.pojo.UserInfo;import com.wuy.springbootdemo.service.intf.UserInfoService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controllerpublic class UserInfoController { private final static Logger LOGGER = LoggerFactory.getLogger(UserInfoController.class); @Autowired UserInfoDao userInfoDao; @Autowired UserInfoService userInfoService; @ResponseBody @GetMapping(value = "/index") public String index(){ return "index"; } @ResponseBody @GetMapping(value = "/userInfo/{id}") public String getUserInfoById(@PathVariable("id") Integer id) { LOGGER.info("访问:/userInfo/{}", id); return JSONObject.toJSONString(userInfoDao.getUserInfoById(id)); } @ResponseBody @GetMapping(value = "/userInfos/{page}/{size}") public String getUserInfoByPage(@PathVariable("page") Integer page, @PathVariable("size") Integer size) { LOGGER.info("访问:/userInfos/{}/{}", page, size); List<UserInfo> result = userInfoService.findUserInfoByPage(page, size).getRows(); LOGGER.info("result: {}", JSONObject.toJSONString(result)); PageInfo<UserInfo> pageInfo = new PageInfo<UserInfo>(result); return JSONObject.toJSONString(result); }}
SpringCloud
1、概述
1.1 简介
1.2 分布式架构
1.3 微服务架构
RPC框架
1、RPC概述
远程过程调用协议(Remote Procedure Call Protocol,RPC)。RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
在RPC的典型使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中RPC 协议表明程序如何进行网络传输和序列化。
1.1 RPC核心功能
一个RPC的核心功能主要由5部分组成,分别是客户端(Client)、客户端存根(Client Stub)、网络传输模块(Network Service)、服务端存根(Server Stub)和服务端(Server)。
下面介绍核心RPC框架的重要组成:
- Client:服务调用方;
- Client Stub:存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端;
- Server Stub:接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理;
- Server:服务的真正提供者;
- Network Service:底层传输,可以是 TCP 或 HTTP。
实现RPC核心功能的三个关键技术点:
- 服务寻址;
- 数据流的序列化和反序列化
- 网络传输。
1.2 RPC调用过程
一次完整的RPC调用流程如下:
- Client 通过本地调用的方式调用服务;
- Client Stub 接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体;
- Client Stub 找到远程的服务地址,并且将消息通过网络发送给服务端;
- Server Stub 收到消息后进行解码(反序列化操作);
- Server Stub 根据解码结果调用本地的服务进行相关处理;
- Server 本地服务业务处理;
- 处理结果返回给 Server Stub;
- Server Stub 序列化结果。
- Server Stub 将结果通过网络发送至消费方;
- Client Stub 接收到消息,并进行解码(反序列化), Client得到最终结果。
2、Dubbo
ZooKeeper
Eurka
Web服务器
1、Tomcat
1.1 简介
Apache Tomcat是由Apache Software Foundation(ASF)开发的开源Web服务器和Servlet容器。Tomcat包含几个Java EE规范,包括Servlet,JSP,JSP的EL表达式,和WebSocket的,并提供了一个“纯Java” HTTP的Java代码运行在Web服务器环境。
由于 Tomcat 本身也内含了 HTTP 服务器,因此也可以视作单独的 Web 服务器。但是,不能将 Tomcat 和 Apache HTTP 服务器混淆,Apache HTTP 服务器是用 C 语言实现的 HTTPWeb 服务器;这两个 HTTP web server 不是捆绑在一起的。Apache Tomcat 包含了配置管理工具,也可以通过编辑 XML 格式的配置文件来进行配置。
Tomcat 提供了一个 Jasper 编译器用以将 JSP 编译成对应的 Servlet。Tomcat 的 Servlet 引擎通常与 Apache 或者其他 Web 服务器一起工作。除了用于开发过程中的调试以及那些对速度和事务处理只有很小要求的用户,很少会将 Tomcat 单独作为 Web 服务器。但随着版本的更新,正有越来越多的用户将其单独作为 Web 服务器用以那些对速度和可靠性有较高要求的环境中。
和其他 Apache 软件基金会旗下的项目一样,Tomcat 是由该基金会的会员和其他志愿者开发与维护的,并且是一个被置于 Apache 协议之下的开源软件。用户可以根据该协议免费获得其源代码及可执行文件。最初被发布出来的版本是 Tomcat 3.0.x。
1.2 体系结构
Tomcat的体系结构由一系列功能组件组成。这些功能组件在每个Tomcat服务器安装目录中定义,具体来说放在Tomcat的安装文件夹的/conf子目录下,有个命名为server.xml的文件。下面将详细讨论这些组件的作用。
默认情况下,server.xml文件有以下配置,下面将详细介绍其中的元素。
<?xml version='1.0' encoding='utf-8'?><Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service></Server>
1.2.1 Context(上下文)
Context是server.xml文件中最里面的元素,它表示单个Web应用程序。Tomcat在加载应用程序时自动实例化并配置标准Context上下文。另外,Tomcat还将加载在Web应用下\WEB-INF\web.xml文件中定义的配置,并使这些配置可用于应用程序。
1.2.2 Connecter(连接器)
Connector(连接器)的主要功能,是接收连接请求,创建Request和Response对象用于和请求端交换数据,然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine。
Tomcat的默认配置包括一个用于处理HTTP通信的连接器。默认情况下,此连接器等待通过端口8080发出的请求。这就是Tomcat默认欢迎页面的访问URL为http://localhost:8080/的原因。请注意,对所有应用程序的请求都通过此连接器的单个实例进行。每个新请求都会创建一个新线程,该新线程在请求期间将保持活动状态。
Connector的connectionTimeout属性值为20000,表示会话超时时长为5小时33分钟,或者不活动后的20秒后终止,而redirectPort="8443"配置意味着,需要安全套接字层(SSL)输送到来的请求被重定向到端口8443。
AJP连接器使Tomcat只处理动态网页,而让纯HTML服务器(例如Apache Web服务器)处理对静态页面的请求。这样可以最大程度地处理请求。您可能可以注释掉此连接器,因为tomcat本身今天非常快,或者仅当您不打算将Web服务器与Tomcat一起使用时,才可以对其进行注释。
1.2.3 Host(主机)
Host(主机)是网络名称(例如,www.yiidian.com)与Tomcat服务器的关联。Host中可以包含多个Context上下文(即Web应用程序)。可以在同一服务器上定义多个Host主机。例如,如果已经注册了域名yiidian.com,则可以定义主机名,例如tools.yiidian.com和book.yiidian.com。请记住,只有当域名服务器将其名称映射到计算机的IP地址时,才可以从互联网上访问它。
Tomcat的默认配置包括名为localhost的主机。本地主机域名和IP的关联可以在这个系统文件中修改:C:\Windows\System32\drivers\etc\hosts。
Host的appBase属性,定义了Tomcat安装文件夹中的应用程序目录。然后,每个应用程序通过该目录中的路径进行标识。唯一的例外是路径ROOT,该路径已映射到空字符串。本地主机的应用程序根目录是webapps。这意味着目录““C:\Program Files\Apache Software Foundation\Tomcat 8.0\webapps\ROOT” 中的应用程序由空字符串标识。因此,其URL为"http://localhost:8080/"。对于存放在ROOT以外的目录中的其他应用程序,如““C:\Program Files\Apache Software Foundation\Tomcat 8.0\webapps\myapp”,URL类似于"http://localhost:8080/myapp/"。
unpackWARs ="true"属性表示,如果将WAR文件存放到appBase目录中,Tomcat将自动将其解压。如果将此属性设置为false,则应用程序将直接运行该WAR文件。但是,因为以WAR文件方式运行的访问速度较慢,所以通常Tomcat需要在执行时解压缩WAR文件,意味unpackWARs 属性值为true。 autoDeploy ="true"属性表示,如果appBase在Tomcat运行时将应用程序存放到目录中,它将自动进行部署。
1.2.4 Engine(引擎)
Engine(引擎)代表特定服务的请求处理管道。由于服务可能具有多个连接器,因此引擎会接收并处理来自这些连接器的所有请求,并将响应传递回适当的连接器以传输给客户端。
引擎必须包含一个或多个主机,其中一个主机被指定为默认主机。Tomcat的默认配置包括引擎Catalina,该引擎包含主机localhost(显然是默认主机,因为它是唯一的主机)。Catalina引擎处理通过HTTP连接器收到的所有传入请求,并发回相应的响应。它根据请求标头中包含的信息将每个请求转发到正确的主机和上下文。
1.2.5 Service(服务)
Service的作用,是在Connector和Engine外面包了一层,把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine;其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。
用户很少修改Service元素,因为默认实现很简单并且足够。
1.2.6 Server(服务器)
Server(服务器)是最重要的组件,代表Tomcat的实例。Server元素在最顶层,代表整个Tomcat容器,因此它必须是server.xml中唯一一个最外层的元素。一个Server元素中可以有一个或多个Service元素。
Server的主要任务,就是提供一个接口让客户端能够访问到这个Service集合,同时维护它所包含的所有的Service的声明周期,包括如何初始化、如何结束服务、如何找到客户端要访问的Service。
1.2.7 Listener(监听器)
Listener(侦听器)是一个Java对象,通过实现org.apache.catalina.LifecycleListener接口,可以在特定事件发生时执行特定的操作,被监听的事件通常是Tomcat的启动和停止。
- AprLifecycleListener:Tomcat启动时,检查APR库,如果存在则加载。APR,即Apache Portable Runtime,是Apache可移植运行库,可以实现高可扩展性、高性能,以及与本地服务器技术更好的集成。
- JasperListener:在Web应用启动之前初始化Jasper,Jasper是JSP引擎,把JVM不认识的JSP文件解析成java文件,然后编译成class文件供JVM使用。
- JreMemoryLeakPreventionListener:与类加载器导致的内存泄露有关。
- GlobalResourcesLifecycleListener:通过该监听器,初始化< GlobalNamingResources>标签中定义的全局JNDI资源;如果没有该监听器,任何全局资源都不能使用。
- ThreadLocalLeakPreventionListener:当Web应用因thread-local导致的内存泄露而要停止时,该监听器会触发线程池中线程的更新。当线程执行完任务被收回线程池时,活跃线程会一个一个的更新。只有当Web应用(即Context元素)的renewThreadsWhenStoppingContext属性设置为true时,该监听器才有效。
1.2.8 GlobalNamingResource与Realm
Engine组件下定义了Realm组件:
<Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/></Realm>
Realm,可以把它理解成“域”;Realm提供了一种用户密码与web应用的映射关系,从而达到角色安全管理的作用。在本例中,Realm的配置使用name为UserDatabase的资源实现。而该资源在Server元素中使用GlobalNamingResources配置:conf/tomcat-users.xml。
<GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /></GlobalNamingResources>
GlobalNamingResources元素定义了全局资源,通过配置可以看出,该配置是通过读取$TOMCAT_HOME/ conf/tomcat-users.xml实现的。
修改Tomcat默认端口号
默认情况下,Tomcat配置的默认端口是8080。这就是为什么所有已部署的Web应用程序都可以通过URL访问的原因:http://localhost:8080/yourapp。如果希望应该在80端口上运行,则需要将默认端口8080更改为80,这是HTTP连接器的默认端口。
要更改此端口,只需要打开server.xml,
并找到以下内容:
<Connector port="8080" //修改这个端口 protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
并将其更改为以下内容即可。
<Connector port="80" //改为80端口 protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
1.3 Tomcat管理器
1.3.1 什么是Tomcat管理器
当使用Tomcat的Webapps目录,对Web应用进行管理时,无法控制单个Web应用的启动与停止
为此,Tomcat提供了一个管理平台,该平台列出了所有的Web应用及其状态,并且,提供了控制每个Web应用的启动、停止与卸载的功能。
1.3.2 如何进入Tomcat管理器
在浏览器的地址栏中输入 http://localhost:8080/
打开Tomcat首页,单击,Tomcat首页的Manager App链接进入Tomcat管理平台 ,结果如下:
登录对话框,需要输入用户名和密码:
由于,是首次登录Tomcat管理平台,并不知道用户名和密码
因此,单击取消按钮,此时,浏览器会跳转到另一个页面:
浏览器显示了,有关登录管理平台的提示信息
根据提示信息,要访问管理平台,需要在conf\tomcat-users.xml文件中,添加具有管理权限的账号
1.3.3 添加Tomcat管理器的管理账户
要访问Tomcat管理器,需要手动创建一个具有特权的用户。
编辑conf\tomcat-users.xml文件,并在其中定义用户来访问tomcat管理器。
<?xml version='1.0' encoding='utf-8'?><tomcat-users xmlns="http://tomcat.apache.org/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" version="1.0"> <user username="admin" password="admin" roles="manager-gui,admin-gui"/></tomcat-users>
在标签中,我们定义了一个角色为manager-gui和admin-gui的用户为"admin",密码为"admin"。
现在重新启动服务器,然后再次访问Tomcat的管理器:
1.4 Tomcat配置SSL证书
1.4.1 Tomcat生成SSL证书
使用如下命令可以生成证书:
keytool -genkey -alias tomcat -keyalg RSA -keystore D:\soft\tomcats\apache-tomcat-8.5.51\keystore\tomcat
该工具将询问一些问题以提供证书。证书将位于文件夹中,证书名称为"tomcat"。可以使用以下方法检查证书:
keytool -list -keystore D:\soft\tomcats\apache-tomcat-8.5.51\keystore\tomcat
1.4.2 使用SSL证书
编辑server.xml文件,添加一个SSL连接器:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="C:\Java\apache-tomcat-8.0.23\keystore\tomcat" keystorePass="changeit" />
重新启动Tomcat,即可完成SSL证书的添加。然后就可以在Tomcat中的HTTPS下运行Web应用程序。
1.5 一台机器上启动多个Tomcat
很多时候,我们遇到需要修改服务器配置以使其适应应用程序的情况。如果我们有多个应用程序,并且希望每个应用程序都有自己定义的配置选项,那么需要怎么配置呢?在本教程中,我将讨论为每个应用程序使用不同的Tomcat实例。
假设需要创建2个新的tomcat实例,现在开始实现目标的完整步骤。
1.5.1 安装Tomcat服务器
这是非常明显的第一步,假设tomcat安装目录为"C:tomcatServer"。
1.5.2 在不同路径下创建2个新的文件夹
这些文件夹用于存储实例特定的配置和其他数据,例如日志,临时数据。
假设新文件夹为"C:/tomcatInstanceOne"和"C:/tomcatInstanceTwo"。
1.5.3 将conf文件夹复制到上面的应用文件夹中
为了使每个实例具有不同的配置,必须执行此步骤。
1.5.4 创建实例特定的startup.bat和shutdown.bat
启动和关闭应用将需要这些文件,内容如下:
startup.bat
set CATALINA_HOME=C:\tomcatServerset CATALINA_BASE=C:\tomcatInstanceOneC:\tomcatServer\bin\startup.bat
shutdown.bat
set CATALINA_HOME=C:\tomcatServerset CATALINA_BASE=C:\tomcatInstanceOneC:\tomcatServer\bin\shutdown.bat
将两个文件都放在两个应用目录的"bin"目录中。[ 例如,创建文件夹C:/tomcatInstanceOne/bin并复制两个文件 ]。
1.5.5 创建setenv.bat环境配置
在"C\tomcatInstanceOne\bin"(以及第二个实例文件夹)目录中创建一个名为setenv.bat的文件,以设置C:\tomcatServer\bin\catalina.bat中提到的任何环境变量。在这里设置系统属性,JPDA地址等。
1.5.6 为日志等创建更多文件夹
现在是时候创建更多文件夹,这些文件夹将在您的应用程序实际运行时使用。这些文件夹类似于日志,临时文件,Web应用程序和工作文件夹。在两个实例文件夹中分别创建它们。
只需确保编辑conf\server.xml文件,以使关闭端口和HTTP连接器端口不会干扰可能正在运行的其他tomcat实例。
例如,更改tomcat将要运行/调试的端口。
第一个应用的server.xml文件:
<connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /><server port="8005" shutdown="SHUTDOWN"/><connector port="8009" protocol="AJP/1.3" redirectPort="8443" /><connector port="8100" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
第二个应用server.xml文件:
<connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /><server port="8006" shutdown="SHUTDOWN"/><connector port="8010" protocol="AJP/1.3" redirectPort="8443" /><connector port="8101" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
完成上述更改后,就能够在计算机中启动同一Tomcat服务器的多个实例。
1.6 HTTP状态码异常原因分析
HTTP状态码对应异常,可能原因分析:
-
404产生的原因为Web服务器(容器)根据请求地址找不到对应资源,以下情况都会出现404的错误提示:
- 输入的地址有误(应用名大小写不正确,名称拼写不正确);
- 在web.xml文件中< servlet>和< servlet-mapping>中的两个< servlet-name>不一致;
- 没有将工程部署到Tomcat的webapps下面;
- Web工程部署时的结构没有遵循Servlet的规范;
具体的解决办法就是根据上面提到的4种情况,逐条进行检查。
-
405这个错误的产生原因是容器找不到service方法来处理请求。以下情况容器都将视为没有找到service()方法
- service方法名写错,字母顺序不对或大小写错误;
- service方法参数类型与标准不一致,或参数顺序与标准不一致。一定是先HttpServletRequest,然后是HttpServletResponse类型;
- service方法异常、返回值类型与标准不一致,一定是抛出ServletException、IOException。返回值类型是void;
- 删掉了doPost()和doGet()里的super.service() 删掉,然后再重启Tomcat,然后就OK;
解决405错误的方法即检查service方法是否存在,签名(方法名、参数、返回值、异常类型)是否与覆盖的父类中的方法一致。
-
500的错误原因是请求的Servlet在执行service方法的过程中出现了错误,程序不能继续运行了。以下情况容器都将视为500的一种情况,而返回给客户端这个错误说明。
- Servlet没有继承HttpServlet或实现Servlet接口;
- web.xml文件中的< servlet-class>写错,与真正的包名类名不符;
- service方法中的代码运行时抛出异常;
2、JBoss
3、Nginx
开源项目
1、小程序相关
2、SpringBoot
- 分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞! (qq.com)
- 一个基于SpringBoot开源的小说和漫画在线阅读网站,简洁大方、强烈推荐 (qq.com)
- 「开源」目前见过的最好的开源OA产品 (qq.com)
- SpringBoot实现QQ邮箱注册和登录
- 短信验证码最佳实践 (qq.com)
- 10k+点赞的Spring Boot后台管理系统竟然出了详细教程!爱了! (qq.com)
- 这6个SpringBoot项目够经典!(含项目地址) (qq.com)
- 好一个 Spring Boot 开源在线考试系统!解决了我的燃眉之急 (qq.com)
- 推荐一款开源java版的视频管理系统!(附源码) (qq.com)
3、SpringMVC
4、其他
- 搞一个短信验证码登录,难吗? (qq.com)
- 排名前 16 的 Java 工具类,你都用过哪些? (qq.com)
- Java外卖点餐系统【附全部代码】 (qq.com)
- 一款PO VO DTO 转换神器,求求你别再到处找工具类了! (qq.com)
- 我手撸了一个划线翻译工具! (qq.com)
- 为什么我决定要放弃 okhttp、httpClient? (qq.com)
- Java身份证号码识别系统(开源项目) (qq.com)
- 阿里开源的10个神级项目 (qq.com)
- 推荐一波国内开源组织的宝藏项目!你有用过吗? (qq.com)
- 想知道聊天室系统是怎么做的吗? (qq.com)
5、源码分析
参考
1、Spring
1.1 概述
- 最常用的Java框架或者开源项目有哪些? (qq.com)
- Spring中经典的9种设计模式 (qq.com)
- 开玩笑吧!写了这么久的Spring Xml配置,竟然连xsd和dtd都不知道? (qq.com)
- 面试官:谈谈Java调用第三方接口示范 (qq.com)
- 4 种主流的 API 架构风格对比 (qq.com)
- Spring 那么多子项目,谁才是真正的一哥? (qq.com)
1.2 IoC
- Spring IOC 知识点总结 (qq.com)
- Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?
- 获取Spring的ApplicationContext的几种方式
- 烂了大街的 Spring 循环依赖问题,你以为自己就真会了吗 (qq.com)
1.3 AOP
- 一文搞懂spring AOP原理,各种代理各种学 (qq.com)
- 2w字搞懂Spring AOP的前世今生 (qq.com)
- 面试官:Spring AOP、AspectJ、CGLIB 都是什么鬼?它们有什么关系? (qq.com)
1.4 事务管理
1.5 常用注解
- Spring最常用的 7 大类注解,哪些你还不知道? (qq.com)
- 用户密码到底要怎么加密存储? (qq.com)
- 全网最全Spring系列面试题129道(附答案解析) (qq.com)
- Spring 自定义注解玩法大全,从入门到… (qq.com)
- Java Web 开发必须掌握的三个技术:Token、Cookie、Session (qq.com)
- Spring注解比较,@bean和@component的区别 (qq.com)
- Spring的@Transactional如何实现的(必考) (qq.com)
2、SpringMVC
2.1 概述
2.1.1 简介
2.1.2 创建SpringMVC项目
- 使用idea基于maven创建SpringMVC项目_非凡的博客
- Intellij IDEA2018基于maven创建springMVC项目以及文件配置,详细图文介绍_新鲜的桔子的博客-CSDN博客_
- xml配置文件引入properties文件_小虾的博客-CSDN博客_xml引入properties
2.2 DispatcherServlet
2.2.2 Servlet
2.2.2 DispatcherServlet
2.3 模板引擎
2.3.1 FreeMaker
- springmvc配置同时支持html和jspl两种格式的页面_苏瑞良的博客-CSDN博客
- springmvc+jsp/html 前后台交互(一):配置多视图解析器_banjing_1993的博客-CSDN博客
- springmvc同时配置html和jsp视图解析器_fourierr的博客-CSDN博客
- Freemarker使用教程——入门篇
2.4 组件整合
2.4.1 MySQL
- 基于 Spring + Spring MVC + Mybatis 的分布式敏捷开发系统架构 (qq.com)
- Spring+Spring MVC+MySQL的简单开发_saiofo的博客-CSDN博客
- SpringMVC+Spring+Mybatis实现支付宝支付功能(附完整代码) (qq.com)
- SringMVC从入门到源码,这一篇就够 (qq.com)
2.4.2 Redis
- Spring和Redis整合详解_feiyangtianyao的专栏-CSDN博客
- Caused by: java.lang.NoClassDefFoundError: redis/clients/util/Pool_datouniao1的博客-CSDN博客
- Redis配置错误(已解决) Could not resolve placeholder ‘redis.maxIdle’ in string value “${redis.maxIdle}”_夏以苍凉-CSDN博客
2.4.3 MQ
2.4.4 Log
2.4.5 单元测试
2.4.6 Dubbo
2.4.7 HBase
3、SpringBoot
- 基于SpringBoot的MySQL实现读写分离 (qq.com)
- 开放API接口签名验证,让你的接口从此不再裸奔 (qq.com)
- 3W 字的 Spring Boot 超详细总结 (qq.com)
- 超详细的Spring Boot全面总结3W字(建议收藏) (qq.com)
- 看看人家那后端API接口写得,那叫一个巴适~,再看看我的,像坨屎! (qq.com)
- 如何设计一个牛逼的API接口 (qq.com)
- SpringBoot 居然有 44 种应用启动器 (qq.com)
- Spring Boot全面总结(超详细,建议收藏) (qq.com)
- 你能说出Spring Boot项目启动的几种方式吗? (qq.com)
- 什么是计时攻击?Spring Boot 中该如何防御? (qq.com)
- spring-boot项目优雅的http客户端工具,太香了! (qq.com)
- Spring Boot 注解总结,你必须掌握的45个注解! (qq.com)
- Spring Boot 定时任务,怎么实现任务动态增删启停? (qq.com)
- Spring Boot + Redis 实现各种操作,写得太好了吧! (qq.com)
- 面试官:说说你对 RESTful 的理解? (qq.com)
- SpringBoot四大核心组件,你了解多少 (qq.com)
- SpringBoot 集成 Shiro 极简教程(实战版) (qq.com)
- SpringBoot 操作 Redis的各种实现(以及Jedis、Redisson、Lettuce的区别比较) (qq.com)
- Spring Boot 学习笔记,这个太全了! (qq.com)
- SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) (qq.com)
- Spring Boot + GraphQL 才是 API 的未来? (qq.com)
- 用 SpringBoot+Redis 解决海量重复提交问题 (qq.com)
- 蚂蚁金服开源增强版 Spring Boot 研发框架 (qq.com)
- 如何保护 SpringBoot 配置文件中的敏感信息 ? (qq.com)
- SpringBoot实现Excel导入导出,好用到爆,POI可以扔掉了! (qq.com)
- Spring Boot 进行优雅的字段校验,写得太好了! (qq.com)
- Spring Boot 面试杀手锏:自动配置原 (qq.com)
- Spring Boot关于日期时间格式化处理方式总结 (qq.com)
- Spring Boot巧用 @Async 提升API接口并发能力!NB! (qq.com)
- 12306抢票:极限并发带来的思考 (qq.com)
- SpringBoot+Kafka+ELK 完成海量日志收集(超详细)! (qq.com)
- SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) (qq.com)
- 4 种主流的 API 架构风格对比 (qq.com)
- 如何保护 SpringBoot 配置文件中的敏感信息 (qq.com)
- SpringBoot 优雅的参数效验! (qq.com)
- Spring Boot 获取 Bean 的 3 种方式!还有谁不会?? (qq.com)
- mybatis的4种分页方式
- 解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)问题
- 几种常见mybatis分页实现
- Mybatis的插件 PageHelper 分页查询使用方法
4、SpringCloud
- 微服务架构最强讲解,那叫一个通俗易懂! (qq.com)
- 秒杀系统架构分析与实战(1.4W字画图详解) (qq.com)
- 3W字带你玩转SpringCloud! (qq.com)
- 成为架构师,必须掌握 10 种常见的架构模式! (qq.com)
- 面试官:面对千万级、亿级流量怎么处理? (qq.com)
- 从零搭建 Spring Cloud 服务(超级详细) (qq.com)
- 吐血总结了分布式各种问题 (qq.com)
- 大话集群和负载均衡 (qq.com)
- 高并发系统三大利器之缓存 (qq.com)
- 万字好文,电商秒杀系统架构分析与实战 (qq.com)
- 搞懂异地多活,看这篇就够了 (qq.com)
- 一网打进,高并发中的 限流、熔断、降级、预热、背压 (qq.com)
- 服务端防止订单重复支付,需要注意啥? (qq.com)
- 聊聊前后端分离的接口规范 (qq.com)
- 微服务是什么? (qq.com)
- 为什么大公司一定要使用微服务? (qq.com)
- 分布式定时任务框架选型,写得太好了! (qq.com)
- 10张图带你彻底搞懂限流、熔断、服务降级 (qq.com)
- 一文讲完 Spring Cloud,2W 字超详细总结 (qq.com)
- 支付宝架构真牛逼 (qq.com)
- 两万字深度介绍分布式系统原理 (qq.com)
- 如何避免重复提交?分布式服务的幂等性设计! (qq.com)
- 啥?分布式啥?啥事务? (qq.com)
- 分布式事务最经典的七种解决方案 (qq.com)
- 面试官:SOA 和微服务的区别?这回终于搞清楚了! (qq.com)
5、RPC框架
- 女朋友问我:Dubbo的服务引用过程 (qq.com)
- RPC三连问:什么是RPC框架?实现原理呢?与SOA、REST有啥区别? (qq.com)
- RPC 和 HTTP 的区别?直观讲解! (qq.com)
- 面试:请说一下rpc和http的区别? (qq.com)
- RPC框架(一)RPC简介_keep_trying的专栏-CSDN博客_rpc框架
- JAVA中几种常用的RPC框架介绍_Vince的修炼之路-CSDN博客_java rpc框架
- 什么是 RPC 框架_乐乐的博客-CSDN博客_rpc框架
- RPC框架设计和调用详解_周江霄 -CSDN博客
- 服务之间的调用为啥不直接用HTTP而用RPC
- 有了HTTP,为什么还要RPC? (qq.com)
- [花了一个星期,我终于把RPC框架整明白了! - 51CTO.COM](https://developer.51cto.com/art/201906/597963.htm#:~:text=RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty,框架的支持。 Thrift:是 Facebook 的开源 RPC 框架,主要是一个跨语言的服务开发框架。)
- JAVA中几种常用的RPC框架介绍_Vince的修炼之路-CSDN博客_java rpc框架
- RPC三连问:什么是RPC框架?实现原理呢?与SOA、REST有啥区别? (qq.com)