Servlet实战(2)

Servlet实战(2) 

  

Servlet架构图

 

 

在实战1中自己对Servlet的使用已经慢慢的熟悉了很多,而且自己在学习中有对比、有发散,也学到了很多东西。在实战2中,我抛弃了Servlet3.0之前的方式,开始全面使用Servlet3.0的规则,即使用注解方式。注意Servlet3.0中Filter上的注解无法排序问题,排序的话可以根据Filter名进行排序A-Z

 

Servlet Cookie处理

实战操作cookie

cookie中文编码和解码

cookie中文测试

有关cookie的更多设置

cookie的获取

Servlet Session跟踪

维持session会话的三种方式

Cookies

URL重写

隐藏表单字段

Servlet 数据库访问

在普通的java类中使用mysql

web中使用mysql

预编译、返回主键

Servlet 文件上传和下载

Servlet3.0之前上传

Servlet3.0上传

大坑

总结

项目的部署路径

总结

eclipse中的Server工程和tomcat的关系

源服务器和克隆服务器

优点

 

 

Servlet Cookie处理

在阅读了大量的cookie与session的文章后,在回过头来看以下菜鸟教程的cookie介绍,就显得简单许多了,cookie并不是在服务器端创建的,服务器端只是向客户端发送创建指令(Set-Cookie),将要创建的cookie放在请求头中,发送(多个cookie,则是发送一组cookie)到客户端(一般是浏览器),浏览器解析后创建cookie,如果这些cookie声明了存活时间,则会被写入客户端文本文件中,如果没有声明则仅仅存在于一次对话中,当客户端浏览器关闭cookie失效。 

 

实战操作cookie

cookie中文编码和解码

如果要想客户端存入中文或者获取客户端存入的中文,都需要进行处理。

String str1 = java.net.URLEncoder.encode("中文","UTF-8"); // 转码
String str2 = java.net.URLDecoder.decode("%E4%B8%AD%E6%96%87","UTF-8"); // 解码

cookie中文测试

总结:cookie的name、value、path、domain都不可以使用中文!对于name不能使用TSPECIALS声明的字符。如果需要使用中文就像上一节那样进行转码。

 

其实我在想,在tomcat7以后,tomcat的编码格式默认都使用UTF-8了,还有必要这样在设置一遍吗?之前在Servet实战(1)中也有说到编码问题,如果依赖的是tomcat,我想设置返回页面的编码格式应该也是可以的,测一测吧:

新建 SetCookieServlet

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/SetCookieServlet")
public class SetCookieServlet extends HttpServlet{

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        
        String name = req.getParameter("name");
        String site = req.getParameter("site");
        
        Cookie nameCookie = new Cookie("name", name);//URLEncoder.encode(name, "UTF-8")
        Cookie siteCookie = new Cookie("site", site);
        resp.addCookie(nameCookie);
        resp.addCookie(siteCookie);
        
        out.println(name);
        out.println(site);
    }
    
}

WebContent下新建cookie.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<style>
    div {
        200px;
        height:200px;
        border:1px solid black;
        margin:10px;
    }
</style>
<body>
<div>
<!-- 这里的action就是web.xml里配置的url-pattern,不加/,如果是注解,则直接些Servlet名 -->
<form action="SetCookieServlet" method="post">
    网站名:<input type="text" name="name"><br>
    站点:<input type="text" name="site">
    <input type="submit">
</form>
</div>
</body>
</html>

启动项目,在网站名中输入中文,会发现后台报错了,跟踪后发现在第28行抛出了异常。

java.lang.IllegalArgumentException: Control character in cookie value or attribute.
    at org.apache.tomcat.util.http.LegacyCookieProcessor.needsQuotes(LegacyCookieProcessor.java:412)
    at org.apache.tomcat.util.http.LegacyCookieProcessor.generateHeader(LegacyCookieProcessor.java:284)
    at org.apache.catalina.connector.Response.generateCookieString(Response.java:940)
    at org.apache.catalina.connector.Response.addCookie(Response.java:888)
    ...

我们将异常信息定位一下,从上面打印的堆栈信息可以看出,异常是在response.addCookie导致的,继续向上定位:

// org.apache.catalina.connector.Response
 
@Override
    public void addCookie(final Cookie cookie) {

        // Ignore any call from an included servlet
        if (included || isCommitted()) {
            return;
        }

        cookies.add(cookie);

        String header = generateCookieString(cookie);

        addHeader("Set-Cookie", header, getContext().getCookieProcessor().getCharset());
    }
    
    public String generateCookieString(final Cookie cookie) {
        // Web application code can receive a IllegalArgumentException
        // from the generateHeader() invocation
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return AccessController.doPrivileged(new PrivilegedAction<String>() {
                @Override
                public String run(){
                    return getContext().getCookieProcessor().generateHeader(cookie);
                }
            });
        } else {
            return getContext().getCookieProcessor().generateHeader(cookie);
        }
    }

response的addCookie方法里可以看出使用了流的方式对cookie进行了处理,继续跟踪到generateCookieString(cookie)方法在到generateHeader:

