servlet

servlet是前端和数据库交互的一个桥梁

静态网页资源的技术:
  在前端整个运行的过程中 我们的网页代码不发生改变的这种情况就称为静态的网页资源技术

动态网页资源的技术:
  在前端运行的过程中 我们的前端页面代码会发生改变的这种情况就称为 动态的网页资源技术
servlet的生命周期
生命周期 就是这个Servlet从创建到销毁的整个过程 

问题来了:Servlet什么时候创建呢?

默认情况下 程序启动是不会 创建Servlet对象的

程序在第一次启动的时候 创建对象 执行init方法

那么有没有方法在程序启动的时候 就让Tomcat将这个对象创建好呢?

@WebServlet(urlPatterns = "/ticket/sell",loadOnStartup = 1)

  <servlet>
        <!--    这个名字逻辑上可以随便写  但是一般见名之意 写这个类的类名-->
        <servlet-name>UserServlet</servlet-name>
        <!--    这个配置的是Servlet的全路径-->
        <servlet-class>com.qfedu.edu.servlet.UserServlet</servlet-class>

        <!--
           <load-on-startup>1</load-on-startup> 是一个在web.xml文件中常见的参数配置。它用于指定在应用程序启动时,是否要立即加载某个Servlet或其他组件。当设置为1时,表示在应用程序启动时立即加载;当设置为0时,表示不立即加载,而是在第一次请求时才加载。
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>

程序的销毁是在 Tomcat关闭的时候 这个Servlet才会被销毁
全局参数

所谓的这个全局的参数指的是 在任何一个Servlet中都能取到这个参数 那么这个参数就是全局参数

  • 在web.xml中申明全局参数
<!--    申明全局参数-->
    <context-param>
        <param-name>filename</param-name>
        <param-value>test.txt</param-value>
    </context-param>

    <context-param>
        <param-name>键</param-name>
        <param-value>值</param-value>
    </context-param>
局部参数

Request对象

tomcat将请求封装成了一个对象,这个对象就是Request

Request相关方法
//req中参数的含义
//http://localhost:8080/users?username=admin&password=123456&%E8%AF%B7%E6%B1%82=%E6%8F%90%E4%BA%A4
//  /users
String requestURI = req.getRequestURI();//端口之后,问号之前的数据
String method = req.getMethod(); //获取请求的方法
//String parameter = req.getParameter();通过名字请求参数
req.getContextPath();//获取上下文信息
req.getCookies(); //cookies信息获取
req.getHeaderNames();//获取HTTP协议中所有的key
req.getRequestURL();//获取请求地址
Request获取请求数据
GET
String queryString = req.getQueryString();
System.out.println("获取到的数据是:"+queryString);
//获取到的数据是:username=%E5%BC%A0%E4%B8%89&password=123456&%E8%AF%B7%E6%B1%82=%E6%8F%90%E4%BA%A4
//获取到的数据是加密的,使用的是URLEncode编码
//要使用URLDecode解码
String decode = URLDecoder.decode(queryString,"UTF-8");
System.out.println(decode);
//================================
//如果是tomcat8以前的版本会出现乱码问题
//因为tomcat在处理数据的时候将这些数据进行了ISO-8859-1的编码
//这个ISO-8859-1是欧洲人的编码
//解决办法是先采用ISO-8859-1解码后,再采用汉字编码重新编码,再解码就可以了
String username = req.getParameter("username");
String password = req.getParameter("password");


//解码
//        byte[] bytes = username.getBytes("ISO-8859-1");
//        username=new String(bytes,"UTF-8");

System.out.println(username+"="+password);

URL和URI的区别

URI:统一资源标记符

不光可以标记本地资源,还可以标记网络资源

URL:统一资源定位符

只能定位网络资源

从范围上看,URL的范围小于URI

POST
public class RequestParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //第一种方式getParameter
        //设置编码格式
