Servlet | Servlet基于注解式开发、使用模板方法设计模式解决类爆炸
目录
一:Servlet基于注解式开发
使用注解解决web.xml配置文件问题!
1、分析前面写的oa项目中的web.xml文件
(1)目前只是一个单表的CRUD,没有复杂的业务逻辑,很简单的功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml文件会非常庞大,有可能最终会达到几十兆。
(2)在web.xml文件中进行servlet信息的配置,显然开发效率比较低,每一个类都需要配置一下。
(3)并且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?当然是可以的!
2、注解式开发
(1)Servlet3.0版本之后,推出了各种Servlet基于注解式开发。
(2)优点是开发效率高,不需要编写大量的配置信息。直接在java类上使用注解进行标注,
web.xml文件体积变小!
(3)并不是说注解有了之后,web.xml文件就不需要了;有一些需要变化的信息,还是要配置到web.xml文件中;一般都是 注解+配置文件 的开发模式。
3、第一个注解式开发
javax.servlet.annotation.WebServlet // 在这个包下
(1)在Servlet类上使用:@WebServlet,WebServlet注解中常用的属性
①name属性:用来指定Servlet的名字。等同于:<servlet-name>
String name() default "";
②urlPatterns属性:用来指定Servlet的映射路径,可以指定多个字符串。等同于:<url-pattern>
String[] urlPatterns() default {};
③loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet。等同于:<load-on-startup>
int loadOnStartup() default -1;
④initParams属性:用来初始化参数的标签。等同于:<init-param>
WebInitParam[] initParams() default {};
// WebInitParam也是一个注解,注解里面套注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam {
String name();
String value();
String description() default "";
}
(2)value属性:value属性和urlPatterns属性一致,都是可以用来指定Servlet的映射路径的。只不过当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略!
(3)注意:属性是一个数组,如果数组中只有一个元素,使用该注解的时候,属性值的大括号可以省略。
(4)注解对象的使用格式: @注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)
(5)例:不使用web.xml配置的方式,使用注解的方式进行开发
package com.bjpowernode.javaweb.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
/**
* @Author:朗朗乾坤
* @Package:com.bjpowernode.javaweb.servlet
* @Project:JavaWeb
* @name:HelloServlet
* @Date:2022/11/15 14:17
*/
@WebServlet(name="hello",
urlPatterns = {"/hello1","/hello2"},
loadOnStartup = 1,
initParams = {@WebInitParam(name="username",value="root"),@WebInitParam(name="password",value="123")})
public class HelloServlet extends HttpServlet {
// 无参构造方法
// 前面在注解中配置了loadOnStartup = 1,表示在服务器启动时就加载Servlet
public HelloServlet() {
System.out.println("无参构造方法HelloServlet方法执行完了");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;chasrset=UTF-8");
PrintWriter out = response.getWriter();
// 获取Servlet name
String servletName = this.getServletName();
out.print("servlet name = " + servletName + "<br>"); // servlet name = hello
// 获取Servlet path
String servletPath = request.getServletPath();
out.print("servlet path = " + servletPath + "<br>"); // servlet path = /hello1
// 获取初始化参数
Enumeration<String> initParameterNames = this.getInitParameterNames();
while(initParameterNames.hasMoreElements()){
String name = initParameterNames.nextElement();
// 通过key获取value
String value = getInitParameter(name);
out.print(name + "=" + value + "<br>"); // username=root password=123
}
}
}
(6)通过反射机制拿到注解的信息
package com.bjpowernode.javaweb.servlet;
import javax.servlet.annotation.WebServlet;
/**
* @Author:朗朗乾坤
* @Package:com.bjpowernode.javaweb.servlet
* @Project:JavaWeb
* @name:ReflectAnnotation
* @Date:2022/11/15 16:23
*/
public class ReflectAnnotation {
public static void main(String[] args) throws Exception {
// 使用反射机制将类上面的注解进行解析
// 获取类Class对象
Class<?> helloClass = Class.forName("com.bjpowernode.javaweb.servlet.HelloServlet");
// 获取这个类上面的注解对象
// 先判断这个类上面有没有这个注解对象,如果有这个注解对象,就获取该注解对象。
boolean annotationPresent = helloClass.isAnnotationPresent(WebServlet.class);
if (annotationPresent){
// 获取这个类上面的注解对象
WebServlet annotation = helloClass.getAnnotation(WebServlet.class);
// 获取注解的urlPatterns属性值
String[] values = annotation.urlPatterns();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);// /hello1 /hello2
}
}
}
}
二:使用模板方法设计模式解决类爆炸
使用模板方法设计模式解决类爆炸问题!
(1)上面的注解解决了配置文件的问题;但是现在的oa项目仍然存在一个代码比较臃肿的问题。
①一个单标的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大)
②怎么解决这个类爆炸问题?可以使用模板方法设计模式。
(2)怎么解决类爆炸问题?
①以前的设计方式是一个请求一个Servlet类,导致类爆炸。
②优化:让一个请求对应一个方法,一个业务才对应一个Servlet类。
(3)通过使用注解的方式和模板设计方法的模式,对oa项目:单表的CRUD操作进行优化
①重写service方法(并没有重写doGet或者doPost)
②并不知道发送的是doGet或者doPost请求,所以重写它们的上一层方法
③也可以doGet或者doPost请求都重写,但是要在doGet调doPost,或者doPost调doGet
package com.bjpowernode.oa.web.action;
import com.bjpowernode.oa.utils.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;
// 模板类,省略了value
@WebServlet({"/dept/list", "/dept/save", "/dept/edit", "/dept/detail", "/dept/delete", "/dept/modify"})
// 模糊匹配
// 只要请求路径是以"/dept"开始的,都走这个Servlet。
//@WebServlet("/dept/*")
public class DeptServlet extends HttpServlet {
// 模板方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取servlet path
String servletPath = request.getServletPath();
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 ServletException, IOException {
// 获取应用的根路径
String contextPath = request.getContextPath();
// 设置响应的内容类型以及字符集。防止中文乱码问题。
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>部门列表页面</title>");
out.print("<script type='text/javascript'>");
out.print(" function del(dno){");
out.print(" if(window.confirm('亲,删了不可恢复哦!')){");
out.print(" document.location.href = '"+contextPath+"/dept/delete?deptno=' + dno");
out.print(" }");
out.print(" }");
out.print("</script>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1 align='center'>部门列表</h1>");
out.print(" <hr >");
out.print(" <table border='1px' align='center' width='50%'>");
out.print(" <tr>");
out.print(" <th>序号</th>");
out.print(" <th>部门编号</th>");
out.print(" <th>部门名称</th>");
out.print(" <th>操作</th>");
out.print(" </tr>");
/*上面一部分是死的*/
// 连接数据库,查询所有的部门
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 获取预编译的数据库操作对象
String sql = "select deptno as a,dname,loc from dept";
ps = conn.prepareStatement(sql);
// 执行SQL语句
rs = ps.executeQuery();
// 处理结果集
int i = 0;
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" <tr>");
out.print(" <td>"+(++i)+"</td>");
out.print(" <td>"+deptno+"</td>");
out.print(" <td>"+dname+"</td>");
out.print(" <td>");
out.print(" <a href='javascript:void(0)' onclick='del("+deptno+")'>删除</a>");
out.print(" <a href='"+contextPath+"/dept/edit?deptno="+deptno+"'>修改</a>");
out.print(" <a href='"+contextPath+"/dept/detail?fdsafdsas="+deptno+"'>详情</a>");
out.print(" </td>");
out.print(" </tr>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
DBUtil.close(conn, ps, rs);
}
/*下面一部分是死的*/
out.print(" </table>");
out.print(" <hr >");
out.print(" <a href='"+contextPath+"/add.html'>新增部门</a>");
out.print(" </body>");
out.print("</html>");
}
private void doSave(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取部门的信息
// 注意乱码问题(Tomcat10不会出现这个问题)
request.setCharacterEncoding("UTF-8");
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行insert语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into dept(deptno, dname, loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
ps.setString(2, dname);
ps.setString(3, loc);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
if (count == 1) {
// 保存成功跳转到列表页面
// 转发是一次请求。
//request.getRequestDispatcher("/dept/list").forward(request, response);
// 这里最好使用重定向(浏览器会发一次全新的请求。)
// 浏览器在地址栏上发送请求,这个请求是get请求。
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 保存失败跳转到错误页面
//request.getRequestDispatcher("/error.html").forward(request, response);
// 这里也建议使用重定向。
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doEdit(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取应用的根路径。
String contextPath = request.getContextPath();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>修改部门</title>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1>修改部门</h1>");
out.print(" <hr >");
out.print(" <form action='"+contextPath+"/dept/modify' method='post'>");
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库,根据部门编号查询部门的信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname, loc as location from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集中只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String location = rs.getString("location"); // 参数"location"是sql语句查询结果列的列名。
// 输出动态网页。
out.print(" 部门编号<input type='text' name='deptno' value='"+deptno+"' readonly /><br>");
out.print(" 部门名称<input type='text' name='dname' value='"+dname+"'/><br>");
out.print(" 部门位置<input type='text' name='loc' value='"+location+"'/><br>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print(" <input type='submit' value='修改'/><br>");
out.print(" </form>");
out.print(" </body>");
out.print("</html>");
}
private void doDetail(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print(" <head>");
out.print(" <meta charset='utf-8'>");
out.print(" <title>部门详情</title>");
out.print(" </head>");
out.print(" <body>");
out.print(" <h1>部门详情</h1>");
out.print(" <hr >");
// 获取部门编号
// /oa/dept/detail?fdsafdsas=30
// 虽然是提交的30,但是服务器获取的是"30"这个字符串。
String deptno = request.getParameter("fdsafdsas");
// 连接数据库,根据部门编号查询部门信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "select dname,loc from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
rs = ps.executeQuery();
// 这个结果集一定只有一条记录。
if(rs.next()){
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print("部门编号:"+deptno+" <br>");
out.print("部门名称:"+dname+"<br>");
out.print("部门位置:"+loc+"<br>");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, rs);
}
out.print(" <input type='button' value='后退' onclick='window.history.back()'/>");
out.print(" </body>");
out.print("</html>");
}
private void doDel(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根据部门编号,删除部门。
// 获取部门编号
String deptno = request.getParameter("deptno");
// 连接数据库删除数据
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
// 开启事务(自动提交机制关闭)
conn.setAutoCommit(false);
String sql = "delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, deptno);
// 返回值是:影响了数据库表当中多少条记录。
count = ps.executeUpdate();
// 事务提交
conn.commit();
} catch (SQLException e) {
// 遇到异常要回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, null);
}
// 判断删除成功了还是失败了。
if (count == 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
//request.getRequestDispatcher("/dept/list").forward(request, response);
response.sendRedirect(request.getContextPath() + "/dept/list");
}else{
// 删除失败
//request.getRequestDispatcher("/error.html").forward(request, response);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
private void doModify(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解决请求体的中文乱码问题。
request.setCharacterEncoding("UTF-8");
// 获取表单中的数据
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 连接数据库执行更新语句
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update dept set dname = ?, loc = ? where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, dname);
ps.setString(2, loc);
ps.setString(3, deptno);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, ps, 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);
response.sendRedirect(request.getContextPath() + "/error.html");
}
}
}
总结:使用了注解式开发优化了web.xml撑爆问题;使用模板方法设计模式解决了类爆炸问题;很大程度的优化了oa项目!
相关文章
- Java抽象类深入理解-----模板方法设计模式(Templete Method)
- 设计模式——模板方法
- 模板方法模式----设计模式系列
- 第二百六十节,Tornado框架-内置模板方法
- 模板方法模式templeteMethod
- 简介Python设计模式中的代理模式与模板方法模式编程
- C#设计模式——模板方法(Template Method)
- ThinkPHP使用smarty模板引擎的方法
- 90、 Android UI模板设计
- 【java设计模式】之 模板方法(Template Method)模式
- intellij idea:设置java方法注释模板(intellij idea 2019.2)
- MATLAB学习笔记 皮尔逊相关系数和模板匹配
- 模板方法(钩子函数)设计模式
- c++模板学习11之类模板与友元
- 【[Offer收割]编程练习赛13 D】骑士游历(矩阵模板,乘法,加法,乘方)
- OpenCV模板检测
- 非常不错的train方法模板
- 设计模式 - 模板方法模式(template method pattern) 具体解释
- 模板形参包的展开方法
- PHP:ThinkPHP5.0视图View模板语法
- python 设计模式之模板方法模式
- 设计模式--模板方法 And State模式
- thinkphp在前端页面的js代码中可以使用 U方法吗? 可以使用模板变量如__URL__等吗?
- 对话框模板简史-介绍
- 模板方法模式
- 【JS高级】ES6_模板字符串、let的简谈与应用_12
- 【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性
- 基于easypoi实现自定义模板导出excel
- springboot使用jxls导出excel___(万能通用模板)--- SpringBoot导入、导出Excel文件___SpringBoot整合EasyExcel模板导出Excel