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