//        req.setCharacterEncoding("UTF-8");
//        String username = req.getParameter("username");
//        String password = req.getParameter("password");
//        System.out.println("username:"+username+"-----password:"+password);
        //第二种方式:流 post方式都可以使用流来获取
        ServletInputStream in = req.getInputStream();
        //将输入流放到缓冲区
        byte[] buf = new byte[8192];
        int readLength = in.read(buf);
        String val = new String(buf, 0, readLength);
        String values = URLDecoder.decode(val,"UTF-8");
        System.out.println("取出的数据是:"+values);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //get第第一种方式
        String queryString = req.getQueryString();
        System.out.println(queryString);//URLEncode编码
        String values = URLDecoder.decode(queryString, "UTF-8");
        System.out.println("获取到的数据时:"+values);
        //username=%E5%BC%A0%E4%B8%89&password=123456
        //第二种方式
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //????是ISO-8859-1编码,需要解码
        byte[] bytes = username.getBytes("ISO-8859-1");
        username = new String(bytes, "UTF-8");
        byte[] bytepwd = password.getBytes("ISO-8859-1");
        password = new String(bytepwd, "UTF-8");
        System.out.println("username:"+username+"-----password:"+password);
    }
}

Response对象的使用

http响应码
对于HTTP协议的这样一个请求 的响应码很重要  

200:表示的是请求成功

302:重定向错误(多次重定向)

400:服务器接受到数据了 但是无法处理参数  简单的说就是参数不匹配

401:未认证

403:没权限访问

404:路径没对

500:服务器报错
页面转发
  • 转发的特点是啥?
    转发的时候请求地址不变 转发只能转发到服务器的内部 不能抓没法到第三方的服务器上 转发是一次性的请求 req 统一的一个
  • 转发如何实现
req.getRequestDispatcher("/teudan.html").forward(req,resp);

页面重定向

  • 重定向的特点
    地址会发生改变
    重定向是可以定位到任意的服务器的
    重定向的请求和原始的请求不是一个
resp.sendRedirect("http://www.baidu.com");
响应数据的编写
//响应数据给客户端
//你就告诉他编码是啥
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("小波波最帅");
writer.flush();
writer.close();
json格式

Filter

什么是过滤器呢?

所谓的过滤器 你可以认为 这个请求和响应都要经过他、经过他的时候他就可以干预你

这个Filter就是横跨在 我们的前端请求 和 Servlet之间的一个桥梁

这个Filter可以单独的对我们的请求 和 响应 进行干预(改变他|拦截他)

这个Filter的出现 就是为了规避Java中的一个职责 (单一职责)----->实际上是一个 责任链的设计模式

单一职责:自己的事情自己干

浏览器上发送的请求 默认都是 get请求

Filter的使用
filter的编写
public class AuthticationFilter implements Filter {
    /**
     * 请求和响应都会经过这个方法
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

        System.out.println("过滤器执行了....之前");
        //这句话的意思就是放行...
        //filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("过滤器执行了....之后");
    }
}
编写声明
<!--   配置   -->

<filter>
    <filter-name>AuthticationFilter</filter-name>
    <filter-class>com.qfedu.edu.filter.AuthticationFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>AuthticationFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
第二种使用方式
@WebFilter(urlPatterns = "/*")
public class BlackFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("黑名单的Filter----之前");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("黑名单的Filter----之后");
}
}
Filter的生命周期
  • 在程序启动的时候创建Filter对象
  • 对象创建完成,调用init方法进行对资源的初始化操作
  • 当Tomcat关闭的时候,这个Filter的destroy执行
参数的初始化
package com.wz.practice.servlet.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;

@WebFilter(urlPatterns = "/*",initParams = {
    @WebInitParam(name="key1",value = "val1")
})
public class AuthenticationFilter implements Filter {
    public AuthenticationFilter() {
        System.out.println("AuthenticationFilter创建对象");
    }

    /**
     * 资源初始化
     *
     * @param filterConfig 这个参数拿到你设计的全局变量或局部变量
     * @throws ServletException
     */

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("AuthenticationFilter--init执行的地方");
        String test = filterConfig.getServletContext().getInitParameter("test");
        System.out.println("拿到的(全局)参数值是"+test);
        //下面这个方法只能拿到自己的参数
        String key1 = filterConfig.getInitParameter("key1");
        System.out.println("拿到的局部参数是"+key1);

    }

    /**
     * 拦截请求
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("请求之前的拦截");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("请求之后的拦截");
    }

    public void destroy() {
        System.out.println("destory执行的地方");
    }
}
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <context-param>
    <param-name>test</param-name>
    <param-value>123</param-value>
  </context-param>

</web-app>
Filter的运行原理
配置模式下这个运行顺序的问题
<!--申明Filter-->
<filter>
    <filter-name>AuthFilter</filter-name>
    <filter-class>com.wz.practice.filter.AuthFilter</filter-class>
</filter>
<filter>
    <filter-name>BlackFilter</filter-name>
    <filter-class>com.wz.practice.filter.BlackFilter</filter-class>
</filter>
<filter>
    <filter-name>LimitFilter</filter-name>
    <filter-class>com.wz.practice.filter.LimitFilter</filter-class>
</filter>


<!--谁先申明,谁先执行-->
<filter-mapping>
    <filter-name>LimitFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>AuthFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>BlackFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
注解方式的顺序

注解方式的时候 跟这个Filter名字字母的先后顺序有关系 排在前面的先执行 排在后面的后执行

混合方式的顺序

先配置 在按照注解的Filter的手写字母排序

配置 注解排序

责任链的设计模式
需求:
  通过一个全局的过滤器  执行到自定义的这个链条中来   这个自定义的链条就可以实现 限流 降级 认证 鉴权 黑名单校验 敏感词校验 .....
  责任链的设计模式主要实现的功能就是 过滤器  或者拦截器
给所有的链编写一个父接口(BaseSlot)
package com.wz.practice.slot.base;

import javax.servlet.http.HttpServletRequest;

/**
* 我们这里的目的是为了实现仿写
* 给这个过滤器整了个爹  这个接口类似于我们的 Filter接口
*/
public interface BaseSlot {

