Servlet

一:概念  运行在服务端的小程序

二:创建步骤

1.创建javaEE项目

2.定义一个类,实现Servlet接口

3.实现接口中的抽象方法

在server方法实现输出:

  

4.在web.xml中配置Servlet

    <!--配置servlet-->
    <servlet>
        <servlet-name>demo1</servlet-name>
        <servlet-class>web.servlet.servletDemo1</servlet-class>  <!--路径-->
    </servlet>
    <!--映射-->
    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>

 执行原理:

当服务器接收到浏览器的请求后,会解析其URL(http://localhost:8080/demo1),根据localhost找到主机,8080找到tomcat软件,再通过demo1找到xml文件的url-pattern找到是否含有demo1的标签;如果有那么在mapping映射中与servlet中观察值是否相同,再根据servlet-class文件的类加载路径,tomcat会实现反射(Class.forName加载字节码文件,newInstance方法创建其对象,调用其方法(servlet))

三:servlet的生命周期:

 

init方法只能执行一次,说明一个Servlet在内存中只存在一个对象,说明Servlet是单例的

可能出现的问题:多个用户同时访问时,可能存在线程安全问题(如对同一个属性修改)

解决:尽量不要在Servlet中定义成员变量;即使定义了成员变量,也不要对其修改值(最好只能获取值)

 servlet何时被创建?

 

 destory方法:只有服务器正常关闭时,才会执行destory方法;先执行destory方法,servlet才能被销毁。一般用于销毁资源

 四:注解配置(不用在xml文件中配置):

 

五:servlet体系结构

 servlet接口

       |

GenericServlet抽象类

       |

HttpServlet抽象类:重写doGet或doPost方法

 

 Http请求:

请求头作用:浏览器告诉服务器端的信息

请求体:封装post请求参数的

常见的请求头:

                        User-Agent:     浏览器告诉服务器,访问服务器使用的浏览器版本信息;服务器可以获取该头信息,解决浏览器的兼容性问题

                         Referer(Referer:http://localhost:8080/test.html):    告诉服务器请求来源     好处:防盗链、统计工作

 六:Request和Response对象

原理:对象由服务器创建,程序负责使用;request获取请求,response设置响应。

Request:

功能:1.获取请求行数据   1.1获取虚拟目录(getContextPath( ) )   1.2获取请求url(getRequestURL( )    StringBuffer getRequestURL( ) )

2.获取请求头数据:

3.获取请求体数据(post方法提交的方式才有请求体,在请求体中封装了请求参数   BufferReader getReader,在通过readLine方法读取)

 

 4.转发:服务器内部资源跳转方式

 

转发的资源共享:

 

 

 5.获取参数通用方式   String getParameter(String name):根据参数名称获取参数值

            Enumeration<String>  getParameters(String name):获取得到的数组(复选)

            String getParameterNames():所有请求的参数名称

           String[ ] getParameterValues():参数值数组

            Map<String,String[ ]> getParameterMap():获取所有参数的map集合

 

 

 其他方法:

 

 post方式中文乱码的解决:设置参数前,设置request编码:request.setCharacterEncoding("utf-8");

 登录案例:

难点一:将数据库中的数据取出来转化为User对象

测试用例:

 

 注:配置文件properties要放在src下,否则会报空指针异常,加载错误;如果User的设置username和password与数据库中的不一致,会报EmptyResultDataAccessExpection异常,所以要对查询中的方法内容进行try、catch包裹,如果账号密码错误,返回null.

错误:

 难点二:500错误  Servlet execution threw an exception

 

 

 解决:lib目录的放置问题

难点三:如何取出数据库中的数据 与 提交到服务器的对比?

 

 

 

 

 

 难点四:输出页面中文问号问题

 

解决方案:在成功和失败的servlet设置编码     response.setContentType("text/html;charset=utf-8");

 

 

 

 dao层:

package UserDao;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcTemplateDemo {
    static JdbcTemplate template =new JdbcTemplate(DruidUtils.getDs());;
    /**
     * @param loginUser 用户名和密码
     * @return 包含的全部数据
     */
    public User queryDemo(User loginUser){
        try {
            String sql = "select * from user where username=? and password=?";
            User user = template.queryForObject(sql,
                    new BeanPropertyRowMapper<User>(User.class),
                    loginUser.getUsername(), loginUser.getPassword());
            return user;
        }catch (Exception e){
            return null;
        }
    }
//    public void test(){
//        String sql = "select * from user ";
//        User user = template.queryForObject(sql,
//                new BeanPropertyRowMapper<User>(User.class));
//        System.out.println(user);
//    }
}

servlet层:

package LoginServlet;

import UserDao.JdbcTemplateDemo;
import UserDao.User;
import org.springframework.jdbc.core.JdbcTemplate;

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 java.io.IOException;

@WebServlet("/LoginDemo/Servlet")
public class Servlet 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");

        //封装User对象
        User user=new User();
        user.setUsername(username);
        user.setPassword(password);

        //从数据库中获取的User对象与html输入的账号密码比较;不一样会返回null
        JdbcTemplateDemo jdbc=new JdbcTemplateDemo();
        User user1 = jdbc.queryDemo(user);

        //输出的与数据库的比较
        if(user1==null){
            //登陆失败
            request.getRequestDispatcher("/LoginDemo/FailServlet").forward(request,response);
        }else {
            //存储数据
            request.setAttribute("user1",user1);
            request.getRequestDispatcher("/LoginDemo/SuccessServlet").forward(request,response);
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}

 

 

 

 

Response:

Http内容{ 状态码:  100-200  服务器接收客户端消息,但没有接收完成,等待一段时间后,会发送1xx  

200-300:成功 

300-400:重定向302  304缓存  

400-500:客户端错误404,请求路径没有对应的资源    405表示请方式没有对应的方法(doxxx)   

5xx : 服务端错误 服务器内部异常500;

Content-Type: 服务器告诉客户端啊本次相应体数据格式以及编码格式
Response对象:设置响应消息(行、头、体)
体:字符输出流:    PrintWriter getWriter( )
       字节输出流: ServletOutputStream getOutputStream( )

}
1.应用:重定向(服务器告诉客户端进行重定向,状态码:302;告诉客户端另一资源的路径,响应头Location;)

 

 sendRedirect方法更简便,也可以重定向到其他网站

 

 

 开发者工具分析:

2.转发重定向区别(forward、 redirect区别):

转发:1.地址栏路径不变   2.只能访问当前服务器下的资源   3.转发只是一次请求,可以使用request对象来共享数据(request.getRequestDispatcher("/ ").forward(request,response))

重定向:1.地址栏路径变化   2.可以访问其他站点(服务器)的资源   3.两次请求,不能使用requesst对象来共享数据(response.sendRedirect("/ "))


3.路径

相对路径:根据绝对路径可以确定唯一资源

./:当前目录

../:后退一级目录

绝对路径:根据绝对路径可以确定唯一资源

客户端:需要加虚拟目录( 建议通过getContextPath()方法动态获取虚拟目录 )

 

服务端:不需要加虚拟目录(转发路径)

4.服务器输出流到客户端浏览器:获取字符输出流,输出数据一节中文乱码问题

 5.验证码效果:

 

 

七:ServletContext对象:代表web对象,和程序的服务器通信

功能:获取MIME类型   域数据共享 (所有用户的,谨慎使用)   得到真实路径:getRealPath( )(注:getClassLoader类加载器只能获取src下的类,有局限..)

 八.会话 cookie session

一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。

功能:在依次会话的范围内多次请求间,共享数据

客户端会话技术:cookie   服务器端会话技术:session

1.cookie:将数据保存在客户端

可以修改默认的Servlet类的模板:File-Settings-code tem-Other-Web-Servlet Annotated class

 

 cookie 500异常解决:

 

报错信息An invalid character [44] was present in the Cookie value,ascii为44的字符是“,”,说明cookie不支持“,”,可以换成“#” 

用例:基本用法

 

 原理:响应头 set-cookie 和请求头cookie

cookie存活时间:默认情况,浏览器关闭,cookie被销毁(负数)

setMaxAge(int seconds)方法:

                  0:删除cookie信息

cookie存储中文:Tomact 8之后,支持中文数据;注:特殊字符不支持,建议使用URL编码存储,URL解码解析

共享范围:在同一个tomcat中,部署多个项目中,默认情况cookie不能共享;如果要共享,可以设置path为setPath("/ ");

 

注:不同的tomcat服务器之间cookie共享问题?

setDomain(String path):如果设置一级域名相同,可以实现多个服务器之间共享。如:setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com可以共享。

cookie特点:1.存储在客户端浏览器   2.对单个cookie大小有限制(4kb)以及对同一个域名下的总cookie数量也有限制(20个)

作用:1.存储少量不太敏感的数据;2. 在不登录的情况下,完成服务器对客户端的身份识别(如:不登录情况下对百度的页面背景设置,关闭后进入依然是之前设置的背景)

某网站cookie:

 

 案例:记住当前访问时间

 输出台的中文乱码问题:

 

设置编码格式:

 

 

将当前时间存入cookie进行编码解码,对cookie特殊字符进行的URL编码转码:

 

package Cookie;
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 java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 判断记录登录次数;
 * 第一次登录:您好,欢迎您首次登录  不是第一次,欢迎回来,本次访问时间
 */
@WebServlet("/CookieTest")
public class CookieTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");//设置响应给客户端的编码为中文编码
        Cookie[] cookies = request.getCookies();
        boolean flag=false;
        if(cookies!=null && cookies.length>0){
            for(Cookie cookie:cookies){
                String name = cookie.getName();
                if("lastname".equals(name)){
                    flag=true;
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
                    String s = sdf.format(date);
                    System.out.println("编码前:"+s);
                    //URL编码
                    s=URLEncoder.encode(s,"utf-8");
                    System.out.println("编码后:"+s);
                    cookie.setValue(s);
                    //设置cookie存活时间
                    cookie.setMaxAge(300);
                    response.addCookie(cookie);
                    String value = cookie.getValue();
                    //URL解码
                    System.out.println("解码前:"+value);
                    value=URLDecoder.decode(value,"utf-8");
                    System.out.println("解码后: "+value);
                    response.getWriter().write("欢迎回来,本次访问时间:"+value);
                    break;
                }
            }
        }
        if(cookies==null ||cookies.length==0|| flag==false){
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日 hh:mm:ss");
            String s = sdf.format(date);
            System.out.println("编码前:"+s);
            //URL编码
            s=URLEncoder.encode(s,"utf-8");
            System.out.println("编码后:"+s);
            //第一次访问
            response.getWriter().write("您好,欢迎您首次访问");
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

 2.Session:服务器端会话技术,在一次会话的多次请求间共享数据

原理:session的实现依赖于cookie。

 当客户端关闭后,服务器不关闭,两次获取的session是否为同一个?  默认情况不是;如果需要相同,创建cookie,cookie的键为JESSIONID,设置最大存活时间,让cookie永久保存。

 

 服务器关闭后,session会被销毁,所以session不会是同一个,但是要确保数据不丢失(需要在tomcat服务器部署)。

session钝化:在session正常关闭前,将session对象系列化到硬盘上。

session活化:在服务器启动后,将session文件转化为内存中的session对象。

失效时间:

1.服务器关闭   2. session对象调用invalidate()  3.session默认失效时间:30分钟(web.xml的session-config标签的session-timeout)

特点:

1.用于存储依次会话的多次请求的数据,存储在服务器端  2.session可以存储任意类型,大小的数据

区别

1.session存储在服务器端,cookie在客户端

2.session没有数据大小限制,cookie有

3.session数据安全,cookie相对不安全

session和jsp综合案例:登录验证码

1.访问带有验证码的登录页面login.jsp

2.用户输入用户名,密码及验证码

 

 

package Servlet;
import UserDao.JdbcTemplateDemo;
import UserDao.User;

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;
import java.io.IOException;
import java.util.Map;

@WebServlet("/Login2/loginServlet")
public class loginServlet 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");
        //比较提交的验证码和Session里面的验证码
        String checkCode = request.getParameter("checkCode");
        HttpSession session = request.getSession();
        String checkCode_session = (String)session.getAttribute("checkCode_session");
        session.removeAttribute("checkCode_session");
        //提交的账号密码
        User user=new User();
        user.setUsername(username);
        user.setPassword(password);
        //从数据库中获取的User对象与html输入的账号密码比较;不一样会返回null
        JdbcTemplateDemo jdbc=new JdbcTemplateDemo();
        User user1 = jdbc.queryDemo(user);
        //判断验证码,忽略大小写
        if(checkCode_session!=null && checkCode_session.equalsIgnoreCase(checkCode) ){
            //验证码正确判断用户名密码,查询数据库
            if(user1==null){//登录失败,转发至新页面
                request.setAttribute("login_error","用户名或密码错误");
                request.getRequestDispatcher("/login2.jsp").forward(request,response);
            }else {
                //存储用户信息
                session.setAttribute("user1",username);
                response.sendRedirect(request.getContextPath()+"/success.jsp");
            }
        }else {
            request.setAttribute("c_error","验证码错误");
            request.getRequestDispatcher("/login2.jsp").forward(request,response);
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

 

 

 

 success.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功页面</title>
</head>
<body>
<h1><%=request.getSession().getAttribute("user1")%>,欢迎您</h1>
</body>
</html>

 

login2.jsp:(暂时没有对每一项的验证)

 

 

<%--
  Created by IntelliJ IDEA.
  User: laurarararararara
  Date: 2020/2/18
  Time: 20:30
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>注册主页面</title>
    <style>
        div{
            color: red;
        }
        /*让字距离近一点*/
        *{
            margin: 0px;
            padding: 0px;
            box-sizing: border-box;
        }
        body{
            background: url("23.jpg") no-repeat center;
        }
        .layout{
            width: 900px;
            height: 500px;
            border: 5px solid beige;
            background: white ;
            /*让div水平居中*/
            margin: auto;
            padding: 40px;
            margin-top:50px ;
        }
        .leftarea{
            float: left;
            margin: 3px;
        }
        .centerarea{
            float: left;
            margin-left: 5px;
            margin-top: 20px;
        }
        a:link{
            color: pink;
        }
        .duiqi{
            width: 100px;
            text-align: right;
            height: 40px;
        }
        .rightgap{
            padding-left: 50px ;
        }
        #user,#password,#birthday,#email,#age,#time{
            width: 250px;
            height: 30px;
            border: 1px solid #A6A6A6;
            /*设置表框圆角*/
            border-radius: 5px;
            padding-left: 10px;
        }
        #sub{
            width: 150px;
            height: 40px;
            background-color: gold;
            border: 1px solid #FFd026;
        }
        #code{
            width: 125px;
            height: 30px;
            border: 1px solid #A6A6A6;
            /*设置表框圆角*/
            border-radius: 5px;
            padding-left: 10px;
        }
        #checkCode{
            width: 120px;
            height: 30px;
            vertical-align: middle;
        }
    </style>
