一、配置文件
- IDEA
- Mysql8.0 和 SQLyog
- Tomcat服务器
- jar包:
commons-logging-1.2.jar
druid-1.1.22.jar
mysql-connector-java-8.0.21.jar
spring-beans-5.1.10.RELEASE.jar
spring-core-5.1.10.RELEASE.jar
spring-jdbc-5.1.10.RELEASE.jar
spring-tx-5.1.10.RELEASE.jar - 自定义的配置文件:druid.properties
url=jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=GMT%2B8
username=root
password=root
driverClassName=com.mysql.cj.jdbc.Driver
#最初连接数
initialSize=10
#最大连接数
maxActive=20
#最多等待时间
maxWait=1000
filters=wall
#是否开启超时自动回收连接
spring.datasource.removeAbandoned=true
#设置自动回收连接的超时时间
spring.datasource.removeAbandonedTimeout=180
#打印回收连接时的错误日志
spring.datasource.logAbandoned=true
- 登录界面login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request/Servlet_demo1" method="post">
账号:<input type="text" name="username"> <br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
二、工具类
- informationadd :javabean类,用来set和get用户信息
public class informationadd {
private int id;//手机号
private String username;//昵称
private String sex;//性别
private String date;//出生日期
private String password;//密码
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "informationadd{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", date='" + date + '\'' +
", password='" + password + '\'' +
'}';
}
}
- Util_Druid:加载druid.properties配置文件和得到数据库连接池对象
public class Util_Druid {
private static DataSource ds ;
static {
try {
//1.创建配置文件对象
Properties pro = new Properties();
//2.加载配置文件
InputStream in = Util_Druid.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(in);
//3.创建DataSource 连接池
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//4.创建连接对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//返回DateSource连接池对象
public static DataSource getDataSource(){
return ds;
}
//5.释放资源
public static void close(ResultSet result, Statement state , Connection conn){
if (result != null){
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- Database_method:操作数据库连接池对象,SetUser判断数据库是否存在账号,存入封装类,InsertUser在封装类中取出信息添加到数据库中
public class Database_method {
//封装类 存放的是用户信息
private informationadd informat = new informationadd();
//JdbcTemplate 操作数据库连接池对象
private JdbcTemplate temp = new JdbcTemplate(Util_Druid.getDataSource());
// public static void main(String[] args) {
// Database_method method = new Database_method();
// int i = method.SetUser(4,"22","男","44","11");
// int j = 0;
// if (i==1){//得到返回值i为1的话就存到数据库
// j = method.InsertUser();
// System.out.println("存入封装类成功...");
// }else System.out.println("存在此手机号,存入封装类失败...");
//
// if (j==1){
// System.out.println("存入数据库成功...");
// }else System.out.println("存入数据库失败...");
// }
//先将用户的信息存储到封装类里,返回0就是没存入封装类里,返回1就是存到了封装类
public int SetUser(int id,String username,String sex ,String date,String password){
//如果id不空
if (id != 0){
//查询数据库中有没有相同的手机号,
String sql = "select count(*) from userhelp where id = ? ";
//返回查询到的行数,如果有就是count=1
int count = temp.queryForObject(sql,int.class,id);
if (count == 0){//count=0说明没有查询到,也就是没此手机号,可以新建
informat.setId(id);//手机号放入封装类
System.out.println("没有此手机号,可以存入封装类");
if (username != null && sex != null && date != null) {
informat.setUsername(username);//将昵称放入封装类中
informat.setSex(sex);//将性别放入封装类
informat.setDate(date);//将日期放入类中
informat.setPassword(password);//将密码放入类中
return 1;
}else {
System.out.println("昵称、性别或者日期 存在空值");
return 0;
}
}
}
//判断第一次输入密码和第二次输入密码是否相同,这个在客户端完成就可以
return 0;
}
//将设置好的封装类加入数据库表中,返回执行条数若是为0就是没存入数据库,返回1就是加入成功了
public int InsertUser(){
String sql = "insert into userhelp(id,username,sex,date,password) value(?,?,?,?,?)";
int update = temp.update(sql, informat.getId(), informat.getUsername(), informat.getSex(), informat.getDate(), informat.getPassword());
return update;
}
}
四、HttpServlet类
接收到服务器页面的消息参数,调用上面Database_method类的两个方法将消息参数存储到数据库,并且判断成功与否。
@WebServlet("/Servlet_demo1")
public class Servlet_demo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置参数编码
request.setCharacterEncoding("utf-8");
//获得参数
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("账号:"+username+"--"+"密码:"+password);
int ss=0;
try
{
ss = Integer.parseInt(username);
}catch (Exception e){
System.out.println("您输入的账号不是数字,数据库存储失败");
System.out.println("-----------分隔符--------------");
return;
}
Database_method method = new Database_method();
int i = method.SetUser(ss,"22","男","44",password);
int j = 0;
if (i==1){//得到返回值i为1的话就存到数据库
j = method.InsertUser();
System.out.println("存入封装类成功...");
}else {
System.out.println("存在此账号,存入封装类失败...");
}
if (j==1){
System.out.println("存入数据库成功...");
System.out.println("-----------分隔符--------------");
}else {
System.out.println("存入数据库失败...");
System.out.println("-----------分隔符--------------");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
五、运行结果
我尝试了三组数据,第一第二组完全一样,第三组我把账号故意写成字符类型,下面的运行结果皆正常。
1.界面

2.控制台和数据库页面

六、遇到的BUG
1.如图HTTP页面报错状态500
原因和解决办法请看我的另外一篇文章:上图中的问题产生原因和解决办法
- 目前位置还有一个问题没解决,在我关闭服务器之后报错
26-Aug-2020 18:14:14.179 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc Web应用程序 [request] 注册了JDBC驱动程序 [com.alibaba.druid.proxy.DruidDriver],但在Web应用程序停止时无法注销它。 为防止内存泄漏,JDBC驱动程序已被强制取消注册。
26-Aug-2020 18:14:14.179 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc Web应用程序 [request] 注册了JDBC驱动程序 [com.mysql.cj.jdbc.Driver],但在Web应用程序停止时无法注销它。 为防止内存泄漏,JDBC驱动程序已被强制取消注册。
26-Aug-2020 18:14:14.180 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Web应用程序[request]似乎启动了一个名为[mysql-cj-abandoned-connection-cleanup]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:85)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
java.lang.Thread.run(Thread.java:748)]
26-Aug-2020 18:14:14.180 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Web应用程序[request]似乎启动了一个名为[Druid-ConnectionPool-Create-1627339834]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2754)]
26-Aug-2020 18:14:14.181 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Web应用程序[request]似乎启动了一个名为[Druid-ConnectionPool-Destroy-1627339834]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[
java.lang.Thread.sleep(Native Method)
com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:2850)]
26-Aug-2020 18:14:14.182 严重 [main] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks web应用程序[request]创建了一个ThreadLocal,其键类型为[java.lang.ThreadLocal](值为[java.lang.ThreadLocal@319b92f3]),值类型为[com.alibaba.druid.wall.spi.WallVisitorUtils.WallTopStatementContext](值为[com.alibaba.druid.wall.spi.WallVisitorUtils$WallTopStatementContext@fcd6521),但在停止web应用程序时未能将其删除。线程将随着时间的推移而更新,以尝试避免可能的内存泄漏
26-Aug-2020 18:14:14.192 信息 [main] org.apache.coyote.AbstractProtocol.stop 正在停止ProtocolHandler ["http-nio-8080"]
26-Aug-2020 18:14:14.194 信息 [main] org.apache.coyote.AbstractProtocol.destroy 正在摧毁协议处理器 ["http-nio-8080"]
从上面报错来看,我感觉是我用了Druid的连接池产生的问题,但是木有关闭它的办法,所以访问一次页面就会新建一个连接池,每个连接池又会新建10个连接对象,然后我手动关闭服务器这些对象得不到释放,导致多个线程tomcat也不能正常关闭,这样就有可能内存泄露,只能强制关闭,然后飘红报错。
经过我一顿搜索终于手动关闭了Driver注册和闲置检测:Daemon Thread [Abandoned connection cleanup thread] ,是用的以下代码,是我写了一个监听器。
public class MyContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("webService start");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("webService stop");
try {
//手动取消jdbc程序的注册
while(DriverManager.getDrivers().hasMoreElements()) {
DriverManager.deregisterDriver(DriverManager.getDrivers().nextElement());
}
System.out.println("jdbc Driver close");
//手动停止名为[mysql-cj-abandoned-connection-cleanup]的线程
AbandonedConnectionCleanupThread.uncheckedShutdown();
System.out.println("clean thread success");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
还有要在web.xml添加
<!--监听侦-->
<listener>
<listener-class>cn.web.servlet.MyContextListener</listener-class>
</listener>
但是还有如下错误:
26-Aug-2020 20:42:20.636 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Web应用程序[request]似乎启动了一个名为[Druid-ConnectionPool-Create-2099465138]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2754)]
26-Aug-2020 20:42:20.637 警告 [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Web应用程序[request]似乎启动了一个名为[Druid-ConnectionPool-Destroy-2099465138]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[
java.lang.Thread.sleep(Native Method)
com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:2850)]
26-Aug-2020 20:42:20.638 严重 [main] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks web应用程序[request]创建了一个ThreadLocal,其键类型为[java.lang.ThreadLocal](值为[java.lang.ThreadLocal@103f852]),值类型为[com.alibaba.druid.wall.spi.WallVisitorUtils.WallTopStatementContext](值为[com.alibaba.druid.wall.spi.WallVisitorUtils$WallTopStatementContext@587c290d),但在停止web应用程序时未能将其删除。线程将随着时间的推移而更新,以尝试避免可能的内存泄漏
26-Aug-2020 20:42:20.648 信息 [main] org.apache.coyote.AbstractProtocol.stop 正在停止ProtocolHandler ["http-nio-8080"]
26-Aug-2020 20:42:20.650 信息 [main] org.apache.coyote.AbstractProtocol.destroy 正在摧毁协议处理器 ["http-nio-8080"]
Disconnected from server
从上边报错看出来也就是下面这三个线程没关,但。。我不会关闭。。。。。。。
创建连接:Daemon Thread [Druid-ConnectionPool-Create-1184124073]
废弃连接:Daemon Thread [Druid-ConnectionPool-Destroy-1184124073]
web应用程序[request]创建了一个ThreadLocal
各位路过的大佬有没有关闭这几个线程的办法?
版权声明:本文为kaikai_gege原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。