// org.apache.tomcat.util.http.LegacyCookieProcessor

public String generateHeader(Cookie cookie) {
        int version = cookie.getVersion();
        String value = cookie.getValue();
        String path = cookie.getPath();
        String domain = cookie.getDomain();
        String comment = cookie.getComment();

        if (version == 0) {
            // Check for the things that require a v1 cookie
            if (needsQuotes(value, 0) || comment != null || needsQuotes(path, 0) || needsQuotes(domain, 0)) {
                version = 1;
            }
        }
        ...
    }
    
    private boolean needsQuotes(String value, int version) {
        if (value == null) {
            return false;
        }

        int i = 0;
        int len = value.length();

        if (alreadyQuoted(value)) {
            i++;
            len--;
        }

        for (; i < len; i++) {
            char c = value.charAt(i);
            if ((c < 0x20 && c != '	') || c >= 0x7f) {
                throw new IllegalArgumentException(
                        "Control character in cookie value or attribute.");
            }
            if (version == 0 && !allowedWithoutQuotes.get(c) ||
                    version == 1 && isHttpSeparator(c)) {
                return true;
            }
        }
        return false;
    }

阅读needsQuotes源码可以发现,这是一个对value参数进行中文校验的函数,如果value参数是中文就会抛出IllegalArgumentException,在回到generateHeader方法,查找对needsQuotes方法的调用,就会发现在generateHeader方法,对cookie的value、path、domain都进行了中文校验。

这我们就明白了,在cookie的value、path、domain都是不能使用中文的,那cookie的name能不能使用中文呢?我们来看以下cookie的构造函数就知道了:

// javax.servlet.http.Cookie

public Cookie(String name, String value) {
        if (name == null || name.length() == 0) {
            throw new IllegalArgumentException(
                    lStrings.getString("err.cookie_name_blank"));
        }
        if (!isToken(name) ||
                name.equalsIgnoreCase("Comment") || // rfc2019
                name.equalsIgnoreCase("Discard") || // 2019++
                name.equalsIgnoreCase("Domain") ||
                name.equalsIgnoreCase("Expires") || // (old cookies)
                name.equalsIgnoreCase("Max-Age") || // rfc2019
                name.equalsIgnoreCase("Path") ||
                name.equalsIgnoreCase("Secure") ||
                name.equalsIgnoreCase("Version") ||
                name.startsWith("$")) {
            String errMsg = lStrings.getString("err.cookie_name_is_token");
            Object[] errArgs = new Object[1];
            errArgs[0] = name;
            errMsg = MessageFormat.format(errMsg, errArgs);
            throw new IllegalArgumentException(errMsg);
        }

        this.name = name;
        this.value = value;
    }
    
     private boolean isToken(String value) {
        int len = value.length();
        for (int i = 0; i < len; i++) {
            char c = value.charAt(i);
            if (c < 0x20 || c >= 0x7f || TSPECIALS.indexOf(c) != -1) {
                return false;
            }
        }

        return true;
    }

在上面的isToken方法里我想你应该看到了有一段if判断和needsQuotes方法是差不多的,也是进行中文校验,校验不通过也会抛出异常。除此之外在isToken方法里还有TSPECIALS这个final 常量需要注意:

static {
        if (Boolean.valueOf(System.getProperty("org.glassfish.web.rfc2109_cookie_names_enforced", "true"))) {
            TSPECIALS = "/()<>@,;:\"[]?={} 	";
        } else {
            TSPECIALS = ",; ";
        }
    }

它规定了cookie的name值不能包含TSPECIALS所声明的这些字符。菜鸟教程这一点是有误的,在此测试记录。

 

总结:cookie的name、value、path、domain都不可以使用中文!对于name不能使用TSPECIALS声明的字符。如果需要使用中文就上上一节那样进行转码。

 

 

 

有关cookie的更多设置

转码+设置存活时间

@WebServlet("/SetCookieServlet")
public class SetCookieServlet extends HttpServlet {

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
            
            // 转码
        String name = URLEncoder.encode(req.getParameter("name"), "UTF-8");
        String site = URLEncoder.encode(req.getParameter("site"), "UTF-8");

        Cookie nameCookie = new Cookie("name", name);
        Cookie siteCookie = new Cookie("site", site);
        
        // 设置过期时间,以秒为单位,下面是一个有效期为1小时的cookie
        nameCookie.setMaxAge(60*60*1);
  
        resp.addCookie(nameCookie);
        resp.addCookie(siteCookie);

    }

}

cookie的获取

 1 package servlet;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 import java.net.URLDecoder;
 6 import javax.servlet.ServletException;
 7 import javax.servlet.annotation.WebServlet;
 8 import javax.servlet.http.Cookie;
 9 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 