</head>
<body>
<form action="/Login2/loginServlet" method="post">
<div class="layout">
    <div class="leftarea">
        <p><font color="#ffd026" size=5% >新用户注册</font></p>
        <p><font color="#a6a6a6" size=5%>USER REGISTER</font></p>
    </div>
    <div class="centerarea">
        <div class="html">
            <table>
                <tr>
                    <td class="duiqi"><label for="user" >用户名</label></td>
                    <td class="rightgap"><input name="username" placeholder="请输入用户名" id="user"></td>
                </tr>
                <tr>
                    <td class="duiqi">密码</td>
                    <td class="rightgap"><input type="password" name="password" placeholder="请输入密码" id="password"></td>
                </tr>
                <tr>
                    <td class="duiqi">性别</td>
                    <td class="rightgap"><input type="radio" name="gender" value="male"><input type="radio" name="gender" value="female" checked> 女</td>
                </tr>
                <tr>
                    <td class="duiqi">生日</td>
                    <td class="rightgap"><input type="date" name="birthday" id="birthday"></td>
                <tr>
                    <td class="duiqi">年龄</td>
                    <td class="rightgap"><input type="number" name="age" id="age"></td></tr>
                <tr>
                    <td class="duiqi">当前时间</td>
                    <td class="rightgap"><input type="datetime-local" name="nowtime" id="time"></td></tr>
                <tr>
                    <td class="duiqi">邮箱</td>
                    <td class="rightgap"><input type="email" name="email" id="email"></td>
                </tr>
                <tr>
                    <td class="duiqi">验证码</td>
                    <td class="rightgap" ><input  type="text" name="checkCode" id="code">
                        <img src="/Login2/CheckCodeServlet" id="checkCode"> </td>
                </tr>
                <tr>
                    <td colspan="2" align="center">
                        <input type="submit"  id="sub" value="登录"></td>
                </tr>
            </table>
        </div>
    </div>
