一:概念 运行在服务端的小程序
二:创建步骤
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{ 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{ 100px; text-align: right; height: 40px; } .rightgap{ padding-left: 50px ; } #user,#password,#birthday,#email,#age,#time{ 250px; height: 30px; border: 1px solid #A6A6A6; /*设置表框圆角*/ border-radius: 5px; padding-left: 10px; } #sub{ 150px; height: 40px; background-color: gold; border: 1px solid #FFd026; } #code{ 125px; height: 30px; border: 1px solid #A6A6A6; /*设置表框圆角*/ border-radius: 5px; padding-left: 10px; } #checkCode{ 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 { } }
过滤器的敏感词汇过滤功能:需要增强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(); } }
具体实例:增强
过滤器实现敏感词汇功能:
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(); } } }