13 @WebServlet("/GetCookieServlet")
14 public class getCookieServlet extends HttpServlet {
15 
16     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
17         req.setCharacterEncoding("UTF-8");
18         resp.setContentType("text/html;charset=UTF-8");
19         PrintWriter out = resp.getWriter();
20             
21         Cookie[] cookies = req.getCookies();
22         if(cookies != null) {
23             for (Cookie cookie : cookies) {
24                 out.println(URLDecoder.decode(cookie.getName(), "UTF-8") + ":" + URLDecoder.decode(cookie.getValue(), "UTF-8") + ",expire:" + cookie.getMaxAge());
25             }
26         }else {
27             out.println("请先设置cookie");
28         }
29         
30     }
31 
32 }

当关闭浏览器后siteCookie为过期,但是nameCookie依然存在,因为我们设置了1个小时的存活时间。

cookie的更多操作,可参见【Session Cookie笔记】

 

 

 

Servlet Session跟踪

由于HTTP是一种"无状态"协议,这就意味着服务器端不会保留之前客户端请求的任何记录。如果是来自同一用户短时间内的相同请求,服务器也无法判断是否是同一用户的操作,那到底该怎么样才能识别用户和保持用户信息呢?这就是要说的session。

 

维持session会话的三种方式

Cookies

一个Web服务器可以分配一个唯一的session会话ID作为每个Web客户端的cookie,对于客户端的后续请求可以使用接收到的cookie来识别。

Cookies小测试

这种方式的实现要分析一下,如果客户端请求的是一个JSP文件:

WebContent下新建index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
Hello
</body>
</html>

启动项目,直接在客户端访问index.jsp,我们知道,对于这个JSP文件我们并没有在web.xml里做任何load的配置,也就是说,只有在第一次访问这个index.jsp时才会编译它,编译请求这个index_jsp.java的service方法,返回结果,你可以在浏览器端看到"Hello"。

现在打开EditThisCookie插件,即可看到有一个name为JSESSIONID的cookie,其值为一段码。如果你的客户端禁止了cookie的话,就不会出现JSESSIONID这个cookie。

 

 

Cookies模拟登陆

如果客户端可以使用cookie,我们来写一个简单的登陆:

新建 LoginServlet.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet{

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置post请求编码问题
        req.setCharacterEncoding("UTF-8");
        // 设置返回页面的格式和编码
        resp.setContentType("text/html; charset=UTF-8");
        HttpSession session = req.getSession();
        session.setMaxInactiveInterval(60*60*1);
        
        if(req.getCookies() != null) {
            System.out.println("浏览器端可以使用cookie!");
        }else {
            System.out.println("浏览器端不能使用cookie!");
        }
        
        String username = req.getParameter("username");
        String pwd = req.getParameter("pwd");
        // 校验用户名密码
        if(username.equals("毛毛") && pwd.equals("123456")) {
            System.out.println("登陆成功");
            req.setAttribute("username", username);
            req.getRequestDispatcher("/welcome.jsp").forward(req, resp);
        }else {
            System.out.println("登陆失败,返回重新登陆!");
            req.getRequestDispatcher("/index.jsp").forward(req, resp);
        }
        
    }
    
}

WebContent下新建welcome.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello! <%=request.getAttribute("username") %>
</body>
</html>

启动项目,上面运行的代码,我在测,我也在想,上面的代码并不能满足让服务器记住我的功能,虽然服务器关闭后,我的JSESSIONID被写入了.metadata.pluginsorg.eclipse.wst.server.core mp0workCatalinalocalhostServlet目录下的SESSIONS.ser里,但是我再次打开页面访问还是要输入用户名和密码,哦哦,好像保存用户名和密码这是cookie要干的事情,用户在第一次登陆后,创建2个长久的cookie即用户名和密码,在创建一个短暂的cookie(session),这个cookie要和服务器端session存活时间保持一致才行(session的存活时间可以被刷新,但是cookie不行,所以每次刷新session时,cookie也要手动刷新)。但有个问题就是当客户端关闭在次从开,访问首页,服务器根据客户端的JSESSIONID在次访问服务器,服务器认识不认识这个JSESSIONID呢?我们来测试一下:

新建SessionAtBrower.java

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/SessionAtBrower")
public class SessionAtBrower extends HelloServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
           System.out.println(session);
        Cookie scookie = new Cookie("JSESSIONID", session.getId());
        scookie.setMaxAge(60 * 60 * 1);
        resp.addCookie(scookie);
    }

}

在这个SessionAtBrower.java里我们将第一次请求时创建的JSESSIONID写入了cookie中,而且设置了这个cookie的过期时间为一个小时后,访问得到JSESSIONID=4EF6EE406AA8EA47A6AC7C036AE68B76。猜想,我现在关闭chrome浏览器,再次打开它访问这个地址,要么再次获取的cookie里的和上面这个相等,相等就说明了一个问题:服务器端是先根据客户端中cookie为JSESSIONID新建或获取已存在的session,如果cookie中JSESSIONID存在,就去服务器端找这个session.id = JSESSIONID的session,找到了(代表存活)将这个session返回,没找到就新建一个session并将该session.id = JSESSIONID将这个新建的session返回。不相等就服务器端使用session并不能保存会话!!!

测试发现,两次浏览器端cookie里的JSESSIONID是一致的,都是4EF6EE406AA8EA47A6AC7C036AE68B76,而且服务器端打印的session对象也是一样的!