</div>
</form>
<div><%=request.getAttribute("c_error") == null ?"":request.getAttribute("c_error")%></div>
<div><%=request.getAttribute("login_error") == null ?"":request.getAttribute("login_error")%></div>
</body>
</html>

 九:过滤器

拦截方式的配置:dispatcherTypes属性  REQUEST(默认)、FORWARD

过滤器链:

 

 

执行顺序:按照类名的字符串比较规则,值小的先执行(注解配置)  filter-mapping属性哪个在上面哪个先执行(xml配置)

 

 案例:登录验证

package web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter("/*")
public class LoginFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request=(HttpServletRequest)req;//强转
        String uri = request.getRequestURI();//获取资源的请求路径
        if(uri.contains("/login.jsp")||uri.contains("/loginServlet")||uri.contains("/CheckCodeServlet")){
            chain.doFilter(req,resp);
        }else {
            Object users = request.getSession().getAttribute("users");
            if(users!=null){
                chain.doFilter(req,resp);
            }else {
                request.setAttribute("login_message","您尚未登录,请登录");
                request.getRequestDispatcher("/login.jsp").forward(req,resp);
            }
        }
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {
    }
}
Filter

过滤器的敏感词汇过滤功能:需要增强request对象的getParameter方法;放行,引入新的request对象

