黑马程序员技术交流社区

标题: web_java项目的知识点 [打印本页]

作者: 腾云科技    时间: 2018-7-13 14:27
标题: web_java项目的知识点
本帖最后由 腾云科技 于 2018-7-13 17:44 编辑

1.多个请求共用一个Servlet
    我们在对JavaWEB工程进行开发的时候,我们经常会遇到这样一个问题,在jsp中发送到Servlet的每一个请求都要写一个对应的Servlet,这样会造成一个工程完成下来需要写几十个Servlet,那么怎么可以做到多个请求共用一个Servlet呢?

一、利用反射原理(根据获取到的url-pattern,截取出相应的方法名,进而调用相应的方法)
    1.配置web.xml文件,<url-pattern>中设置以 *.扩展名 的方式进行匹配(下面的例子以 *.do 匹配,调用的Servlet为 StaffServlet,对Staff表的查询query 和删除delete 请求进行处理)
      
[XML] 纯文本查看 复制代码
 <servlet>
            <servlet-name>StaffServlet</servlet-name>
            <servlet-class>cn.edu.lingnan.servlet.StaffServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>StaffServlet</servlet-name>
            <url-pattern>*.do</url-pattern>
          </servlet-mapping>
        所有以 .do 结尾的请求都会调用StaffServlet


    2.编写Servlet(例子:StaffServlet)
      
[Java] 纯文本查看 复制代码
 public class StaffServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                doPost(request, response);
            }
            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //1.获取能够与"url-pattern"中匹配的路径
                String method = request.getServletPath();
                //(此时处理的请求是查询 query.do)
                System.out.println("request.getServletPath()获取的值为: " + method);//输出 /query.do
                //2.通过字符串截取,把方法名 query 截取出来
                method = method.substring(1, method.length()-3);
                System.out.println("截取后的值为: "+ method);
                Method m = null;
                try {
                    //3.获取当前类中名字为 method 的方法
                    m = this.getClass().getDeclaredMethod(method,HttpServletRequest.class, HttpServletResponse.class);
                    //4.调用  method 方法
                    m.invoke(this, request, response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            private void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                System.out.println("query方法被调用");
            }
            private void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                System.out.println("delete方法被调用");
            }
        }


    3. jsp 中发送请求调用servlet
      
[HTML] 纯文本查看 复制代码
  <%@ 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>
            <a href="query.do">这是一个查询请求,调用Servlet中的query方法</a>
            <br>
            <br>
            <a href="delete.do">这是一个删除请求,调用Servlet中的delete方法</a>
        </body>
        </html>


    注意:url-pattern 中的扩展名可以是任意字符,不一定要是 .do , 例如也可以是 .doStaff , 但是改了扩展名了要注意修改截取的字符串的位置,如修改由.do 修改为 .doStaff 时,doPost中

        method = method.substring(1, method.length()-3);需修改为(.do 为三个字符)
   
        method = method.substring(1, method.length()-8);(.doStaff 为八个字符)
   
          jsp 中调用时,则也相应修改 query.do ---> query.doStaff


二、利用URL中的参数传送需要调用的方法名
    1.配置web.xml, 映射Servlet路径(以下以 StudentServlet 调用 Servlet 中的 query 和 delete 方法为例子)
        
[HTML] 纯文本查看 复制代码
<servlet>
            <servlet-name>StudentServlet</servlet-name>
            <servlet-class>servlet.StudentServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>StudentServlet</servlet-name>
            <url-pattern>/studentServlet</url-pattern>
        </servlet-mapping>

    2.编写Servlet(StudentServlet)
        