总结:服务器端是先根据客户端中cookie为JSESSIONID新建或获取已存在的session。

1.如果cookie中JSESSIONID不存在:第一次访问服务器如果调用了request.getSession()则会在服务器端生成一个session然后将这个session对象的id发送的浏览器的cookei中(JSESSIONID=session.id)。

2.如果cookie中JESSIONID存在:就去服务器端找这个session.id = JESSIONID的session,找到了(代表存活)将这个session返回,没找到就新建一个session并将该session.id = JESSIONID将这个新建的session返回。

 

经过上面的测试发现当两次使用相同浏览器去访问服务器时(第一次关闭浏览器后第二次在启动访问),服务器时认是你的,认识你就好办多了,这样我们写登陆这块首先在第一次访问时要往客户端保存3个cookie,username,pwd,JSESSIONID,有了JSESSIONID我在关闭打开浏览器访问时,就用这个JESSIONID去校验一下子,这样校验,还是request.getSession()返回一个session然后调用这个session.isNew()判断这个session是不是新建的,如果是说明之前的session过期了,过期了的话就重新登陆校验一下,没过期就不在登陆校验了,直接变为已登录。

@WebServlet("/SessionAtBrower")
public class SessionAtBrower extends HelloServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        if(session.isNew()) {
            System.out.println("上次访问的session过期啦,要再次验证一下用户名密码,成功后转到首页");
        }else {
            System.out.println("重定向到首页");
        }
        System.out.println(session);
        Cookie scookie = new Cookie("JSESSIONID", session.getId());
        scookie.setMaxAge(60*60*1);
        resp.addCookie(scookie);
    }

}

在测一测,第一次访问,关闭浏览器,再次打开浏览器访问得到结果如下:

信息: 拦截的地址:http://localhost:8080/Servlet/SessionAtBrower
上次访问的session过期啦,要再次验证一下用户名密码,成功后转到首页
org.apache.catalina.session.StandardSessionFacade@1f9b3ca8
五月 08, 2019 8:05:47 下午 filter.LoggerFilter doFilter
信息: 拦截的地址:http://localhost:8080/Servlet/SessionAtBrower
重定向到首页
org.apache.catalina.session.StandardSessionFacade@1f9b3ca8

哈哈哈哈!按照这个思路我们的登陆模块终于可以完成啦!

 

 

Cookies登陆实战

从新修改一下我们之前的index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%@ page import="java.net.*" %>
<%
if(session.isNew()) {
%>
<form action="LoginServlet" method="post">
用户名:<input type="text" name="username" />
密码:<input type="password" name="pwd" />
<input type="submit" />
</form>
<%
}else {
    System.out.println("重定向到首页");
    Cookie[] cookies = request.getCookies();
    String username = "";
    if(cookies != null) {
        for (Cookie cookie : cookies) {
            if(URLDecoder.decode(cookie.getName(), "UTF-8").equals("username")){
                username = URLDecoder.decode(cookie.getValue(), "UTF-8");
            }
        }
    }
    request.setAttribute("username", username);
    request.getRequestDispatcher("/welcome.jsp").forward(request, response);
}
%>
</body>
</html>

如果是初次登陆,直接访问http://localhost:8080/Servlet/tomcat会自动找到我们项目下的index文件,解析JSP后这个session肯定是new的,因为它之前没有登陆过,然后就跳转到登陆页面。当它点击登陆提交后,就是我们LoginServlet要干的事了:

package servlet;

import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet{

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置post请求编码问题
        req.setCharacterEncoding("UTF-8");
        // 设置返回页面的格式和编码
        resp.setContentType("text/html; charset=UTF-8");
        HttpSession session = req.getSession();
        System.out.println(session.getId());

        String username = req.getParameter("username");
        String pwd = req.getParameter("pwd");
        // 校验用户名密码
        if(username.equals("毛毛") && pwd.equals("123456")) {
            System.out.println("登陆成功");
            req.setAttribute("username", username);
            
            // 第一次登陆,将cookie保存在浏览器
            Cookie[] cookies = req.getCookies();
            if(cookies != null) {
                for (Cookie cookie : cookies) {
                    if(URLDecoder.decode(cookie.getName(), "UTF-8").equals("JSESSIONID")) {
                        cookie.setMaxAge(60*60*1);
                        resp.addCookie(cookie);
                    }
                }
            }
            Cookie usernameC = new Cookie("username",URLEncoder.encode(username, "UTF-8"));
            Cookie pwdC = new Cookie("pwd",URLEncoder.encode(pwd, "UTF-8"));
            usernameC.setMaxAge(60*60*1);
            pwdC.setMaxAge(60*60*1);
            resp.addCookie(usernameC);
            resp.addCookie(pwdC);
            
            req.getRequestDispatcher("/welcome.jsp").forward(req, resp);
        }else {
            System.out.println("登陆失败,返回重新登陆!");
            req.getRequestDispatcher("/index.jsp").forward(req, resp);
        }
        
    }
    
}

