有句谚语说:“一怒之下踢石头,只有痛着脚趾头。”
比一件糟糕的事情更可拍的,是你用糟糕的态度去面对它。看过一个很有意思的故事:
有个男人清早洗漱的时候,把自己的手表放在了桌子上。他的儿子不小心把手表碰倒地上摔坏了,男人
气得儿子揍了一顿,还埋怨妻子没看好儿子,两个人吵了起来。
男人气急败坏地摔门出去,路上想起有一份重要文件忘记带了,他匆忙回家取。可没有人在家,
他只得打电话让妻子回来送钥匙。妻子赶回家时,不小心撞翻了路边的一个小吃摊,赔了一笔钱。
男人没等到妻子回家,因为迟到也遭受了罚款。
费斯汀格法则认为:“10%的生活,由发生在你身上的事情组成,而另外的90%,则取决于你做出的反应。”
这个故事中。摔坏手表就是其中10%,而后面的一系列的事情则是另外的90%,是由埋怨引起的。
面对人生得失时,一颗平常心,比一百种智慧更有力量。
人这一生,得与失,都是常态。不是此处得,彼处失,就是彼处得,此处失,得得失失,失失得得,才构成了“人间事”。
正如席慕容的那首诗《写给幸福》:“挫折会来,也会过去。热泪会流下来,也会收起。没有什么
可以让我气馁的,因为我有着长长的一生。”
别为昨日忧愁,别为琐事烦忧,因为真正的人间清醒,是努力活着。
余生,愿你所有快乐,无需假装;愿你此生尽兴,赤诚善良。
—————— 《一禅心灵庙语》
分析 oa
项目中的 web.xml
文件 具体的可以移步至: 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客
web.xml
文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话 web.xml
文件会非常庞大,有可能最终会达到几十兆。web.xml
文件中进行 servlet
信息的配置,显然开发效率比较低,每一个都需要配置一下。web.xml
文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类
当中呢?可以的。Servlet3.0
版本之后,推出了各种Servlet
基于注解式开发。优点是什么?
开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。
web.xml
文件体积变小了。
并不是说注解有了之后,web.xml
文件就不需要了:
web.xml
文件中。一般都是 注解+配置文件
的开发模式。注解对象的使用格式:
@注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)
// 如果注解当中的属性赋值的类型是数组,格式如下
@注解名称(属性名={属性值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 "";
}
在 WebServlet 当中存在着不少的属性,这里我们只介绍一些常用的属性。其他的大家感兴趣的可以去学习。
/**
* @return name of the Servlet
*/
String name() default ""; // default 表示该属性的默认值为 "" 空字符串
注意: 需要注意的是:urlPatterns
所赋值的字符串映射的url 路径要带 /
开始的
/**
* @return array of URL patterns to which this Filter applies
*/
String[] urlPatterns() default {}; // 是一个字符串数组的类型,因为一个Servlet 可以有多个映射路径 url
// 注意 urlPatterns 属性值和 web.xml 当中的 带 "/" 开始
举例:
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中的的值: " + name + "
");
// 获取到该类当中 web.xml 中的 url-pattern 的值
// 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
String servletPath = request.getServletPath();
writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "
");
}
}
@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 + "
");
}
}
注意: 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 + "
");
// 获取到初始化参数,对应的一个Servlet 标签当中的 标签当中设置
String username = getInitParameter("user"); // 根据对应的配置的 name 获取到对应的 value 值
writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 user的值: " + username + "
");
String password = getInitParameter("password");
writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 password的值: " + password + "
");
}
}
/**
* @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 的构造器执行");
}
}
注意:建议: 如果你阅读到这里时,非常感谢您的大力支持,如果还要继续阅读的话,建议先移步至: 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客 博客,方便后续的内容上的理解阅读。
上面的**@WebServlet** 注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。
怎么解决类爆炸问题?
Servlet
类。1000个请求对应1000个Servlet类。导致类爆炸。思想:
模板方法:父类定义骨架,子类实现某些细节。而这里我们将骨架定义为一个核心的方法也就是这里重写的父类中的 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(" ");
writer.println("");
writer.println("");
writer.println(" ");
writer.println(" 部门列表页面 ");
writer.println("");
writer.println(" ");
writer.println("");
writer.println(" 部门列表
");
writer.println(" ");
writer.println(" ");
writer.println(" 序号 ");
writer.println(" 部门编号 ");
writer.println(" 部门名称 ");
writer.println(" ");
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(" ");
writer.print(" " + (++i) + " ");
writer.print(" " + det + " ");
writer.print(" " + dname + " ");
writer.print(" ");
writer.print(" + det + ")'>删除");
// 将部门编号传过去,用户数据库查询修改
writer.print(" 修改");
//注意这里的是前端的资源,需要加项目名,但是这里的项目名我们通过 getContestPath()方法动态获取
// 并且将部门名传过去,再从数据库当中查找出来对应的部门的详细信息:注意: ?(间隔) Http传输协议
writer.print(" 详情");
writer.print(" ");
writer.print(" ");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 关闭资源
DBUtil.close(connection, preparedStatement, resultSet);
}
writer.println("
");
writer.println("
");
// 前端的资源路径访问需要加项目名
writer.println("新增部门");
writer.println("");
writer.println("");
}
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(" ");
writer.println("");
writer.println("");
writer.println(" ");
writer.println(" 部门列表页面 ");
writer.println("");
writer.println("");
writer.println(" 修改部门
");
writer.println(" );
/*
思路:
获取到提交的过来的 部门编号
根据部门编号修改信息,注意:部门编号是唯一的不要被修改了
连接数据库,查询到相关信息显示到浏览器页面当中,方便用户修改
*/
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");
//