Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目

每博一文案

有句谚语说:“一怒之下踢石头,只有痛着脚趾头。”
比一件糟糕的事情更可拍的,是你用糟糕的态度去面对它。看过一个很有意思的故事:
有个男人清早洗漱的时候,把自己的手表放在了桌子上。他的儿子不小心把手表碰倒地上摔坏了,男人
气得儿子揍了一顿,还埋怨妻子没看好儿子,两个人吵了起来。
男人气急败坏地摔门出去,路上想起有一份重要文件忘记带了,他匆忙回家取。可没有人在家,
他只得打电话让妻子回来送钥匙。妻子赶回家时,不小心撞翻了路边的一个小吃摊,赔了一笔钱。
男人没等到妻子回家,因为迟到也遭受了罚款。
费斯汀格法则认为:“10%的生活,由发生在你身上的事情组成,而另外的90%,则取决于你做出的反应。”
这个故事中。摔坏手表就是其中10%,而后面的一系列的事情则是另外的90%,是由埋怨引起的。
面对人生得失时,一颗平常心,比一百种智慧更有力量。
人这一生,得与失,都是常态。不是此处得,彼处失,就是彼处得,此处失,得得失失,失失得得,才构成了“人间事”。
正如席慕容的那首诗《写给幸福》:“挫折会来,也会过去。热泪会流下来,也会收起。没有什么
可以让我气馁的,因为我有着长长的一生。”
别为昨日忧愁,别为琐事烦忧,因为真正的人间清醒,是努力活着。
余生,愿你所有快乐,无需假装;愿你此生尽兴,赤诚善良。
—————— 《一禅心灵庙语》

@

1. web.xml 的缺点分析

分析 oa项目中的 web.xml文件 具体的可以移步至: 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客

  • 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话 web.xml文件会非常庞大,有可能最终会达到几十兆。
  • web.xml文件中进行 servlet信息的配置,显然开发效率比较低,每一个都需要配置一下。
  • 而且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?可以的。

Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?

  • 开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。

  • web.xml文件体积变小了。

  • 并不是说注解有了之后,web.xml文件就不需要了:

    • 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
    • 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。

2. @WebServlet 注解

注解对象的使用格式:

@注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)
// 如果注解当中的属性赋值的类型是数组,格式如下
@注解名称(属性名={属性值1,属性值2,属性值3},属性名=属性值)
// 如果注解当中还有注解的赋值如下:
@注解名称(属性名=属性值,注解名称(属性名=属性值,属性名=属性值),属性名=属性值)

想要了解更多的注解信息的内容,大家可以移步至: Java “框架 = 注解 + 反射 + 设计模式” 之 注解详解_ChinaRainbowSea的博客-CSDN博客

如下是 @WebServlet 注解基于 Tomcat 10 的源码:


package jakarta.servlet.annotation; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet { /**
* @return name of the Servlet
*/
String name() default ""; /**
* A convenience method, to allow extremely simple annotation of a class.
*
* @return array of URL patterns
* @see #urlPatterns()
*/
String[] value() default {}; /**
* @return array of URL patterns to which this Filter applies
*/
String[] urlPatterns() default {}; /**
* @return load on startup ordering hint
*/
int loadOnStartup() default -1; /**
* @return array of initialization params for this Servlet
*/
WebInitParam[] initParams() default {}; /**
* @return asynchronous operation supported by this Servlet
*/
boolean asyncSupported() default false; /**
* @return small icon for this Servlet, if present
*/
String smallIcon() default ""; /**
* @return large icon for this Servlet, if present
*/
String largeIcon() default ""; /**
* @return description of this Servlet, if present
*/
String description() default ""; /**
* @return display name of this Servlet, if present
*/
String displayName() default "";
}

2.1 @WebServlet注解当中常用的一些属性的说明

WebServlet 当中存在着不少的属性,这里我们只介绍一些常用的属性。其他的大家感兴趣的可以去学习。

  • name 属性的作用:用来指定 Servle t的名字。等同于web.xml 当中的 : 。如下图所示的