登陆成功,将用户名,密码重写到cookie,并将JSESSIONID的过期时间更新,将请求转发到welcome.jsp,这个文件还是我们之前的那个,没有任何改动。登陆成功后页面会显示:Hello 毛毛

现在我们关闭浏览器,再次打开,再次访问http://localhost:8080/Servlet/,你会在页面上看到:Hello 毛毛。这段处理逻辑在index.jsp里,如果session不是新的,就说明刚登陆过,那获取cookie里的信息,直接显示出来即可。

 

 

URL重写

你可以在每一个URL末尾追加一个额外的标识session会话,服务器会把该session会话标识符与已存储的有关session会话的数据相关联【菜鸟教程】(补充:要现有这个session才行)

session一般是要和cookie一起工作的,如果浏览器不支持cookie怎么办?当我尝试了将chrome浏览器的cookie禁用后,在运行上面的实例,在浏览器端就读取不到任何的cookie了。

URL重写和直接使用cookie类似,使用cookie的方式是浏览器帮我们加的(当且仅当浏览器支持cookie),如果浏览器不知道cookie,我们可以手动加在URL上,格式如下:

"/ProjectName/Servlet;JSESSIONID=***;key1=value1?id=10"

也能达到和使用cookie一样的效果。

按照菜鸟教程的说法我们来测试一下:

一开始我使用的还是之前用过的getCookieServlet.java

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/GetCookieServlet")
public class getCookieServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        System.out.println(req.getRequestURL());
        PrintWriter out = resp.getWriter();
        System.out.println(req.getSession().getId());
            
        Cookie[] cookies = req.getCookies();
        if(cookies != null) {
            for (Cookie cookie : cookies) {
                out.println(URLDecoder.decode(cookie.getName(), "UTF-8") + ":" + URLDecoder.decode(cookie.getValue(), "UTF-8") + ",expire:" + cookie.getMaxAge());
            }
        }else {
            out.println("请先设置cookie");
        }
        
    }

}

启动项目,访问:

http://localhost:8080/Servlet/GetCookieServlet;JSESSIONID=4EF6EE406AA8EA47A6AC7C036AE68B76;

结果并不像菜鸟教程里说的那样,服务器并没有按照我传入的这个sessionid帮我关联一个session,而是创建了一个新的session。原因可想而知,就是我理解错误,菜鸟教程上面说的那句话,还有一点就是它关联的是一个已存在的session,首先我是第一次访问,服务器端肯定是没有我之前的任何session的,所以即使我传入了一个新的sessionid,它也关联不到某个seesion.id==sessionid的session,所以,我上面这样测试是不对的。

按照思路,是要现有一个session,然后我拿着这个session的id在URL地址里访问,才能被关联,那就简单了。我们先写一个index.jsp,让它帮我们生成一个sessionid

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="<%=response.encodeURL("welcome.jsp") %>" method="post">
用户名:<input type="text" name="username" />
<input type="submit" />
</form>
</body>
</html>

welcome.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello!<%=request.getParameter("username") %>
<a href="<%=response.encodeURL("index.jsp") %>" >重新登陆</a>
<a href="<%=response.encodeURL("logout.jsp") %>" >注销</a>
</body>
</html>

logout.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
session.invalidate();
%>
注销成功!
<a href="<%=response.encodeURL("index.jsp") %>" >返回登陆</a>
</body>
</html>

用这三个静态页面即可测试,首先在加载index.jsp的时候,隐式对象session已经存在,encodURL方法的作用是将session.id包含在url地址中,并进行编码,也就是说在index.jsp被生成时表单的action已经确定

 

在表单提交到welcome.jsp时,session.id=F4BD08A13DA7BE1B3E7B8B6C16238A84session已经存在,所以url地址栏中的sessionid还是F4BD0...,当在welcome.jsp中点击重新登陆或注销时还是在当前会话中,再次重新登陆(没注销)session没过期时还是当前这个session,如果session过期了会生成新的sessionid。点击注销时,就是将该session设置为过期,这和上一句是一样的,会生成新的sessionid,不在是当前会话了。

 

隐藏表单字段

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="GetCookieServlet" method="post">
<input type="hidden" value="<%=session.getId() %>>" />
用户名:<input type="text" name="username" />
<input type="submit" />
</form>
</body>
</html>

在第一次访问首页jsp文件时,就将生成的sessionid保存。但是我们不可能在每个页面里都写一个这个隐藏的input吧?如果请求资源是一个超文本链接,点击的时候,并不会导致表单的提交,所以使用隐藏表单字段你的形式并不支持常规的session会话跟踪。

 

 

 

Servlet 数据库访问

在普通的java类中使用mysql

好久没写过最原始的数据库链接和使用了,趁此回顾一下,使用mysql连接操作数据库,我只记得在普通的java类中写数据库连接基本是下面这样的:

1.引包,目前我还是在Servlet项目里写,所以我就用pom文件帮我下载最新的mysql驱动包了:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
</dependency>

