程序员学习一门新的技术,永远绕不开一个永恒的话题——
Hello World!
,在我们学习servlet
的时候也首先来实现一个Hello World!
入门程序!
Hello World!
入门程序
首先编写一个Servlet
类,在其中返回hello world!
。
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
out.write("hello world!");
out.flush();
out.close();
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
然后需要配置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_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<!-- servlet名字 -->
<servlet-name>helloworldServlet</servlet-name>
<!-- servlet的类路径,会将这个类命名为上面配置的名字 -->
<servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloworldServlet</servlet-name>
<!-- 拦截路径,当访问hello的时候就会交给这个servlet处理 -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
这样配置了之后一个简单的hello world
入门程序就完成了。
在servlet3.0
版本中,将xml配置进一步简化了,可以使用注解来完成。上面的servlet
代码就可以修改为:
// 标注这是一个servlet,名字叫helloworldServlet,拦截的路径是/hello,这个注解的效果和之前的xml配置一样。
@WebServlet(name = "helloworldServlet", urlPatterns = "/hello")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
out.write("hello world!");
out.flush();
out.close();
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
servlet
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。 ——菜鸟教程
http
请求通过servlet
来和程序进行交互。
servlet
生命周期
servlet
生命周期指的是servlet
从创建到销毁的整个过程。它遵循以下的几个过程:
- 初始化后会调用
init()
方法 - 调用
service()
方法来处理客户端请求 - 销毁前调用
destroy()
方法 - 最后由垃圾回收器回收
创建servlet
的方式
创建servlet
有三种方式,这里介绍其中两种比较常用的方式。
第一种:实现Servlet
接口
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("=== 初始化执行 ===");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("=== 处理业务逻辑 ===");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("=== 销毁执行 ===");
}
}
*第二种:继承HttpServlet
类
其实通过查看源代码可以发现,HttpServlet
类最终也是实现了Servlet
接口,只是对Servlet
中一些方法做了实现。
public abstract class HttpServlet extends GenericServlet {}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}
使用HttpServlet
类创建
@WebServlet(name = "httpServletDemo", urlPatterns = "/hello")
public class ServletHttpDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello world");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
HttpServlet
对Servlet
接口做了实现,同时提供了一些用于处理http
请求的方法,在继承了HttpServlet
类之后就可以非常方便的处理用户请求,而不用在去关心其他。
手写一个HttpServlet
根据上面的发现,我们自己也可以来对Servlet
接口进行一个实现,来达到和HttpServlet
类相似的功能。说干就干。
由于Servlet
中实际会调用Service
方法进行业务处理,那就需要将该方法进行重写,让它支持http
请求。
/**
* @DESCRIPTION: 继承了servlet接口,实现类似于HttpServlet相似的功能
* @AUTHOR: AnotherOne
* @DATE: 2021/7/14 16:23
*/
public class BaseServlet implements Servlet {
public BaseServlet() {
// do nothing
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("=== BaseServlet ===");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
// 重写该方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) servletRequest;
response = (HttpServletResponse) servletResponse;
this.service(request, response);
}catch (Exception e){
System.out.println("无效的请求");
}
}
// 实现不同请求方式的判断与业务的转发
protected void service(HttpServletRequest res, HttpServletResponse rep) throws ServletException, IOException {
String method = res.getMethod();
if (HttpType.GET.name().equalsIgnoreCase(method)){
this.doGet(res, rep);
} else if (HttpType.POST.name().equalsIgnoreCase(method)){
this.doPost(res, rep);
} else {
System.out.println("暂不支持的请求类型");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("=== BaseServlet ===");
}
protected void doGet(ServletRequest req, ServletResponse res){
System.out.println("BaseServlet get 默认实现");
}
protected void doPost(ServletRequest req, ServletResponse res){
System.out.println("BaseServlet post 默认实现");
}
/**
* 定义http请求枚举类
*/
enum HttpType {
GET,
POST,
PUT,
DELETE
}
}
接下来来写个业务类验证一下。
@WebServlet(name = "servletDemo2", urlPatterns = "/demo2")
public class ServletHttpDemo2 extends BaseServlet {
@Override
protected void doGet(ServletRequest req, ServletResponse res) {
// super.doGet(req, res);
System.out.println("子类业务处理");
}
}
当访问/demo2
时控制台可以打印出子类业务处理
,说明效果不错。
ServletConfig
对象
在容器创建Servlet
实例对象的时候,会将一些初始化参数封装到ServletConfig
对象中,并且在调用对象的init(ServletConfig config)
方法时传递给该实例。所以可以通过该对象得到一些初始化参数信息。
@WebServlet(name = "httpServletDemo", urlPatterns = "/demo", initParams = {
@WebInitParam(name = "name", value = "fzkj"),
@WebInitParam(name = "age", value = "23")
})
public class ServletHttpDemo extends HttpServlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取配置的name和age属性值
System.out.println(config.getInitParameter("name"));
System.out.println(config.getInitParameter("age"));
// 获取当前servlet名称
System.out.println(config.getServletName());
// 获取当前参数属性名称的枚举
Enumeration<String> names = config.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
}
// 获取servletcontext对象
ServletContext servletContext = config.getServletContext();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
上面代码是使用注解的方式定义初始化参数。也可以使用xml配置的方式进行。
<servlet>
<servlet-name>indeServlet</servlet-name>
<servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
<init-param>
<param-name>namespace</param-name>
<param-value>fzkj</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>23</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>indeServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
这两者的效果并没有什么不同。
上述代码运行结果:
fzkj
23
httpServletDemo
name
age
ServletContext
对象
web容器在启动的时候,会为每个web应用程序创建一个ServletContext
对象,它代表的就是当前web应用。
所以在同一个web应用中,所有的servlet
共享同一个ServletContext
对象,因此可以通过这个对象来实现不同Servlet
之间的通信。
servletContext
实现多个servlet
之间数据共享
/**
* @DESCRIPTION: 多个servlet数据共享 和 demo2
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:15
*/
@WebServlet(name = "demo1", urlPatterns = "/demo1")
public class Demo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext context = this.getServletContext();
context.setAttribute("name", "fzkj");
PrintWriter out = resp.getWriter();
out.write("数据共享成功");
out.flush();
out.close();
}
}
/**
* @DESCRIPTION: TODO 多个servlet数据共享 和 demo1
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:15
*/
@WebServlet(name = "demo2", urlPatterns = "/demo2")
public class Demo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext context = this.getServletContext();
String name = (String)context.getAttribute("name");
PrintWriter out = resp.getWriter();
out.write("数据读取成功" + name);
out.flush();
out.close();
}
}
读取web应用初始化参数
/**
* @DESCRIPTION: TODO 读取web应用初始化参数 -> 在xml中配置<context-param></context-param>
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:28
*/
@WebServlet(name = "demo3", urlPatterns = "/demo3")
public class Demo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println(context.getInitParameter("names"));
}
}
<context-param>
<param-name>names</param-name>
<param-value>fzkj123</param-value>
</context-param>
==是不是有一种可以通过注解的方式创建web初始化参数?后面再来研究==
ServletContext对象实现请求转发
/**
* @DESCRIPTION: TODO 实现请求转发,转发给Demo5
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:39
*/
@WebServlet(name = "dmeo4", urlPatterns = "/demo4")
public class Demo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
resp.setCharacterEncoding("utf-8");
resp.getOutputStream().write("这是再demo4中".getBytes());
// 转发给demo5
RequestDispatcher rd = context.getRequestDispatcher("/demo5");
rd.forward(req, resp);
}
}
/**
* @DESCRIPTION: TODO 接收demo4的转发请求并处理
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 11:43
*/
@WebServlet(name = "dmeo5", urlPatterns = "/demo5")
public class Demo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.getOutputStream().write("这是再demo5中".getBytes());
}
}
由于再demo4的处理逻辑中将请求转发给了demo5,所以实际上页面会打印这是再demo5中
。
Filter
Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。
可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet。Servlet 过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。调用 Servlet 前调用所有附加的 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 部署描述符(web.xml)中的 XML 标签来声明,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。
当 Web 容器启动 Web 应用程序时,它会为您在部署描述符中声明的每一个过滤器创建一个实例。
Filter的执行顺序与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。
——菜鸟教程
Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
FilterChain
:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
创建一个简单的过滤器
/**
* @DESCRIPTION: TODO
* @AUTHOR: AnotherOne
* @DATE: 2021/7/15 12:47
*/
// 标注这是一个过滤器,urlPatterns用于配置拦截的url规则,符合规则的url才会被拦截
@WebFilter(filterName = "fzkj_filter_demo1", urlPatterns = "/*", initParams = {
@WebInitParam(name = "name", value = "fzkj_filter_demo1"),
@WebInitParam(name = "age", value = "23")
})
public class Demo1 implements Filter {
private FilterConfig config;
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
System.out.println("=== demo1 过滤器初始化 ===");
// 获取当前过滤器名称
System.out.println(config.getFilterName());
System.out.println("=========================");
// 获取初始化参数‘name’的值
System.out.println(config.getInitParameter("name"));
System.out.println("=========================");
// 获取所有初始化参数的名称
Enumeration<String> names = config.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println(names.nextElement());
}
System.out.println("=========================");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
System.out.println("经过 " + this.config.getFilterName() + " filter");
// 交给下一过滤器
chain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("=== demo1 过滤器销毁 ===");
}
}
Filter
的中的方法与用法和Servlet
非常相似,不在过多赘述
持续更新~~