增强对象的功能:设计模式

1.装饰模式

2.代理模式:动态代理、静态代理(区别:静态在一个类文件描述代理模式;动态在内存中形成代理类)

真实对象:被代理的对象                    代理模式:代理对象代理真实对象,达到增强真实对象功能的目的

/*
用户方
2020/2/26
 */
public class ProxyTest {
    public static void main(String[] args) {
        RealObject object = new RealObject();
        //object.saleCom(8000);
        /**
         * 类加载器:真实对象.getClass().getClassLoader()
         * 接口数组:真实对象.getClass().getInterfaces()
         * 处理器:new InvocationHandler()
         */
        saleComputer proxy_real = (saleComputer) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
            /*
            代理逻辑编写的方法,代理对象所有的方法都会触发该方法执行
                 代理对象: proxy
                 method:代理对象调用的方法封装为的对象
                 args:代理对象调用方法时传递的实际参数:8000
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("invoke方法执行");
                System.out.println(method.getName());//方法的名称
                System.out.println(args[0]);
                return null;
            }
        });
        String s = proxy_real.saleCom(8000);
        System.out.println(s);
        // proxy_real.show();
    }
}
ProxyTest

 

 具体实例:增强

 过滤器实现敏感词汇功能:

 

 

 

 

package Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
//敏感词汇过滤器
@WebFilter("/TestServlet")
public class StrengthenFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强getParameter方法
                if (method.getName().equals("getParameter")) {
                    String value = (String) method.invoke(req, args);
                    if(value!=null){
                        for(String str:list){
                            if(value.contains(str)){
                                value=value.replaceAll(str,"xxx");
                            }
                        }
                    }
                    return value;
                }
                return method.invoke(req,args);
            }
        });
        chain.doFilter(proxy_req, resp);
    }
    private List<String> list=new ArrayList();//包含敏感词汇的list集合
    public void init(FilterConfig config) throws ServletException {
        //加载文件;读取文件,添加到list集合中
        try {
            String realPath = config.getServletContext().getRealPath("/WEB-INF/classes/敏感词汇.txt");//获取真实路径
            BufferedReader bufferedReader = new BufferedReader(new FileReader(realPath));//读取文件
            //添加
            String line=null;
            while ((line=bufferedReader.readLine())!=null){
                list.add(line);
            }
            bufferedReader.close();//关闭流
            System.out.println(list.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
StrengthenFilter

 

posted @ 2020-02-10 18:54  acehm  阅读(193)  评论(0编辑  收藏  举报