2.写测试代码,我在main函数里写一个测测:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DBTest {
    public static final String url = "jdbc:mysql://localhost:3306/test";
    public static final String user = "root";
    public static final String password = "root";
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(url, user, password);
            statement = conn.createStatement();
            resultSet = statement.executeQuery("SELECT * FROM book");
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                int user_id = resultSet.getInt("user_id");
                String name = resultSet.getString("name");
                System.out.println("id:" + id + " user_id:" + user_id + " name:" + name);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                if(resultSet != null) {
                    resultSet.close();
                }
                if(statement != null) {
                    statement.close();
                }
                if(conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

运行,报了三个异常:

1.Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2.java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
    ...
    at DBTest.main(DBTest.java:17)
3.Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
    ...
    ... 6 more

第一个异常是说,使用”com.mysql.jdbc.Driver“加载驱动的方式已经被弃用了,新的加载驱动方式是”com.mysql.cj.jdbc.Driver“,虽然被弃用了,但是mysql目前依旧支持者,只是提倡你是要新方式。

第二个和第三个异常是说:mysql服务器时区有问题,要么你去配置一下mysql服务器的时区:

// 在mysql中执行命令
set global time_zone='+8:00'

或者是在JDBC驱动的url地址加上serverTimezone参数指明详细的时区,通常是serverTimezone=UTC。

 

好久没使用的mysql驱动了,mysql驱动也更新了,自己也随即更新了一下代码:

public static final String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";

Class.forName("com.mysql.cj.jdbc.Driver");

好了,接下来就是把上面的代码迁移到我们的Servlet就行。

 

web中使用mysql

新建DBServlet.java

package servlet;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/DBServlet")
public class DBServlet extends HttpServlet {
    public static final String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
    public static final String user = "root";
    public static final String password = "root";

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
                Connection conn = null;
                Statement statement = null;
                ResultSet resultSet = null;
                try {
                    Class.forName("com.mysql.cj.jdbc.Driver");
                    conn = DriverManager.getConnection(url, user, password);
                    statement = conn.createStatement();
                    resultSet = statement.executeQuery("SELECT * FROM book");
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        int user_id = resultSet.getInt("user_id");
                        String name = resultSet.getString("name");
                        System.out.println("id:" + id + " user_id:" + user_id + " name:" + name);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        if(resultSet != null) {
                            resultSet.close();
                        }
                        if(statement != null) {
                            statement.close();
                        }
                        if(conn != null) {
                            conn.close();
                        }
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    }
    
    

}

直接启动项目,访问,报错?

信息: 拦截的地址:http://localhost:8080/Servlet/DBServlet
java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1352)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1180)
    ...
    at java.lang.Thread.run(Thread.java:748)

说找不到数据库驱动???怎么回事?我的数据库驱动明明在啊,刚才通过普通的java类测试的时候没问题,怎么到web项目里就报找不到数据库驱动了?在网上找到答案,

普通的java项目,可以把数据库驱动放在项目里就能使用到。对于web项目,并不是依赖在项目里的数据库驱动,而是要把数据库驱动放在tomcat的lib目录下。

 

 

预编译、返回主键