   /**
    * 这个就要要处理的方法
    * 这个方法类似于我们的 doFilter方法
    * @param req
    */
   void dealReq(HttpServletRequest req);
}
给这个BaseSlot的执行 编写一个父接口 最终需要一个类去调用这个BaseSlot的所有孩子 SlotChain
package com.wz.practice.slot.base;

import javax.servlet.http.HttpServletRequest;

/**
* 这个接口的抽象是为了调用BaseSlot的实现类的
*/
public interface SlotChain {

   /**
    * 这个就要要处理的方法
    * 这个方法类似于我们的 doFilter方法
    *
    * @param req
    */
   void dealReq(HttpServletRequest req);
}
编写链条 --------- 给这个BaseSlot编写实现类
package com.wz.practice.slot.impl;

import com.wz.practice.slot.base.BaseSlot;

import javax.servlet.http.HttpServletRequest;

public class AuthSlot implements BaseSlot {

   public void dealReq(HttpServletRequest req) {
       System.out.println("----AuthSlot-----------");
   }
}


package com.wz.practice.slot.impl;

import com.wz.practice.slot.base.BaseSlot;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class BlackSlot implements BaseSlot {

   public void dealReq(HttpServletRequest req) {
       System.out.println("-------BlackSlot--------");
   }
}


package com.wz.practice.slot.impl;

import com.wz.practice.slot.base.BaseSlot;

import javax.servlet.http.HttpServletRequest;

public class LimitSlot implements BaseSlot {
   public void dealReq(HttpServletRequest req) {
       System.out.println("-------LimitSlot--------");
   }
}
编写链条的调用者 -------- SlotChain的实现类
在SlotChainImpl中去申明我们的BaseSlot的容器
package com.wz.practice.slot.impl;

import com.wz.practice.slot.base.BaseSlot;
import com.wz.practice.slot.base.SlotChain;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class SlotChainImpl implements SlotChain {
   private static Map<String, BaseSlot> maps = new ConcurrentHashMap<String,BaseSlot>();
   private static String slotOrder;
   static {
       maps.put("authSlot",new AuthSlot());
       maps.put("blackSlot",new BlackSlot());
       maps.put("limitSlot",new LimitSlot());
       //资源加载
       InputStream in = SlotChainImpl.class.getClassLoader().getResourceAsStream("slot.properties");
       Properties properties = new Properties();
       try {
           properties.load(in);
           slotOrder = (String) properties.get("slotOrder");
       } catch (IOException e) {
           e.printStackTrace();
       }

   }
   public void dealReq(HttpServletRequest req) {
       if(maps.size()==0){
           throw new RuntimeException("slot数据为空无法启动链条");
       }
       if("".equals(slotOrder)||null==slotOrder){
           throw new RuntimeException("slot执行顺序未定义....");
       }
       //执行到这里两边都有值
       if(!slotOrder.contains(",")){
           //说明只有一个值
           BaseSlot baseSlot = maps.get(slotOrder);
           handlerSimpleSlot(req, baseSlot);
           return ;
       }
       //程序如果是执行到这里 说明有多个名字
       String[] split = slotOrder.split(",");
       for (int i = 0; i <split.length ; i++) {
           //取出这个名字
           String slotName = split[i];
           //说明只有一个值
           BaseSlot baseSlot = maps.get(slotName);
           //接下来从maps中取出数据
           handlerSimpleSlot(req, baseSlot);
       }
   }

   /**
    * 处理单个Slot
    * @param req
    * @param baseSlot
    */
   private static void handlerSimpleSlot(HttpServletRequest req, BaseSlot baseSlot) {
       if(null== baseSlot){
           throw new RuntimeException("slot执行顺序的名字不对");
       }
       //执行到这里说明名字是对的
       baseSlot.dealReq(req);
   }
}
给这个BaseSlot编写调用顺序----slot.properties
slotOrder = limitSlot,authSlot,blackSlot
在SlotChainImpl中去进行BaseSlot链条的调用
package com.wz.practice.filter;