/**
* @return name of the Servlet
*/
String name() default ""; // default 表示该属性的默认值为 "" 空字符串
  • urlPatterns 属性的作用:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :。如下图所示的.

注意: 需要注意的是:urlPatterns 所赋值的字符串映射的url 路径要带 / 开始的

/**
* @return array of URL patterns to which this Filter applies
*/
String[] urlPatterns() default {}; // 是一个字符串数组的类型,因为一个Servlet 可以有多个映射路径 url
// 注意 urlPatterns 属性值和 web.xml 当中的 <url-pattern> 带 "/" 开始

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter; // 注意: urlPatterns 属性值以 "/" 开始
@WebServlet(name="Test",urlPatterns = {"/test","/test2","/test3"})
public class TestWebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 设置在浏览器端显示的格式类型,以及字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter(); // 获取到本Servlet Name
String name = getServletName();
writer.println("注解当中的name 值也就是web.xml中的<servlet-name>的值: " + name + "<br>"); // 获取到该类当中 web.xml 中的 url-pattern 的值
// 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
String servletPath = request.getServletPath();
writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>"); }
}

  • value 属性的作用: 和 urlPatterns 属性的作用是一样的:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :。
  • 可以使用模糊查询
@WebServlet("/dept/*")  // 可以使用模糊查询,* 任意字符串 ;表示只要是 /dept/xxx的任意都可以访问该Servlet

这里为什么要设置两个作用一样的属性值呢?

因为 是一个 Servlet 当中最常用,而且是必须要有的。如果注解当中只对一个的属性赋值,并且该属性名名为 value的话。那么这个 value 的属性名就可以省略不写。 这里再说一点就是如果一个注解当中属性类型为数组,但是该数组只赋一个值的话,可以省略数组的 {} 花括号。

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter; // 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = {"/test"})
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略{}
@WebServlet("/test") // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 设置在浏览器端显示的格式类型,以及字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter(); // 获取到该类当中 web.xml 中的 url-pattern 的值
// 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
String servletPath = request.getServletPath();
writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>"); }
}

  • initParams 属性作用:表示的是 Servlet对象的配置信息对象的信息,一个Servlet 就有一个 Servlet 配置对象的信息,封装在了 标签当中的 标签当中设置。如下图所示

注意: initPatams属性的类型是 WebInitParam这个注解 数组。

/**
* @return array of initialization params for this Servlet
*/
WebInitParam[] initParams() default {};

如下是 @WebInitParam的源码:

该@WebInitParam 注解当中的 name 表示:XXX的值,而 value 表示 : XXX/param-value> 的值。

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jakarta.servlet.annotation; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam { /**
* @return name of the initialization parameter
*/
String name(); /**
* @return value of the initialization parameter
*/
String value(); /**
* @return description of the initialization parameter
*/
String description() default "";
}

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter; // 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = {"/test"})
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略{}
@WebServlet(value = "/test", initParams = {@WebInitParam(name = "user", value = "root"),
@WebInitParam(name = "password", value = "11235813")}) // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 设置在浏览器端显示的格式类型,以及字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter(); // 获取到该类当中 web.xml 中的 url-pattern 的值
// 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
String servletPath = request.getServletPath();
writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>"); // 获取到初始化参数,对应的一个Servlet 标签当中的 <init-param> 标签当中设置
String username = getInitParameter("user"); // 根据对应的配置的 name 获取到对应的 value 值
writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 user的值: " + username + "<br>"); String password = getInitParameter("password");
writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 password的值: " + password + "<br>"); }
}

  • loadOnStartUp属性的作用:用来指定在服务器启动阶段是否加载该Servlet的构造器(默认是加载Servlet 是不会立即就调用其中的构造器的,而是访问的时候才会调用该Servlet的构造器)。等同于:整数值 其中数值越小,优先被调用执行。
/**
* @return load on startup ordering hint
*/
int loadOnStartup() default -1;

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter; @WebServlet(value = "/test",loadOnStartup = 1)
public class TestWebServlet extends HttpServlet { public TestWebServlet() n{
System.out.println("TestWebServlet 的构造器执行");
}
}