try {
            statement = conn.prepareStatement("insert into comment(pid,title,comment) values(?,?,?)",PreparedStatement.RETURN_GENERATED_KEYS);
            statement.setInt(1, 1);
            statement.setString(2, title);
            statement.setString(3, comment);
            statement.executeUpdate();
            resultSet = statement.getGeneratedKeys();
            if(resultSet.next()) {
                id = resultSet.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

在创建Statement(或PreparedStatement)是加入Statement(或PreparedStatement).RETURN_GENERATED_KEY即可,获取结果集,在结果集中拿到主键。

 

 

Servlet 文件上传和下载

Servlet3.0之前上传

我本来还想使用最原始的方式通过流去读取request请求里面的文件,上传到本地呢,但是后来才发现,在Servlet3.0之前中根本不提供上传的功能,要想上传文件需要依赖第三方框架。

pom.xml:

<dependency>
    <groupId>com.liferay</groupId>
    <artifactId>org.apache.commons.fileupload</artifactId>
    <version>1.2.2.LIFERAY-PATCHED-1</version>
</dependency>

这个包依赖的commons.io.jar也会添加到项目里,你也可以去官网下,官网上也有使用说明。

 

Servlet3.0上传

Servlet3.0中,已经内置了上传的功能,我们主要以Servlet3.0做开发,在搞这个上传文件的时候路径是个问题,我们来看一下:

新建WebContent/upload.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="UploadServlet" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>

新建UploadServlet.java:

package servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet("/UploadServlet")
@MultipartConfig(location = "", maxFileSize = 1024 * 1024 * 20)
public class UploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
        PrintWriter out = null;
        Part part = null;
        String uploadpath = getServletConfig().getServletContext().getRealPath("/");
        System.out.println(uploadpath);

        try {
            out = resp.getWriter();
            part = req.getPart("file");
            if (part != null) {
                System.out.println(part.getName());
                part.write("123.md");
            }
            out.println("上传成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

启动项目,访问upload.html,提交一个文件测试,发现保存的路径不是在我上面打印的uploadpath里

// uploadpath
C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core	mp0wtpwebappsServlet

// 实际保存目录
C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core	mp0workCatalinalocalhostServlet

而且如果我在Multipart注解的location里写任何路径,比如:location="/upload",都会报错说找不到这个路径。找不到那简单啊,我获取这个实际保存路径的地址,然后新建一个upload不就行了,但是我怎么获取这个实际保存路径的地址?在一系列路径混乱的情况下,我写了一个专门打印路径的RoadServlet.java

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/RoadServlet")
public class RoadServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("<table border='1px'>");
        out.println("        <tr><td>req.getContextPath()</td><td>"+req.getContextPath()+"</td></tr>");
        out.println("        <tr><td>req.getRequestURI()</td><td>"+req.getRequestURI()+"</td></tr>");
        out.println("        <tr><td>req.getRequestURL()</td><td>"+req.getRequestURL()+"</td></tr>");
        out.println("        <tr><td>req.getServletPath()</td><td>"+req.getServletPath()+"</td></tr>");
        out.println("        <tr><td>req.getSession().getServletContext().getContextPath()</td><td>"+req.getSession().getServletContext().getContextPath()+"</td></tr>");
        out.println("        <tr><td>getServletContext().getContextPath()</td><td>"+getServletContext().getContextPath()+"</td></tr>");
        out.println("        <tr><td>getServletConfig().getServletContext().getContextPath()</td><td>"+getServletConfig().getServletContext().getContextPath()+"</td></tr>");
        out.println("        <tr><td>getServletConfig().getServletContext().getRealPath("/")</td><td>"+getServletConfig().getServletContext().getRealPath("/")+"</td></tr>");
        out.println("        <tr><td>getServletConfig().getServletContext().getRealPath("")</td><td>"+getServletConfig().getServletContext().getRealPath("")+"</td></tr>");
        out.println("        <tr><td>getServletConfig().getServletContext().getRealPath("/../../temp") </td><td>"+getServletConfig().getServletContext().getRealPath("/../../temp") +"</td></tr>");
        out.println("        <tr><td>getServletConfig().getServletContext().getRealPath("/../../temp") </td><td>"+ Thread.currentThread().getContextClassLoader().getResource("").getPath() +"</td></tr>");
        
        
        out.println("</table>");
    }
}

访问:

到此,我们先不在追究文件上传路径的问题,我们来看下一节【项目部署路径】,一个重要的知识点。

在阅读过项目部署路径和eclipse中的Server工程和tomcat的关系这两节后,我们可以继续我们的上传了。

改写代码如下:

package servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.apache.commons.lang3.StringUtils;

@WebServlet("/UploadServlet")
@MultipartConfig(maxFileSize = 1024 * 1024 * 20)
public class UploadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
        PrintWriter out = null;
        Part part = null;
        String uploadpath = getServletConfig().getServletContext().getRealPath("/WEB-INF/upload");
        try {
            out = resp.getWriter();
            part = req.getPart("file");
            if (part != null) {
                String fileName = getFileName(part);
                part.write(uploadpath + File.separator +  fileName);
            }
            out.println("上传成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
      * 如何得到上传的文件名, API没有提供直接的方法,只能从content-disposition属性中获取
      * 
      * @param part
      * @return
      */
     protected static String getFileName(Part part) {
      if (part == null)
       return null;

      String fileName = part.getHeader("content-disposition");
      if (StringUtils.isBlank(fileName)) {
       return null;
      }

      return StringUtils.substringBetween(fileName, "filename="", """);
     }

}

pom.xml引入:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

启动项目,访问upload.html,在这里我遇到了一个大坑。

 

大坑

那就是项目一直报StringUtils类ClassNotFoundException!我以为是因为我使用了源服务器部署导致了,但是我把他改成克隆服务器部署还是报这个错误,后来到网上找到了原因,我把它记录在【工具-eclipse笔记中了】,主要是因为我的项目所依赖的包,也要一起发布到项目里,也就是放到WEB-INF下的lib目录下,你可以手动去一个个拷贝,也可以到【eclipse笔记中看一下快捷添加方式】。

现在再次访问upload.html上传,你就会在你的***ServletWEB-INFupload目录下看到你上传的文件了(***取决于你使用的是哪种tomcat服务器)。

 

 

总结

到此我们大概也就知道了,使用注解方式不说明上传地址时,不管我们把部署路径配置在哪,默认时都是上传到克隆服务器的work目录中。但是如果我们不使用默认上传地址的方式时,我们可以使用下面方式来指定上传地址。

getServletConfig().getServletContext().getRealPath("/")

获取克隆服务器/源服务器中项目地址(到底是哪个,取决你eclipse中server的配置),不管使用哪个,迁移到源服务器是不影响的!

 

 

项目的部署路径

说到上面那一堆路径,我不得不说一下我们的项目部署路径。默认的我们在eclipse新建的web项目,如果使用eclipse集成tomcat,那当你在new一个tomcat Server后,打开Server会看到这么一张图:

 

在这里有一个Deploy path=wtpwebapps,这是eclipse集成tomcat后的默认部署位置

也就是在你的eclipse的工作目录下该项目的部署位置。但是,你细想我们之前没有使用eclipse集成tomcat时是怎么把项目部署到tomcat里的?之前是直接把项目的war包,放到tomcat的E:apache-tomcat-8.0.52webapps目录下,然后启动tomcat即可。最开始时我们是修改了E:apache-tomcat-8.0.52confserver.xml改成了下面这样:

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost">
        <Context path="" docBase="C:UsersadminDesktopServletBaseServletDemowebapp" reloadable="true"/>
      </Host>
    </Engine>
  </Service>
</Server>

上面这种都是在tomcat自身的部署目录里部署的,但是用eclipse集成tomcat的话默认就是在eclipse项目文件下部署项目。我们可以在新建tomcat Server时修改【只有新建时能修改,当添加项目进去后就不能修改了】,Deploy path为tomcat自身的部署目录即:

 

好了,现在我们更新了项目的部署路径,再次去访问RoadServlet,得到的图是下面这样的:

 

而且如果我们使用tomcat自身的部署环境而不是eclipse集成tomcat的部署环境,那么文件的上传和下载就简单多了!因为我们现在使用的是tomcat自身的部署环境,你在去C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core mp1下就再也找不到让人头疼的wtpwebapps文件夹了,而且你也不用在关心temp1下的work,webapps这些东西,因为你现在是在tomcat自身的部署环境了,这个环境很干净。

 

你可以很明确的知道你要上传到哪?还有就是我们再来测一下我们使用默认不配置@MultipartConfig中location时也不写其他路径,它会存在哪里?结果是:

C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core mp1workCatalinalocalhostServlet。你会意外,嗳?我们不是切换成tomcat自身的部署环境了吗?而且当你把eclipse集成的tomcat删除之后org.eclipse.wst.server.core下面就没有temp1文件夹了啊,但是为什么我们在新建一个部署在tomcat环境里的Server,org.eclipse.wst.server.core下又会生成temp1,而且之前的哪些文件除了wtpwebapps文件夹没有了,其他的依旧还在。想想你也应该明白,虽然我们在eclipse中切换了tomcat的部署环境,但是我们实际上使用的还是eclipse中集成的tomcat。我们配置eclipse中的Deploy path只是把项目部署在tomcat里,但是运行时依靠的tomcat还是eclipse中的tomcat,而不是E:apache-tomcat-8.0.52这个。说到这可能已经晕的不行了。重启一段来说明一下eclipse中的Server工程和tomcat的关系。【请阅读下一节:eclipse中的Server工程和tomcat的关系】

看过下一节后,我们就可以清楚的定义源服务器和克隆服务器了,上面说的就是在eclipse中使用的时克隆服务器,但是项目的部署地址放到了源服务器下,但是文件上传注解location参数不写时,它上传在了克隆服务器下org.eclipse.wst.server.core mp1workCatalinalocalhostServlet内。【这里的temp0和temp1请不要纠结,它们只是我之前创建的克隆服务器没删掉而已,不是重点】。

 

 

eclipse中的Server工程和tomcat的关系

源服务器和克隆服务器

最开始的时候,如果我们的eclipse中没有任何项目,也没new server时,在我们的C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core文件夹下是这样的:

 

但当我们在eclipse中创建一个web项目后,new server并添加一个server服务器即tomcat然后指定tomcat目录和JRE环境点击next-finish,此时我们只是创建了一个空的server,还没向里面添加任何web项目。现在我们运行一下这个空的server,看看org.eclipse.wst.server.core文件夹下会发生什么变化。

 

temp0

当启动server服务时,在

C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core

目录下创建了一个temp0,如果你仔细对比E:apache-tomcat-8.0.52文件夹下内容和temp0下内容你会发现

 

这两个目录下的内容基本时相似的。只是temp0更多了一个wptwebapps文件夹,而bin、lib只有E:apache-tomcat-8.0.52才有。

其实

C:UsersadminDesktopServletBase.metadata.pluginsorg.eclipse.wst.server.core	emp0

只是E:apache-tomcat-8.0.52目录的一个克隆,所以上面代码段所描述的这个目录也就具备了源服务器的功能。如果你在new几个server,就会在org.eclipse.wst.server.core目录下依次出现temp1,temp2,temp3等多个克隆服务器,但是这里每次只能启动上面一个克隆服务器,因为它们都是使用相同的启动端口。

 

 

优点

这样的机制就保证了你eclipse里的项目不会影响原先tomcat里的配置,每次都用不同的参数来启动tomcat。这样会有一个问题,就是如果你原先的tomcat配置文件有错的话,eclipse会先拷贝你原有的tomcat下的配置,然后在这个配置的基础上修改。所以,遇到这种问题,先保证原有的配置没问题,然后再去修改eclipse新生成的,或者直接删除重配。

 

前进时,请别遗忘了身后的脚印。
原文地址:https://www.cnblogs.com/liudaihuablogs/p/13462392.html