import com.wz.practice.slot.base.SlotChain;
import com.wz.practice.slot.impl.SlotChainImpl;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
* 全局过滤器
*/
@WebFilter(urlPatterns = "/*")
public class GlobleFilter implements Filter {
   private SlotChain slotChain = new SlotChainImpl();
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       try {
           slotChain.dealReq((HttpServletRequest) servletRequest);
       } catch (Exception e) {
           System.out.println("执行到了异常!");
       }
   }
}
代理的设计模式

代理模式的主要实现功能是什么?

对方法进行监听,在执行方法是动态的植入我们的代码

静态代理

静态代理和动态代理,被代理的类必须实现接口

静态代理的步骤:
	1、编写一个代理类和被代理类,实现相同的接口
	2、在这个代理类维护被代理类的类对象
	3、在下面的方法中,调用被代理类中相同名字的方法,就能完成代理

静态代理的步骤:

  • 编写接口
package com.wz.practice.proxy.static1;

public interface IUserService {
   void add();
   void update();
}
  • 编写被代理类
package com.wz.practice.proxy.static1;

public class UserService implements IUserService{
   public void add() {
       System.out.println("-------add执行------");
   }

   public void update() {
       System.out.println("--------update执行-----");
   }
}
  • 编写代理类
package com.wz.practice.proxy.static1;

public class UserServiceProxy implements IUserService{
   //在这个类中维护被代理的对象
   private IUserService userService;
   public UserServiceProxy(IUserService userService){
       this.userService=userService;
   }

   //在下面的方法中调用 被代理类中相同名字的方法 就可以完成监控了
   public void add() {
       System.out.println("打开事务");
       userService.add();
       System.out.println("提交事务");
   }

   public void update() {
       System.out.println("打开事务");
       userService.update();
       System.out.println("提交事务");
   }
}
  • 测试
package com.wz.practice.proxy.static1;

public class test {
   public static void main(String[] args) {
       UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
       userServiceProxy.update();
       userServiceProxy.add();
   }
}

结果:

servlet,Filter,责任的设计模式,静态代理_动态代理

动态代理(JDK代理)

静态代理和动态代理,被代理的类必须实现接口

整个代理的过程被JDK实现了

Proxy.newProxyInstance

动态代理的步骤:

  • 编写接口
package com.wz.practice.proxy.dynamic;

public interface IUserService {
    void add();
    void update();
}
  • 编写被代理的类
package com.wz.practice.proxy.dynamic;

public class UserService implements IUserService{
    public void add() {
        System.out.println("-------add执行-------");
    }

    public void update() {
        System.out.println("---------update执行-----");
    }
}
  • 测试
package com.wz.practice.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        /**
         * 第一个参数类加载器 这个是有固定的写法
         *    被代理的类.class.getClassLoader
         *  第二个参数:就是我们被代理的类实现的接口
         *    如果被代理的是类:被代理的类.class.getInterfaces();
         *    如果被代理的是接口:new Class[]{被代理的接口.class}
         *  第三个参数:就是对这个代理的类或者接口中方法执行的监听
         *    new InvocationHandler(){}
         */
        IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), UserService.class.getInterfaces(), new InvocationHandler() {
            /**
             * 这个方法就是对你当前执行方法的监听
             * proxy:代理类对象
             * method:当前执行的方法
             * args:执行方法需要的参数
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("打开事务");
                String name = method.getName();
                System.out.println("当前执行的方法是:"+name);

                //可以去执行这个方法
                //这里的第一个参数:被代理类的对象
                Object invoke = method.invoke(new UserService(), args);
                System.out.println("提交事务.....");
                return invoke;
            }
        });
        userServiceProxy.add();
        userServiceProxy.update();
    }
}