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 {@Overrideprotected 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);}@Overrideprotected 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();}
}
结果:
动态代理(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();}
}