[Java] 纯文本查看 复制代码
public class StudentServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                this.doPost(request, response);
            }
            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //获取请求参数 method
                String method = request.getParameter("method");
                System.out.println("获取到的method参数为: " + method);
                //调用 method 方法
                if(method.equals("query")) {
                    this.query(request, response);
                }else {
                    if(method.equals("delete")) {
                        this.delete(request, response);
                    }
                }
            }
            private void query(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException  {
                System.out.println("query 方法被调用");
            }
            private void delete(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException  {
                System.out.println("delete 方法被调用");
            }
        }


    3. jsp中发送请求调用Servlet
      
[HTML] 纯文本查看 复制代码
 <%@ 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>
            <a href="studentServlet?method=query">这是一个查询请求,调用Servlet中的query方法</a>
            <br>
            <br>
            <a href="delete.do?method=delete">这是一个删除请求,调用Servlet中的delete方法</a>
        </body>
        </html>


2.自动登录的案例:
     步骤:
        1.创建数据库和表
        2.导入jar包和工具类
        3.创建包结构及常用类
        4.登录功能
        5.利用Cookie记住用户名和密码
        6.实现自动登录的过滤器
    代码书写:
        1.index.jsp中判断能否获取到用户信息
            
[HTML] 纯文本查看 复制代码
<ol class="list-inline">
                <c:if test="${ empty user }">
                    <li><a href="${pageContext.request.contextPath}/login/login.jsp">登录</a></li>
                    <li><a href="register.htm">注册</a></li>
                </c:if>
                <c:if test="${ not empty user }">
                    <li>您好:${ user.nickname }</li>
                    <li><a href="#">退出</a></li>
                </c:if>
                <li><a href="cart.htm">购物车</a></li>
            </ol>

        2.login.jsp中自动登录按钮添加name和value属性
            <input type="checkbox" name="autoLogin" value="true"> 自动登录
        
        3.servlet中返回的user实体类是否为空,如果登陆成功,将用户名和密码保存在cookie中,并设置保存时间
           
[Java] 纯文本查看 复制代码
 // 跳转页面:
            if(existUser == null){
                // 登录失败:
                req.setAttribute("msg", "用户名或密码错误!");
                req.getRequestDispatcher("/demo5/login.jsp").forward(req, resp);
            }else{
                // 登录成功:
                // 记住用户名和密码:
                String autoLogin = req.getParameter("autoLogin");
                if("true".equals(autoLogin)){
                    // 自动登录的复选框已经勾选.
                    Cookie cookie = new Cookie("autoLogin",existUser.getUsername()+"#"+existUser.getPassword());
                    cookie.setPath("/day16");
                    cookie.setMaxAge(60 * 60 * 24 * 7);
                    resp.addCookie(cookie);
                }
                // 使用Session记录用户信息.
                req.getSession().setAttribute("existUser", existUser);
                resp.sendRedirect(req.getContextPath()+"/demo5/index.jsp");
            }

        4.过滤器Filter中的代码编写:
            
[Java] 纯文本查看 复制代码
public class AutoLoginFilter implements Filter{
                @Override
                public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                        throws IOException, ServletException {
                    /**
                     * 判断session中是否有用户的信息:
                     * * session中如果有:放行.
                     * * session中没有:
                     *    * 从Cookie中获取:
                     *        * Cookie中没有:放行.
                     *        * Cookie中有:
                     *            * 获取Cookie中存的用户名和密码到数据库查询.
                     *                * 没有查询到:放行.
                     *                * 查询到:将用户信息存入到session . 放行.
                     * /
                    // 判断session中是否有用户的信息:
                    HttpServletRequest req = (HttpServletRequest) request;
                    HttpSession session = req.getSession();
                    User existUser = (User) session.getAttribute("existUser");
                    if(existUser != null){
                        // session中有用户信息.
                        chain.doFilter(req, response);
                    }else{
                        // session中没有用户信息.
                        // 获得Cookie的数据:
                        Cookie[] cookies = req.getCookies();
                        Cookie cookie = CookieUtils.findCookie(cookies, "autoLogin");
                        // 判断Cookie中有没有信息:
                        if(cookie == null){
                            // 没有携带Cookie的信息过来:
                            chain.doFilter(req, response);
                        }else{
                            // 带着Cookie信息过来.
                            String value = cookie.getValue();// aaa#111
                            // 获得用户名和密码:
                            String username = value.split("#")[0];
                            String password = value.split("#")[1];
                            // 去数据库进行查询:
                            User user = new User();
                            user.setUsername(username);
                            user.setPassword(password);
                            UserService userService = new UserService();
                            try {
                                existUser = userService.login(user);
                                if(existUser == null){
                                    // 用户名或密码错误:Cookie被篡改的.
                                    chain.doFilter(req, response);
                                }else{
                                    // 将用户存到session中,放行
                                    session.setAttribute("existUser", existUser);
                                    chain.doFilter(req, response);
                                }
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                //doFilter的简化写法:
               
[AppleScript] 纯文本查看 复制代码
HttpServletRequest req = (HttpServletRequest) request;
                HttpSession session = req.getSession();
                User existUser = (User) session.getAttribute("existUser");
                if(existUser==null){
                    Cookie[] cookies = req.getCookies();
                    Cookie cookie = CookieUtils.findCookie(cookies, "autoLogin");
                    if(cookie!=null){
                        String value = cookie.getValue();// aaa#111
                        // 获得用户名和密码:
                        String username = value.split("#")[0];
                        String password = value.split("#")[1];
                        // 去数据库进行查询:
                        User user = new User();
                        user.setUsername(username);
                        user.setPassword(password);
                        UserService userService = new UserService();
                        try {
                            existUser = userService.login(user);
                            if(existUser!=null){
                                session.setAttribute("existUser", existUser);
                            }
                           
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                chain.doFilter(req, response);

                    
        5.web.xml中的配置文件:
           
[XML] 纯文本查看 复制代码
 <filter>
                <filter-name>autoLoginFilter</filter-name>
                <filter-class>com.itheima_Filter.autoLoginFilter</filter-class>
            </filter>
            <filter-mapping>
                <filter-name>autoLoginFilter</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>


3.通用的字符集编码的过滤器案例:
    网站上通常会提交带有中文的数据,GET/POST请求都有可能提交中文数据.
    通常情况下在Servlet中处理中文乱码.
   
    只需要在Servlet中调用request.getParameter();接收参数就可以,而不去关心到底get/post如何处理乱码.

    增强request中的getParameter方法:
        继承:控制这个类构造.
        装饰者模式:增强的类和被增强类实现相同的接口,增强的类中获得到被增强的类的引用.
            缺点:接口中方法太多.
        动态代理:被增强的类实现接口就可以.

    装饰者模式对request中的getParameter方法增强:
        1.HttpServletRequest是接口;
        2.HttpServletRequestWrapper是HttpServletRequest的模板实现类,里边的方法全部调用父类的方法
            用来让别的类直接继承,增强其中的某一个方法,减少了代码的书写
        3.新建一个MyHttpServletRequestWrapper继承HttpServletRequestWrapper
        4.新建的My...类,构造方法传入HttpServletRequest request;
            private HttpServletRequest request;
            public MyHttpServletRequestWrapper(HttpServletRequest request) {
                super(request);
                this.request = request;
            }
        5.将HttpServletRequest传入的request赋值给My...的私有成员变量HttpServletRequest request;
        6.用被赋值的request重写getParameter(String name);方法即可对这个方法进行增强
        7.在filter中创建MyHttpServletRequestWrapper对象,此时的request已经是被增强过的了
        8.将被增强过得request通过chain.doFilter方法传给Servlet即可
   
    装饰着模式的层次理解:
                被增强的类及对象:ServletRequest request
            __________|____________________
            |                                             |        
        实现类HttpServletRequest        模板实现类HttpServletRequestWrapper
        对象:req                                      |
                                                           |   
                                    --创建一个对模板类的的继承类,这样就可以修改那个方法就重写哪个方法
                                        减少代码的书写
                                                          |
                                                          |
                                    MyHttpServletRequestWrapper   
                                    成员变量:private HttpServletRequest request;
                                    --有参构造需要将HttpServletRequest的对象req出入
                                        在构造方法中将req赋值给成员变量request
                                            public MyHttpServletRequestWrapper(HttpServletRequest req) {
                                                super(req);
                                                this.request = request;
                                            }
                                    --在类的内部可以使用request对方法进行增强
                                                         |
                                                         |
                                    过滤器filter中创建MyHttp...Wrapper的对象MyReq,将其传入到放行方法中            
                                        MyHttp...Wrapper MyReq = new MyHttp...Wrapper(Http...request req);            
                                        chain.doFilter(myReq, response);
   
    代码实现:
      
[Java] 纯文本查看 复制代码
 //在Filter中的doFilter方法代码书写
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            MyHttpServletRequestWrapper myReq = new MyHttpServletRequestWrapper(req);
            chain.doFilter(myReq, response);
        }
      
        //装饰者模式增强request的getParameter方法
        public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper{
            private HttpServletRequest request;
            public MyHttpServletRequestWrapper(HttpServletRequest request) {
                super(request);
                this.request = request;
            }
            @Override
            public String getParameter(String name) {
                // 根据请求方式不同,去处理:
                // 获得请求方式:
                String method = request.getMethod();
                if("get".equalsIgnoreCase(method)){
                    String value = null;
                    try {
                        value = new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    return value;
                }else if("post".equalsIgnoreCase(method)){
                    try {
                        request.setCharacterEncoding("UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
                return super.getParameter(name);
            }
        }



4.动态代理:
使用动态代理完成字符集编码过滤器的编写:
    request中的方法的增强
        继承        :能够控制这个类的构造.
        装饰者    :增强的类和被增强的类实现相同的接口,增强的类中获得到被增强的类的引用.
            接口中方法过多,只增强其中的某个方法.其他的方法也需要重写.
        动态代理    :被增强的类实现了接口.
   
    代理的概述   
        代理:
        JDK中动态代理:Proxy对象.
            Proxy.newProxyInstance(ClassLoader cl,Class[] interfaces,InvocationHandler ih);
   
    动态代理入门案例:
      
[Java] 纯文本查看 复制代码
 public class ProxyDemo1 {
            @Test
            public void demo1(){
                Waiter waiter = new Waitress();
                // waiter.server();
               
                // 使用动态代理:Proxy.newProxyInstance();
                /**
                 * ClassLoader :类加载器.
                 * Class[]     :被增强的对象实现的所有接口
                 * InvocationHandler    :处理类.
                 * /
                // 第一个参数:
                ClassLoader classLoader = waiter.getClass().getClassLoader();
                // 第二个参数:
                Class[] interfaces = waiter.getClass().getInterfaces();
                // 第三次参数:
               
                Waiter waiter2 = (Waiter)Proxy.newProxyInstance(classLoader, interfaces, new MyInvocationHandler(waiter));
                waiter2.server();
                // 说明现在调用代理对象的任何方法的时候,InvocationHandler中的invoke都会执行.
                String s = waiter2.sayHello("张凤");
                System.out.println(s);
            }
        }
   
        public class MyInvocationHandler implements InvocationHandler{
            private Waiter waiter;
            public MyInvocationHandler(Waiter waiter){
                this.waiter = waiter;
            }
            @Override
            /**
             * 方法的参数:
             * * proxy    :产生的代理对象.
             * * method    :当前正在调用的目标类的方法.
             * * params    :正在执行的方法中的参数.
             * /
            public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
                // System.out.println("InvocationHandler invoke 执行了...");
                // System.out.println("微笑=========");
                // waiter.sayHello("张凤");
                // System.out.println(method.getName());
                if("server".equals(method.getName())){
                    System.out.println("微笑==========");
                    Object obj = method.invoke(waiter, params);
                    System.out.println("白白==========");
                    return obj;
                }else{
                    return method.invoke(waiter, params);
                }
            }
        }


    动态代理层次理解:
            被增强的类
        ________|_________
        |                         |
        |                         |                    
        实现类            代理对象        
                                  |
                                  |            
                    (构造方法:三个参数)   
                     1.类的加载器:实现类对象.getClass().getClassLoader()
                     2.被增强的对象实现的所有接口:实现类对象.getClass().getInterfaces()
                     3.处理类:InvocationHandler接口的实现类
                                 |
                                 |
                (InvocationHandler接口中只有一个invoke方法:需要重写)
                    方法中传入三个参数:
                        1.Object proxy:产生的代理对象.
                        2.Method method:当前正在调用的目标类的方法.
                        3.Object[] params:正在执行的方法中的参数.

        注意:
            1.method代表的就是实现类的里边的方法
            2.通过反射调用method方法就是调用了实现类中相应的方法
            3.调用method方法的前后都可以添加代码,被增强的类的对象调用方法时,就会实现invoke方法
            4.创建InvocationHandler接口的实现类,通过构造方法,将要增强的类的对象传入,在赋值给他的成员变量(类型相同)
                    
                    
    字符集编码过滤器的动态代理代码实现:
        
[Java] 纯文本查看 复制代码
@WebFilter(urlPatterns="/*")
        public class GenericEncodingFilter implements Filter {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
            }
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException {
                final HttpServletRequest req = (HttpServletRequest) request;
                // 增强req:
                HttpServletRequest myReq = (HttpServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(),
                            req.getClass().getInterfaces(), new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                // 判断执行的方法是否是getParameter:
                                if("getParameter".equals(method.getName())){
                                    // 调用的是getParameter:需要增强这个方法.
                                    // 判断请求方式是GET还是POST:
                                    String type = req.getMethod();
                                    if("get".equalsIgnoreCase(type)){
                                        String value = (String) method.invoke(req, args);
                                        value = new String(value.getBytes("ISO-8859-1"),"UTF-8");
                                        return value;
                                    }else if("post".equalsIgnoreCase(type)){
                                        req.setCharacterEncoding("UTF-8");
                                    }
                                }
                                return method.invoke(req, args);
                            }
                        });
                chain.doFilter(myReq, response);
            }
            @Override
            public void destroy() {
            }
        }






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2