3. 使用模板方法设计模式优化oa项目

注意:建议: 如果你阅读到这里时,非常感谢您的大力支持,如果还要继续阅读的话,建议先移步至: 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客 博客,方便后续的内容上的理解阅读。

上面的@WebServlet 注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。

  • 一个单标的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大。)

  • 怎么解决这个类爆炸问题?可以使用模板方法设计模式(定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。"父类定义骨架,子类实现某些细节。")。具体的模板方法设计模式,大家可以移步至: 23种设计模式之 : 模板方法设计模式_ChinaRainbowSea的博客-CSDN博客

怎么解决类爆炸问题?

  • 以前的设计是一个请求一个 Servlet类。1000个请求对应1000个Servlet类。导致类爆炸。
  • 可以这样做:一个请求对应一个方法。一个业务对应一个Servlet类。比如:处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlet。处理银行卡卡片业务对应一个CardServlet

思想:

模板方法:父类定义骨架,子类实现某些细节。而这里我们将骨架定义为一个核心的方法也就是这里重写的父类中的 protected void service(HttpServletRequest request, HttpServletResponse response)的方法,需要注意的是:重写的 service 就没有 405 错误的提示了。

这里我们使用@WebServlet 注解的方式,进行一个 url 映射路径的配置。

该核心方法思路是: 通过浏览器地址栏上访问的不同的 url ,对应不同的功能访问。我们就可以使用request.getServletPath()获取到浏览器地址栏上的 url 的字符串,再根据获取到的不同的 url 字符串进行一个功能上的匹配equals 对应不同的功能,我们使用方法将该功能实现。

可以为 value 属性值设置为 模糊查询

@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet

具体代码如下:

package com.RainbowSea.oa;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; /**
* 采用模板方法设计模式,重新设计一个 OA 系统
*/ //@WebServlet(value = {"/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"} //@WebServlet({"/dept/list", "/dept/save", "/dept/edit", "/dept/detail", "/dept/delete", "/dept/modify"})
@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet
public class DeptServlet extends HttpServlet { // 模板方法
// 重写其中的 servlet 方法(并没有重写其中的doGet()方法,405错误没有了) @Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException { // 获取到该对应请求地址栏上的 url ,也就是一个servet对应当中的 url 映射路径
// 这里是对应浏览器地址栏上的 url
String servletPath = request.getServletPath(); // 返回的 url 的开头是带了 "/" 的 if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/save".equals(servletPath)) {
doSave(request, response);
} else if ("/dept/edit".equals(servletPath)) {
doEdit(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doDel(request, response);
} else if ("/dept/modify".equals(servletPath)) {
doModify(request, response);
} } private void doList(HttpServletRequest request, HttpServletResponse response) throws IOException { // 设置前端浏览器显示的格式类型,以及编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null; // 获取到该webapp的项目根路径:也就是在Tomcat 当中设置的访问的项目路径
String contextPath = request.getContextPath(); int i = 0; writer.println(" <!DOCTYPE html>");
writer.println("<html lang='en'>"); writer.println("<head>");
writer.println(" <meta charset='UTF-8'>");
writer.println(" <title>部门列表页面</title>");
writer.println("</head>"); writer.println(" <script type = 'text/javascript' >");
writer.println(" function del(dno) {");
// 弹出确认框,用户点击确定,返回true,点击取消返回false
writer.println(" var ok = window.confirm('亲,删了不可恢复哦!');");
writer.println(" if (ok) {");
// 发送请求进行删除数据的操作
// 在js代码当中如何发送请求给服务
// document.location.href='请求路径
// document.location = '请求路径'")
// window.location.href = '请求路径
// window.location = '请求路径'
// 注意是根据所传的部门编号删除数据的
writer.println(" document.location.href = '" + contextPath + "/dept/delete?deptno=' + dno");
writer.println(" }");
writer.println(" }");
writer.println("</script >"); writer.println("<body>");
writer.println(" <h1 align='center'>部门列表</h1>");
writer.println(" <table border='1px' align='center' width='50%'>");
writer.println(" <tr>");
writer.println(" <th>序号</th>");
writer.println(" <th>部门编号</th>");
writer.println(" <th>部门名称</th>");
writer.println(" </tr>"); try {
// 连接数据库,查询所有部门:
// 1. 注册驱动,获取连接
connection = DBUtil.getConnection();
// 2. 获取操作数据库对象,预编译sql语句
String sql = "select depton as det,dname,loc from dept"; // 在mysql中测试一下是否正确
preparedStatement = connection.prepareStatement(sql); // 3. 执行sql语句
resultSet = preparedStatement.executeQuery(); // 4. 处理查询结果集
while (resultSet.next()) {
String det = resultSet.getString("det"); // 有别名要使用别名
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc"); writer.print(" <tr>");
writer.print(" <td>" + (++i) + "</td>");
writer.print(" <td>" + det + "</td>");
writer.print(" <td>" + dname + "</td>");
writer.print(" <td>");
writer.print(" <a href='javascript:void(0)' onclick= 'del(" + det + ")'>删除</a>");
// 将部门编号传过去,用户数据库查询修改
writer.print(" <a href='" + contextPath + "/dept/edit?deptno=" + det + "'>修改</a>");
//注意这里的是前端的资源,需要加项目名,但是这里的项目名我们通过 getContestPath()方法动态获取
// 并且将部门名传过去,再从数据库当中查找出来对应的部门的详细信息:注意: ?(间隔) Http传输协议
writer.print(" <a href='" + contextPath + "/dept/detail?deptno=" + det + "'>详情</a>");
writer.print(" </td>");
writer.print(" </tr>");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally { // 5. 关闭资源
DBUtil.close(connection, preparedStatement, resultSet);
} writer.println("</table>");
writer.println("<hr>");
// 前端的资源路径访问需要加项目名
writer.println("<a href='" + contextPath + "/add.html'>新增部门</a>");
writer.println("</body>");
writer.println("</html>"); } private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException {
/*
思路:
获取到前端的提交的数据,注意 编码设置post 请求
连接数据库: 进行添加数据
添加成功: 返回部门列表页面
添加失败: 返回失败的页面
*/ request.setCharacterEncoding("UTF-8"); // 获取到前端的数据,建议 name 使用复制
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc"); // 连接数据库,添加数据
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影响数据库的行数
int count = 0; try {
// 1. 注册驱动,连接数据库
connection = DBUtil.getConnection(); // 2. 获取操作数据库对象,预编译sql语句,Sql测试
String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符, 真正执行sql语句,
// 注意: 占位符的填充是从 1 开始的,基本上数据库相关的起始下标索引都是从 1下标开始的
preparedStatement.setString(1, deptno);
preparedStatement.setString(2, dname);
preparedStatement.setString(3, loc); // 返回影响数据库的行数
count = preparedStatement.executeUpdate(); // 5.释放资源
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection, preparedStatement, null);
} // 保存成功,返回部门列表页面
if (count == 1) {
// 这里应该使用,重定向
// 这里用的转发,是服务器内部的,不要加项目名
//request.getRequestDispatcher("/dept/list/").forward(request, response); // 重定向
response.sendRedirect(request.getContextPath() + "/dept/list/");
} else {
// 保存失败
// web当中的 html资源,这里的 "/" 表示 web 目录
//request.getRequestDispatcher("/error.html").forward(request, response); response.sendRedirect(request.getContextPath() + "/error.html");
}
} private void doEdit(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter(); writer.println(" <!DOCTYPE html>");
writer.println("<html lang='en'>"); writer.println("<head>");
writer.println(" <meta charset='UTF-8'>");
writer.println(" <title>部门列表页面</title>");
writer.println("</head>");
writer.println("<body>");
writer.println(" <h1>修改部门</h1>"); writer.println(" <form action='" + request.getContextPath() + "/dept/modify' method='post'>"); /*
思路:
获取到提交的过来的 部门编号
根据部门编号修改信息,注意:部门编号是唯一的不要被修改了
连接数据库,查询到相关信息显示到浏览器页面当中,方便用户修改
*/ String deptno = request.getParameter("deptno"); // 连接数据库
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null; try {
// 1. 注册驱动,连接数据库
connection = DBUtil.getConnection(); // 2. 获取到操作数据库对象,预编译SQL语句,sql测试
String sql = "select dname,loc from dept where depton = ?"; preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符,真正执行sql语句
preparedStatement.setString(1, deptno);
resultSet = preparedStatement.executeQuery(); // 4. 处理查询结果集
while (resultSet.next()) {
String dname = resultSet.getString("dname"); // 查询使用的别名,要用别名
String loc = resultSet.getString("loc"); // <!-- readonly 表示只读,不可修改的作用
writer.println(" 部门编号: <input type='text' name='deptno' value='" + deptno + "' readonly /><br>");
writer.println(" 部门名称: <input type='text' name='dname' value=" + dname + " /><br>");
writer.println(" 部门位置: <input type='text' name='loc' value=" + loc + " /><br>"); }
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5.释放资源,最后使用的优先关闭(因为如果是关闭优先使用的话,再最后面使用的可能需要前面的资源,才能执行)
DBUtil.close(connection, preparedStatement, resultSet);
} writer.println(" <input type='submit' value='修改' />");
writer.println(" </form>");
writer.println("</body>");
writer.println("</html>");
} private void doDetail(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设置前端浏览器格式类型和字符集编码
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
//中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
// 第一步:获取部门编号
// 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
// 第三步:将部门信息响应到浏览器上。(显示一个详情。) // 1.
// http://127.0.0.1:8080/servlet09/dept/detail?deptno=40
String deptno = request.getParameter("deptno"); // 注意是我们前端提交的数据,建议复制name try {
// 2. 连接数据库,根据部门编号查询数据库
// 1.注册驱动,连接数据库
connection = DBUtil.getConnection(); // 2. 预编译SQL语句,sql要测试
String sql = "select dname,loc from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符,真正执行sql语句
preparedStatement.setString(1, deptno);
resultSet = preparedStatement.executeQuery(); // 4. 处理查询结果集
while (resultSet.next()) {
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc"); // 注意将 “双引号转换为单引号,因为在Java当中不可以嵌套多个双引号,除非是字符串的拼接
// 所以使用 '单引号
writer.println(" <body>");
writer.println(" <h1>部门详情</h1>");
writer.println(" 部门编号: " + deptno + " <br>");
writer.println(" 部门名称: " + dname + "<br>");
writer.println(" 部门位置: " + loc + "<br>");
writer.println(" <input type='button' value='后退' onclick='window.history.back()' />"); }
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 释放资源
DBUtil.close(connection, preparedStatement, resultSet);
} writer.println("</body>");
writer.println("</html>");
} private void doDel(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charSet=UTF-8");
PrintWriter writer = response.getWriter();
request.setCharacterEncoding("UTF-8"); // 思路:
/*
根据部门编号删除信息,
删除成功,跳转回原来的部门列表页面
删除失败,跳转删除失败的页面
*/ Connection connection = null;
PreparedStatement preparedStatement = null; // 记录删除数据库的行数
int count = 0; // 获取到前端提交的数据
String deptno = request.getParameter("deptno"); // 连接数据库进行删除操作 try {
// 1.注册驱动,连接数据库
connection = DBUtil.getConnection(); // 开启事务(取消自动提交机制),实现可回滚
connection.setAutoCommit(false); // 2. 预编译sql语句,sql测试
String sql = "delete from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符,真正的执行sql语句
preparedStatement.setString(1, deptno);
// 返回影响数据库的行数
count = preparedStatement.executeUpdate();
connection.commit(); // 手动提交数据
} catch (SQLException e) {
// 遇到异常回滚
if (connection != null) {
try {
// 事务的回滚
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
} finally {
// 4. 释放资源
// 因为这里是删除数据,没有查询操作,所以 没有 ResultSet 可以传null
DBUtil.close(connection, preparedStatement, null);
} if (count == 1) {
// 删除成功
// 仍然跳转到部门列表页面
// 部门列表页面的显示需要执行另外一个Servlet,怎么办,可以使用跳转,不过这里最后是使用重定向
// 注意:转发是在服务器间的,所以不要加“项目名” 而是 / + web.xml 映射的路径即可
//request.getRequestDispatcher("/dept/list/").forward(request,response); // 优化:使用重定向机制 注意: 重定向是自发到前端的地址栏上的,前端所以需要指明项目名
// 注意: request.getContextPath() 返回的根路径是,包含了 "/" 的
response.sendRedirect(request.getContextPath() + "/dept/list/");
} else {
// 删除失败
// web当中的 html资源,这里的 "/" 表示 web 目录
//request.getRequestDispatcher("/error.html/").forward(request, response); // 优化,使用重定向
response.sendRedirect(request.getContextPath() + "/error.html/");
}
} private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException {
/*
思路:
获取到提交的数据信息,
根据提交的信息,连接数据库修改
修改成功,跳转到部门列表页面,
修改失败:跳转到失败的页面
*/ request.setCharacterEncoding("UTF-8"); // 设置获取的的信息的编码集
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影响数据库的行数
int count = 0; String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc"); try {
// 1. 注册驱动,连接数据库
connection = DBUtil.getConnection(); // 2. 获取到操作数据库的对象,预编译sql语句,sql测试
String sql = "update dept set dname = ?,loc = ? where depton = ?";
preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符,真正执行sql语句
// 从下标 1开始
preparedStatement.setString(1, dname);
preparedStatement.setString(2, loc);
preparedStatement.setString(3, deptno); count = preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 4. 释放资源,最后使用的优先被释放
DBUtil.close(connection, preparedStatement, null);
} if (count == 1) {
// 更新成功
// 跳转到部门列表页面(部门列表表面是通过java程序动态生成的,所以还需要再次执行另一个Servlet)
// 转发是服务器内部的操作,“/” 不要加项目名
// request.getRequestDispatcher("/dept/list/").forward(request,response); // 优化使用重定向,自发前端(需要指明项目名)
response.sendRedirect(request.getContextPath() + "/dept/list/"); } else {
// 更新失败
// request.getRequestDispatcher("/error.html").forward(request,response); // 优化重定向
// request.getContextPath() 获取的根路径是,带了 "/" 的
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}

4. 总结:

  1. @WebServlet 注解,提高开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。web.xml文件体积变小了。
  2. @WebServlet 注解当中的valueurlPatterns 的作用都是一样的,表示 映射的url路径,注意 : 设置赋值的 url 要以 / 开始。不要忘记了,不要漏掉了。
  3. @WebServlet 常用的属性的作用和使用。
  4. 模板方法的核心思想:父类定义骨架,子类实现某些细节。也可以一个方法为骨架,多个方法实现某些细节。
  5. 注解可以使用模糊查询
@WebServlet("/dept/*")  // 可以使用模糊查询,* 任意字符串

5. 最后:

限于自身水平,其中存在的错误,希望大家,给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目的更多相关文章

  1. JSP(2)—绝对路径与相对路径、配置Servlet与Servlet注解

    一.绝对路径和相对路径 ①开发时建议使用据对路径,使用绝对路径肯定没有问题,但是用相对路径可能会有问题. 在由Servlet转发到JSP页面时,此时在浏览器地址栏显示Sevvlet路径,若JSP页面的 ...

  2. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  3. spring-mvc注解(mvc:annotation-driver,JSON,配置详解)

    一.DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter 的使用已经过时! spring 3.1 开始我们应该用 Reque ...

  4. springMVC注解方式+easyUI+MYSQL配置实例

    刚接触springMVC,使用的注解方式,也在学习阶段,所以把自己学习到的记下来.本文利用springMVC从数据库读取用户信息为例,分享一下. 1.准备相关架包及资源.因为使用springMVC+e ...

  5. (D)spring boot使用注解类代替xml配置实例化bean

    bean经常需要被实例化,最常见的就是new一个呗,Bean bean = new Bean(),方便好用还快捷. 然而在我们刚开始学习写i项目的时候却发现,new不好用哦,并且也不报错,根本不知道怎 ...

  6. @WebServlet注解(Servlet注解)

    @WebServlet 注解的属性 @WebServlet 用于将一个类声明为 Servlet,该注解会在部署时被容器处理,容器根据其具体的属性配置将相应的类部署为 Servlet.该注解具有下表给出 ...

  7. Spring系列9:基于注解的Spring容器配置

    写在前面 前面几篇中我们说过,Spring容器支持3种方式进行bean定义信息的配置,现在具体说明下: XML:bean的定义和依赖都在xml文件中配置,比较繁杂. Annotation-based ...

  8. Java开发学习(四十六)----MyBatisPlus新增语句之id生成策略控制及其简化配置

    在前面有一篇博客:Java开发学习(四十一)----MyBatisPlus标准数据层(增删查改分页)开发,我们在新增的时候留了一个问题,就是新增成功后,主键ID是一个很长串的内容. 我们更想要的是按照 ...

  9. C#开发中使用配置文件对象简化配置的本地保存

    C#开发中使用配置文件对象简化配置的本地保存 0x00 起因 程序的核心是数据和逻辑,开发过程中免不了要对操作的数据进行设置,而有些数据在程序执行过程中被用户或程序做出的修改是应该保存下来的,这样程序 ...

  10. WCF学习之旅—WCF4.0中的简化配置功能(十五)

    六 WCF4.0中的简化配置功能 WCF4.0为了简化服务配置,提供了默认的终结点.绑定和服务行为.也就是说,在开发WCF服务程序的时候,即使我们不提供显示的 服务终结点,WCF框架也能为我们的服务提 ...

随机推荐

  1. LOTO示波器电源环路增益分析客户实测

    我们在之前有文章介绍过LOTO示波器+信号源扫频测电源环路增益稳定性的方法和过程,可以参考演示视频如下: https://www.ixigua.com/7135738415382790663?logT ...

  2. date_histogram,es按照时间分组统计

    日期直方图聚合(date_histogram) 与histogram相似,es中内部将日期表示为一个long值,所以有时候可以用histogram来达到相同的目的,但往往没有date_histogra ...

  3. Linux 基础命令和帮助命令

    Linux命令 X Window 与命令行模式的切换   Linux默认的情况下会提供六个终端来让用户登录,切换的方式为使用[Ctrl+Alt+F1~F6]的组合键.系统会将[F1 ~ F6]命名为t ...

  4. ET8开发微信小游戏之部署云服务器Nginx代理

    最近用ET8搞微信小游戏测试,部署到云服务器,手机上运行,必须要用https备案过得域名,客户端使用websocket创建必须要wss开头,服务端部分通过Nginx进行https通信之后转发到云服务器 ...

  5. #树套树,二维线段树#HDU 4819 Mosaic

    题目 多组数据,给定一个\(n*n\)的矩阵(\(n\leq 80,a_{i,j}\leq 10^9\)) 多组询问一个以\((x,y)\)为中心,边长为\(L\)的子矩阵最大值\(mx\)和最小值\ ...

  6. #根号分治,树上倍增#洛谷 3591 [POI2015]ODW

    题目 分析 考虑直接用倍增跳会TLE,设\(f[x][i]\)表示以\(x\)为起点每次跳\(i\)步的点权和, 这可以预处理出来,综合一下两种做法,当\(i>\sqrt{n}\)时直接上倍增, ...

  7. C#针对 private Dictionary<String, String> list说明

  8. JS从图片base64数据中获取图片的宽高

    // js从base64数据中获取宽高 var image = new Image(); image.src = "data:image/png;base64," + base64 ...

  9. redis 简单整理——客户端通信协议[十五]

    前言 简单介绍一下客户端的通信协议. 正文 第 一,客户端与服务端之间的通信协议是在TCP协议之上构建的. 第二, Redis制定了RESP(REdis Serialization Protocol, ...

  10. Flink 自定义 ClickHouse Table Connector 的简单实现

    本次实现基于 Flink 1.18 版本,具体的对象之间的关系可以先参考官网的图: 先拿官网上的 Socket 示例来说一下实现过程: 首先编写 SocketDynamicTableFactory 实 ...