Java Web 笔记
一些基本概念:
访问一个服务器要打冒号加端口,一般服务器默认是80端口,这时不用打端口名。Ip与端口难记,就有了域名,会先去访问DNS,可以被解析成IP。
Http协议是搭建在TCP协议之上的
Tomcat就是HTTPServer,浏览器与HTTPServer是浏览器request与服务器response的关系。HTTP建立一个TCP连接,将request或response以文件形式发送然后TCP向上提交给对方。浏览器收到response将头去除剩下的就是HTML网页交给内核渲染生成DOM。
Tomcat
原始方法启动
在Tomcat本地目录中bin文件夹startup.dat文件双击,就启动了一个黑框:
JAVA_HOME这个环境变量配置好了黑框才出来
localhost:8080浏览器输入这,就看到了熟悉的黄猫页面。黑框关闭就服务关闭了刷新浏览器无效了。
已经打开一个黑框,再双击startup.dat,此时去logs文件夹下看.log文件,里面有错误信息,端口被挤占。一个端口只能被绑定一次。
新开一个cmd命令行:
输入这个可查看被谁绑定了:
conf文件夹server.xml文件能将8080端口改成其它数字(端口)。浏览器输入修改的端口访问成功。
鼠标放在这个小框上右键属性:
属性显示如图GBK
conf文件夹下logging.properties这个文件
如图修改成GBK就不会乱码。
项目部署
三个方法都是Tomcat本地安装目录中的为文件/文件夹,一般推荐第三种方法
1.
已经双击startup.dat启动服务器的情况下:本地tocat安装目录->webapps文件夹:新建一个hello文件夹将001.html放到里面。
浏览器:localhost:8080/hello/001.html
1.01优雅点的方法:001.html右键变成压缩文件001.zip。文件001.zip改名为hello.war。
hello.war直接放到webapps文件加下等一会自动会出现hello文件夹,里面有html文件。同样浏览器:localhost:8080/hello/001.html
2.
如图,在全文件最底下新增context标签,docbase是001.html所在的文件夹,path是设定一个tomcat虚拟路径。
双击启动Tomcat后浏览器访问:localhost:8080/hello2/001.html
server.xml是比较敏感的一个文件,一般不要修改。
3.
server.xml是比较敏感的一个文件,一般不要修改。
所以有了第三种方法,在localhost文件夹下添加一个hello3.xml内容如下:
同样启动后浏览器访问:localhost:8080/hello3/001.html
IDEA中建立
云盘里有个五分钟视频讲破解。
刚开始学习,不用maven创建,newproject时选择 java Enterprise,然后继续就可以。
常见完成之后在总的项目名称上右键Add Framework:
然后:
这时候出现了Web文件夹,里面有web-INF文件夹,index.jsp等
然后配置tomcat选择local
新建的项目配置tomcat时点击上图加号出现两个选项点击artifact,然后就出现了这个路径。它是指明web文件夹路径。
浏览器访问的时候直接localhost:8080/myweb_war_exploded默认访问index.jsp新建了其他页面文件时,斜杠加文件名。
这时候已经将logging.properties中UTF-8全部改为GBK了。不出现乱码
已经启动tomcat时又新建了页面文件,要点击deploy或者重启。
Servlet
概念
Servlet是一种服务器端的Java应用程序,独立于平台和协议,可动态生成web页面。
Servlet是使用JavaServlet接口,运行在Web应用服务器上的Java应用程序,可以对web浏览器或其它Http客户端发送的请求进行处理。
处理客户传来的HTTP请求并返回响应。通常所说的servlet就是指HttpServlet,能处理的请求有doGet等等,开发时可直接继承javax.servlet.http.HttpServlet。
Servlet需要在web.xml中描述,映射Servlet的名字,配置Servlet类,初始化参数。安全配置,URL映射以及启动优先权等。
添加servlet
引入包,自己看图:
Web.xml的配置
一个Servlet对象正常运行需要进行适当配置,告知哪个请求调用哪个Servlet对象处理。
<!-- <servlet>-->起到注册作用
<!-- <servlet-name>demo01</servlet-name>--> //这个类自定义名字
<!-- <servlet-class>web.servlet.MyServletDemo01</servlet-class>--> //指定完整位置包名,类名
<!-- <load-on-startup>0</load-on-startup>-->
<!-- 如果放的是一个0或者正整数,系统将在启动后加载
如果放的是一个负数(缺省是-1),那么就是懒加载-->
<!-- </servlet>-->
<!-- <servlet-mapping>-->映射访问servlet的url
<!-- <servlet-name>demo01</servlet-name>--> 类名要与想调用的类的<servlet>中的<servlet-name>一样
<!-- <url-pattern>/demo01</url-pattern>--> 当这个路径来访时调用类的service
<!-- </servlet-mapping>-->
配置好需重启tomcat
此时运行tomcat时只显示连接成功,浏览器默认访问index当访问:localhost:8080/myweb_war_exploded/demo01时
控制台输出了MyServletDemo01类的service方法的输出内容。
生命周期
package Web.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet(urlPatterns = {"/demo01"})
public class MyServletDemo01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init()....................");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service.......................");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destory.......................");
}
}
启动Tomcat,除了提示Connected to server什么都没有,浏览器输入localhost:8080/myweb_war_exploded/demo01访问此类时:
再去掉用别的类也不会destroy说明一直处于等待状态。
知道点击关闭服务器:
三个阶段:初始化,响应请求阶段与终止。分别对应上述三个方法。
初始化
init(),Servlet生命周期起点,此时将servlet装入tomcat内存,所种方式:请求时或脑图中所说的自动加载。
处理请求
调用service()方法,用于传递"请求"与"响应"对象。从response对象获得请求信息然后用response对象的方法响应。
service()方法可以调用doGet,doPost等方法来处理请求。
该方法可对此执行
终止
服务器不再需要Servlet或重新装入新实例时,只执行一次。
本次实验时分别访问了三个类,可见只有在关闭时才调用终止方法
@WebServlet
//@WebServlet({"/demo01", "/process/demo02", "/data/demo03"})
//@WebServlet("/data/data1/data/*")
@WebServlet("*.do")
可以看到第三种方式竟然没有斜杠,网页点击提交时怎么访问的?
可见斜杠是自动加的。
派生类
第二个类浏览器url访问demo03时默认doget方法响应。
当自己写表单要求post方法访问时post方法才响应:
package Web.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo02")
public class MyServletDemo02 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("这个接口只需要实现一个方法~-~");
}
}
package Web.servlet;
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("/demo03")
public class MyServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet inHttpServlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost() of HttpServlet");
}
}
GenericServlet类为三个接口中方法提供了部分实现,除了service(),但没实现Http请求处理。
通常情况下,编写Servlet类都继承于HttpServlet,因为HttpServlet提供了http请求的处理方法。专门为HTTP设计,对javax.servlet
Servlet接口中所有方法提供了默认实现。只需重写两个方法就可以实现自己的Servlet。
关于HTTP的编程
两个接口,一个获得请求参数,一个响应。
典型应用
读取表单,查看请求信息
写一个简单表单,点击了选项B,点击提交,访问链接如上图。
package web.servlet; 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.BufferedReader; import java.io.IOException;
import java.util.Enumeration; import java.util.Map;import java.util.Set;
@WebServlet("*.do")
//做这个案例时使用上面这个路径要与网页交互,在Demo02中再换成@WebServlet("/demo01")
public class MyServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet()..............");
// 1. 获得Method
String method = req.getMethod();
System.out.printf("Method: %s\n", method);//Method: GET
// 2. 获得路径相关
// URI = <虚拟路径> + <servlet路径>
// URL : http:// ...
String contextPath = req.getContextPath(); //虚拟路径
String servletPath = req.getServletPath();
String uri = req.getRequestURI(); //主机名端口号与请求参数之间的路径 servlet
StringBuffer url = req.getRequestURL();
System.out.printf("Path: %s\n", contextPath); //ContextPath:/myweb_war_exploded
System.out.printf("ServletPath: %s\n", servletPath);//ServletPath:/a.do
System.out.printf("uri: %s\n", uri); //uri:/myweb_war_exploded/a.do
System.out.printf("url: %s\n", url.toString()); //rl:http://localhost:8080/myweb_war_exploded/a.do
// 3. 请求参数
String queryString = req.getQueryString();
System.out.printf("QueryString: %s\n", queryString);//QueryString:username-li=aaaa&username-2=bbbb&options=optionB
// 4. HTTP协议版本
String protocol = req.getProtocol();
System.out.printf("Protocol: %s\n", protocol);//Protocal:HTTP/1.1
// 5. 客户端机器的IP地址
String remoteAddress = req.getRemoteAddr();
System.out.printf("RemoteAddr: %s\n", remoteAddress);//remoteAddress:0:0:0:0:0:0:0:1
// 6. 根据信息名来获得值,以及所有的信息名
(请求头里的所有key)
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()){//方法: boolean hasMoreElements( ) 测试此枚举是否包含更多的元素。有元素时true
String name = headerNames.nextElement();//返回下一个元素
String value = req.getHeader(name);
System.out.printf("[Header] %s: %s\n", name, value);打印的东西太多见下面第一个图
}
String s1 = (String)req.getAttribute("myP1");/获得共享数据(下面请求转发例子),知识:请求转发是同一次请求,所以能获得数据,否则为null
System.out.println(s1);null
//Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用
方法:Object nextElement( )如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
// 9. 参数值的获得
// 获得唯一值的参数值
String v1 = req.getParameter("username-li");//get函数
System.out.printf("username-li=%s\n", v1);//username-li=aaaa
// 获得多个值的参数(多选)
String[] options = req.getParameterValues("options");
for(String option: options){
System.out.println(option);//optionB。因为之选了B选项
}
// // 获得所有的参数
//所有的参数名
Enumeration<String> pNames = req.getParameterNames();
while(pNames.hasMoreElements()){
String name = pNames.nextElement();
String value = req.getParameter(name);//通过参数名获得参数
System.out.printf("%s=%s\n", name, value);
打印结果:
username-li = aaaa
username-2 = bbbb
options = optionB
//options无论几个只返回一个,要和前端商量好
}
// // 获得所有的参数与值的键值对,值为数组类型,即1参数名可对应多个值。
Map<String, String[]> pMaps = req.getParameterMap();
//Key是name,V是参数,数组类型是可能有多个参数
Set<String> keyset = pMaps.keySet();//Map的遍历方法获得Key
for(String name: keyset){
String[] values = pMaps.get(name);
System.out.printf("%s=", name);
for(String value: values){
System.out.printf("%s; ", value);//username-li=aaaa; username-2=bbbb; options=optionB;
}
System.out.println("");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost().................");
// 7. 获得请求体,请求体的定义就是用post传的数据或文件
// BufferedReader br = req.getReader();
//
// String line = null;
// while((line = br.readLine()) != null){
// System.out.println(line);//username-li=aaaa&username-2=bbbb&options=optionA&options=optionB
// }
// 8.练习题 建立一个servlet,可以通过referer判断是不是盗链。(下面图片里蓝色的就是referer)
req.setCharacterEncoding("utf-8");//方法为 post时,要设定。否则中文乱码
// 获得唯一值的参数
String v1 = req.getParameter("username-wang");//与7,冲突,两个方法只能使用一个,7注释掉。
System.out.printf("username-wang=%s\n", v1);
// 获得多个值的参数
String[] options = req.getParameterValues("options");
for(String option: options){
System.out.println(option);
}
// 获得所有的参数
Enumeration<String> pNames = req.getParameterNames();
while(pNames.hasMoreElements()){
String name = pNames.nextElement();
String value = req.getParameter(name);
System.out.printf("%s=%s\n", name, value);
}
// 获得所有的参数,以及参数对应的多个值
Map<String, String[]> pMaps = req.getParameterMap();
Set<String> keyset = pMaps.keySet();
for(String name: keyset){
String[] values = pMaps.get(name);
System.out.printf("%s=", name);
for(String value: values){
System.out.printf("%s; ", value);
}
System.out.println("");
}
}
}
这个图就是getHeaderNames()方法打印内容
请求转发
package web.servlet;
import javax.servlet.RequestDispatcher;
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("/demo02")
public class MyServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("in doGet() of /demo02");//访问demo02这一行打印出来了,紧接着下面才自动打印demo01的东西,说明这里的东西也是要执行的
// RequestDispatcher dispatcher = req.getRequestDispatcher("/demo01");
// dispatcher.forward(req, resp);
// 10. 请求转发:请求转发到服务器上的另一个资源(servlet,jsp或html)
// 11. 请求间共享数据
// void setAttribute(String name, Object obj)
// Object getAttribute(String name)
// void removeAttribute(String name)
req.setAttribute("myP1", "王校长很帅");//先访问一次demo01,那边get打印null,再运行demo02,由于请求转发了,又访问了demo01,这时候那边的get方法就有东西 。注意请求转发是同一个请求所以才能共享数据,重定向不行。
req.getRequestDispatcher("/demo01").forward(req, resp);//连写
}
}
响应
package web.servlet;
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;
import java.io.PrintWriter;
@WebServlet("/demo03")
public class MyServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 响应行 状态码 setStatus(int sc)
resp.setStatus(200);
resp.setCharacterEncoding("utf-8");//通知浏览器自己使用的编码。有下面的setContentType,这里可以不写
// 2. 设置响应头 setHeader(String name, String value)
resp.setHeader("abcd", "xiaomi");
// resp.setHeader("content-type", "text/html;charset=utf-8");
resp.setContentType("text/html;charset=utf-8");//告诉浏览器要使用***来解码。(确保写入汉字时不会乱码)
// 3. 写入响应体 字符流 PrintWriter resp.getWriter()
// 字节流 ServletOutputStream resp.getOutputStream()
PrintWriter writer = resp.getWriter();
writer.println("<HTML>" +
" <HEAD>" +
" </HEAD>" +
" <BODY>" +
" <p>Hello, World! 你好!</p>" +
" </BODY>" +
"</HTML>");
writer.flush();
writer.close();
}
可以看到,访问03时,直接弹出来一个文件,下载之后就是html中的内容。还有右边捕捉的headers内容
重定向
package web.servlet;
//重定向
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("/demo04")
public class MyServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setStatus(HttpServletResponse.SC_FOUND);
// resp.setHeader("location", "/myWeb_war_exploded/demo03");
resp.sendRedirect("./demo03");//用一行,代替上面两行(相对路径同级目录)
}
}
访问demo04时与访问03效果相同,直接弹出了html文件。
响应一张图片
package web.servlet;
import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
@WebServlet("/demo05")
public class MyServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 初始化一张图片
int width = 200;
int height = 100;
• BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);//图片类型,RGB
• // 2. 画东西 图片涂成粉色,画一个蓝色的边框
• Graphics g = image.getGraphics();
• g.setColor(Color.PINK);
• g.fillRect(0, 0, width, height);//左上到右下的坐标,整个涂满
• g.setColor(Color.BLUE);
• g.drawRect(0, 0, width - 1, height - 1);//draw非实心
• // 3. resp输出
• ImageIO.write(image, "jpg", resp.getOutputStream());//将image以jpg形式,输出给resp.getOutputStream()
}
}
这就是那张图片,黑色部分不是
ServletContext
《ServletContext类定义了一组方法,用于Servlet容器之间的通信,以获得相关信息,如文件的mime类型,分发请求和日志等》
import javax.servlet.ServletContext;
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("/demo01")
public class MyServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
ServletContext context2 = req.getServletContext();//两种方式得到都是一样的
System.out.printf("p1: %s\n", (String)context.getAttribute("p1"));//第一次访问,null,第二次访问就有数据了。只要服务器不重启。
System.out.println(context == context2);//true
context.setAttribute("p1", "Napoleon wang");
String filename = "a.jpg";
String mimeType = context.getMimeType(filename);
System.out.println(mimeType); //image/jpeg
String realPath = context.getRealPath("/myWeb_war_exploded/index.jsp");//一个文件在操作系统上的真实路径
System.out.println(realPath);//E:\IdeaProject\0102\out\artifacts\myweb_war_exploded\myweb_war_exploded\index.jsp
}
}
import javax.servlet.ServletContext;
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("/demo02")
public class MyServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.printf("p1: %s\n", (String)context.getAttribute("p1"));//第一次null,运行一次demo01,第二次访问,就有数据了
}
}
//范围
cookie
package myWeb;
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;
@WebServlet("/1/cookie")
public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取Cookie
Cookie[] cookies = req.getCookies();
if(cookies != null){
for(Cookie c: cookies){
String name = c.getName();
String value = c.getValue();
System.out.printf("%s: %s\n", name, value);
}
}
// 2. 写入Cookie
// Cookie cookie = new Cookie("servletKey", "AValue");
// resp.addCookie(cookie);
// 3. 是否可以一次发送多个Cookie?
// Cookie cookie2 = new Cookie("servletKey2", "AnotherValue");
// resp.addCookie(cookie2);
// 4. Cookie在浏览器中能保持多长时间 4.1Path知识,注意看上张导图。
Cookie cookie = new Cookie("servletKey", "AValue");
cookie.setPath("/loginDemo_war_exploded");
cookie.setMaxAge(300);
resp.addCookie(cookie);
Cookie cookie2 = new Cookie("servletKey2", "AnotherValue");
cookie.setMaxAge(1);
resp.addCookie(cookie2);
// setMaxAge : 正数 存在的秒数 记住
// 负数 浏览器关闭,Cookie就消失,默认
// 零 删除这个Cookie
// 5. Cookie里面是否可以有中文
// Tomcat 8 以前:不可以 -- URLEncode
// Tomcat 8 以及以后:可以
}
}
访问这个Servlet可以在network里面抓到,两个cookie都存在(其他的忽略掉,无关)
关闭浏览器,再访问默认路径可以看到只剩"servletKey了,servletKey2已经消失
JavaScript操作cookie
访问、下载文件
一个点表示同级目录,
所以是此html文件同级的:http://localhost:8080/loginDemo_war_exploded/2.html
则这个点代表的是http://localhost:8080/loginDemo_war_exploded
所以当点击上面三个链接的时候服务器直接显示响应文件
点击下面三个链接时,映射到servlet来处理:
package myWeb;
//让连接指向的文件,无论浏览器是否能搞定都直接下载(默认是能搞定的直接打开,否则下载)
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/downloadFile")
public class DownloadFile extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获得 fileName
String fileName = req.getParameter("filename");//参数名
// 2. 获得 fileName的realpath,并且用File打开
ServletContext context = this.getServletContext();
String realPath = context.getRealPath("./" + fileName);//获得此路径的本地真实路径
FileInputStream fis = new FileInputStream(realPath);
// 3. 设定Response的header
String mimeType = context.getMimeType(fileName);
resp.setHeader("content-type", mimeType);
resp.setHeader("content-disposition", "attachment;filename=" + fileName);//保存的时候按什么文件保存,浏览器就知道什么类型了,按附件形式
// UrlEncode UrlDecode: Chrome
// Base64 Encode/Decode: Firefox
// 作业,根据不同的浏览器,对文件名进行不同的编码,以使得能够正确的显示中文文件名
// 4. 把数据写入Response
ServletOutputStream sos = resp.getOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff, 0, len);
}
sos.flush();
sos.close();
fis.close();
}
},
登录密码加盐等有空要看
Session
保存页面状态。会话跟踪:从一个客户机请求传送的数据开始,维持状态到下一个请求,并且能够识别出是相同的客户机所发送的信息,连接状态信息会一直保存。10位客户访问同一个网站,该网站自动生成十 个不同的会话保存客户活动信息,直到客户离开网站。
识别用户身份:session为客户端用户分配一个编号。session ID:服务器端随机生成的session文件的文件名,因此能够保证唯一性,进而确保session的安全。
session作用于同一浏览器中,在各个页面中共享数据,无论当前浏览器是否在各个页面之间执行了跳转操作,整个用户会话一直存在。直到关闭浏览器,生命周期结束,如果在一个会话中客户端长时间不向服务器发送请求,session对象就会消失,这取决于服务器,Tomcat,默认30min,可以修改。
JSP一个很重要的内置对象,是javax.servlet.httpServletSession类的一个对象。
用户第一次访问jsp页面时,jsp容器会自动创建一个session对象,直到关闭浏览器服务器上的该客户的session才被注销。用户重新打开并再次连接到服务器时,服务器将为该客户创建一个新的session对象以及session ID。
用户登录时可以setAttribute(),用户点击注销时可以remove。然后跳转到首页。
遇到了一个错误有空可以看看:
package myWeb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/session")
public class SessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 读取session
HttpSession session = req.getSession();
String value = (String)session.getAttribute("key");
System.out.println(value);
// Cookie设定JSESSIONID
Cookie c = new Cookie("JSESSIONID", session.getId());
c.setMaxAge(120);
resp.addCookie(c);
// 2. 写入session
session.setAttribute("key", "MyValue");
// 3. 删除 removeAttribute
}
}
JSP
介绍
jsp只在服务器端运行,浏览器端不会出现。
jsp文件会被Tomcat编译成.java文件,
位置:每次启动tomcat时打印在控制台上的:
Using CATALINA_BASE: "C:\Users\24563\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\cd77e4b9-10b5-4910-9bb9-60c6cac2fe69"
内容是这样的:
可以看到将html变成字符串形式输出了。
jsp先被生成为以上之中servlet然后再编译为.class文件。所以jsp本质是servlet
页面元素
<%任意的java代码%>
<%= %> 里面只写一个表达式,不写分号,相当于out.print()这个方法的参数。
<%! %>声明一个java类或方法或变量,声明之后作用域范围为当前页面,这种方法慎用。一般用前两种方法
<>
内置对象
内置对象:在servlet类中需要get才能用,在jsp页面中直接使用,不加声明使用的成员变量,
下面的图:了解
指令
<% @ 指令名 属性1 ="属性值" 属性2 = "属性值"...%>
<%@ page import="java.io.PrintWriter" %>
page
在一个jsp页面中page指令可以出现多次,但每种属性只能出现一次,重复设置会覆盖。
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" errorPage="500.jsp" language="java" %>
errorPage:页面发生异常时,容器将转向属性值指向的页面。
这时500.jsp页面:
这里出现了另一个属性:isErrorPage,isErrorPage="true"属性要设置为true,才可使用内建对象exception
手册:
include
将指定位置上的资源包含到当前页面
taglib
taglib:允许在jsp页面使用标签(用户自定义标签)
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 使用某标签,标签地址
prefix:在jsp里引用这个标签的前缀。
uri:标签地址。
D:\后端零基础就业班\0103_JavaWeb\loginDemo\lib 里面有两个包jstl、standard
要想taglib就需要把他们放到自己项目的lib文件中。
学taglib引入资源时需要用到,课程中放在lib文件夹,但是遇到了错误:
遇到错误解决半小时:
无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]
解决方法:csdn
检查是否正确jar包以及tld文件,jstl.jar和standard.jar放在在WEB-INF/lib/下 tld文件都放在WEB-INF/下,然后引入jstl1.0jar包中的c.tld、c-1_0.tld、c-1_0-rt.tld文件在WEB-INF文件夹中。(我试过当jstl1.0版本的也成功过)其实jstl.jar包中也有这三个文件,但当我打开一探究竟时,发现几乎完全一样,只有协议不一样,所以我觉得jar包缺陷和jar包冲突可能性更大。
注释与MVC
隐藏注释:<%-- --%> 这是jsp注释
还有以下几种<%//单行注释%>、<%/* 多行*/%>
上一行:不会被发送到浏览器。
第二行:会被发送到浏览器,虽然被注释掉了。在浏览器里能看到这行注释。发到浏览器端,浏览器端的引擎决定将其注释。
mvc
model时读取数据库生成的data
view是页面
动作元素
就业班没介绍。
用于在jsp页面执行某一个操作,如动态包含一个文件,转向另一个文件。动作元素和指令元素不同,动作元素是在客户端请求时期动态执行的,每次请求可能都会被执行一次,而指令元素编译时期被执行。
<jsp:include> forward,param,useBean,setProperty,getProperty等用时自己查。
EL
EL是表达式直接使用,JSTL则是标签库
降低使用门槛,对不擅长使用Java的人更友好。
与jsp的区别:<%=null%> 会报错 因为这个方式先去调用.toString()
${ null } el语言不会报错
禁用前与禁用后:禁用前显示这个key的值,禁用后当字符串显示出来了
${'${'} 输出结果为${
由于EL表达式是${开头,如果在网页中要显示${字样字符串,必须以字符串或者前面加\禁用
补充--输出字符串:
${"字符串内容"} 单双引号都可以
${'字符串内容'}
<%@ page import="myWeb.User" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %><%--
Created by IntelliJ IDEA.
User: admin
Date: 2020/11/7
Time: 19:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 1. EL 如何禁用?
1. 在$前放一个\
2. 使用指令page的isELIgnored="true" 默认不禁用,如果设置了,整个页面都会被禁用
3.web.xml中配置<el-ignored>袁术
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config> 以上适合禁用web应用中所有jsp页面.
--%>
<%-- 2. EL中可以支持什么样的表达式?(${}里面都能放什么)
能放:
2.1 四种域对象中的属性
2.2 属性或者数字、字符串(单双引号都行)、布尔值、通过运算符连接得到的表达式
1. 算术运算符: + - * /(div) %(mod) 意思是除号也可以写为div所以括号里的东西在el里算关键字,
2. 关系运算符: >(gt) <(lt) >=(ge) <=(le) ==(eq) !=(ne)
3. 逻辑运算符: &&(and) ||(or) !(not)
4. ? :
5.empty运算符 前缀运算符用来确定一个对象或变量是否为mull或为空。可以与not运算符结合使用
--%>
为变量命名时使用了关键字会抛出异常。
<%-- 3. 如何获取一个属性
3.1 ${域对象.属性}
3.2 ${属性} -> 按照 pageScope,requestScope, sessionScop, applicationScope的顺序从小到大找
3.3 ${属性} 属性是一个对象,可以访问属性的getter和setter的 getXxxXxxXxx -> xxxXxxXxx
3.4 ${属性[索引]} 属性是一个List对象,可以使用方括号索引的方法来使用
3.5 ${属性.key} 属性是一个Map对象,可以使用.key的方法来使用
${属性["key"]}
--%>
<%-- 4. 隐含对象 类似jsp中的内置对象直接通过对象名操作,有11个
pageContext javabean对象,对应于javax.servlet.jsp.PageContest类型。其它隐含对象均对应map类型
页面上下文对象,用于访问jsp内置对象和servletContext。获取这些对象后即可获取其属性值
--%>
<%
String ab = "";//aaa,EL没法用,要加到域对象里el才能使用
String a2 = "bbb";
String a3 = "ccc";
箭头后就是访问作用域范围的隐含对象,只能用来取得指定范围内的属性值。
// pageContext -> pageScope : 单一jsp page的范围
// request -> requestScope :用户请求期间(或一个jsp到另一个jsp请求期间)
// session -> sessionScop :用户持续在服务器连接的期间
// application -> applicationScope :服务器执行服务到服务器关闭为止
pageContext.setAttribute("a", ab);
pageContext.setAttribute("a2", a2);
session.setAttribute("a2", a3);
User user = new User("abcd");
pageContext.setAttribute("user", user);
ArrayList<String> list = new ArrayList<String>();
list.add("a1111");
list.add("a2222");
pageContext.setAttribute("list", list);
HashMap<String, String> map = new HashMap<>();
map.put("s1", "s1 String");
map.put("s2", "s2 String");
pageContext.setAttribute("map", map);
%>
<p> ${a}</p>
<p>${pageScope.a == "" ? "<空字符串>" : pageScope.a}${pageScope.a2}${sessionScope.a2}</p>
<%-- 可以写a亦可以写全pageScope.a,不写会按照注释3.2依次找,如果指定了范围,就只在此范围内找--%>
<p>${pageScope.user.username}</p> <%-- 使用了src底下的user类--%>
<p>${list}=${list[0]}=${list[10]}</p>
<p>${map}=${map.s1}=${map.s3}</p>
<p>${map}=${map["s1"]}=${map["s3"]}</p><%-- 同上一行--%>
<p>${pageContext.request.contextPath}</p>
<p> ${a}</p>
<p>\${a}</p>
</body>
</html>
运行:
EL表达式存取范围
变量没有指定范围时,系统默认从page范围中查找,然后依次在request、session、application范围中查找。如在此过程中找到此变量直接返回,如果没有,返回null。
JSTL
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
有五个标签库,这只是核心标签库。
EL是表达式,jstl 是标签库,要引入jar包.
<%@ page import="java.util.ArrayList" %><%--
Created by IntelliJ IDEA.
User: admin
Date: 2020/11/9
Time: 14:08
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
System.out.println("In JSTL1.jsp...");
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 1. c:if test="布尔表达式"
没有else,true:输出到页面(浏览器网页显示),false:不输出
2. c:choose, c:when test="布尔表达式", c:otherwise 想switch
3. forEach var="i" begin="0" end="14" step="1" varStatus (count) =》for(int i = 0; i < 15; i++) step(每次增加) varStatus循环次数
forEach items="${list}" var="str" varStatus="s" (index, count) =》for(String s: list)
--%>
<% // 获得 Data =》 Model
// 根据Data 输出到 HTML =》Control + View
Boolean b1 = true;
pageContext.setAttribute("b1", b1);
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
pageContext.setAttribute("list", list);
int weekday = 15; // 0 1 ~ 6
pageContext.setAttribute("weekday", weekday);
%>
<c:if test="${b1}">
某些要输出的语句。。。<br/>
</c:if>
<c:if test="${empty list}">
空的List<br/>
</c:if>
<c:if test="${not empty list}">
不空的List,能看到 <br/>
</c:if>
<c:choose>
<c:when test="${weekday == 0}">星期日<br/></c:when>
<c:when test="${weekday == 1}">星期一<br/></c:when>
<c:when test="${weekday == 2}">星期二<br/></c:when>
<c:when test="${weekday == 3}">星期三<br/></c:when>
<c:when test="${weekday == 4}">星期四<br/></c:when>
<c:when test="${weekday == 5}">星期五<br/></c:when>
<c:when test="${weekday == 6}">星期六<br/></c:when>
<c:otherwise>数字错误超出范围<br/></c:otherwise>
</c:choose>
<c:forEach var="i" begin="0" end="14" step="2" varStatus="s">
${i}-${s.count}
</c:forEach>
<br/>
<c:forEach items="${list}" var="str" varStatus="s">
${str}-${s.index}-${s.count}
</c:forEach>
<br/>
</body>
</html>
三层
前后端分离:每次刷新,向服务器发送一次http请求,更新dom
不分离:Ajax也会发送http请求但是dom不摧毁,通过js更新dom.
前后端不分离图用户请求到达servlet,然后调用业务层,完成任务,数据传回servlet,然后根据数据组成jsp页面,传回浏览器。
前后端分离就不需要jsp了,发送Ajax请求,传回json格式数据给浏览器。浏览器根据json数据更新dom
Servlet过滤器
介绍
过滤器是一种程序,先于servlet或Jsp页面运行在服务器中。可以附加到一个或多个servlet或jsp页面上,并且可以检查进入这些资源的请求信息。过滤器是客户端与目标资源间的中间层组件,拦截客户端的请求与相应信息。
服务器判断请求与过滤器对象是否相关联,如果是,交给过滤器处理。
过滤器对象在javax.servlet包中
package myWeb;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(value = "/JSTL1.jsp", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
public class FilterDemo01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter init...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter Demo01 is called .......");
// 直接放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter Demo01 is called 2 ......");
}
@Override
public void destroy() {
System.out.println("Filter destroy...");
}
}
运行顺序:
多过滤器运行顺序
package myWeb;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
//劫持哪个网页
public class FilterDemo02 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter Demo02 is called .......");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter Demo02 is called 2 ......");
}
}
//访问jstl1测试多个过滤器调用顺序
上图可看运行顺序,中间一句时是stl.jsp页面中的语句。
可以用多个过滤器如字符编码,认证等等。
配置
方法一:注解
方法二:web,xml中。
声明过滤器对象:
<filter>标签用于声明过滤器对象,除了上图中出现的子标签还有<init-param>用于设置过滤器的初始化参数。
过滤器请求方式
属于可选择配置,属于上面配置中的可选配置
标签注解都可。注解方式第一段代码中有例子
API
FilterChain接口由容器实现,只包含一个方法:doFIlter(),用于将请求或响应传递给下一个过滤器对象
Servlet监听器
例子
package myWeb;
//监听属性变动,结合listener1.jsp使用
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class ServletContextAttributeDemoListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("Servlet Context: An attribute is set.");
ServletContext context = scae.getServletContext();
String name = scae.getName();
System.out.println(name);
System.out.println(scae.getValue());
if(name.equals("aa")) {//如果添加进来的属性名是这个就删掉
context.removeAttribute(name);
}
}
// @Override
// public void attributeRemoved(ServletContextAttributeEvent scae) {
//
// }
//
// @Override
// public void attributeReplaced(ServletContextAttributeEvent scae) {
//
// }
ServletContextAttributeListener 顾名思义,是专门用来监听ServletContext属性的各种类型监听器之一,下面图片会介绍其他类型。
package myWeb;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
//@WebListener
不需要配置参数
public class ServletContextDemoListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Servlet Context is created...");
ServletContext context = sce.getServletContext();
System.out.println(context);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Servlet Context is destroyed...");
}
}
本例内容为监听到ServletContext 属性设置,符合条件的属性被删掉。
浏览器访问:Listener1.jsp
运行结果如下:刚加上来没来得及打印,就被即时删掉了
监听:上下文,会话,请求
配置很简单
Ajax与json
Ajax在前端课程0141,0142也就是倒数第二节与倒数第三节
JavaScript中的对象:
大括号,逗号隔开
json格式:可以在网站中测试,有错误右边会提示。
还可以是数组:
json格式可以递归,只需要最外层大括号,里面对象名括号括起来。
例子前端实现Ajax后端处理json
Ajax实现可以使用js原生或者使用jQuery或vue。本例中使用jQuery因为简单,学习成本低。
下载jQuery包到项目文件夹下,然后用的时候引入。
首先网页中向后端发送这样的一行数据:
然后后端给前端发送这样的数据,
网页端拿到后端数据后组装成这样添加到标签中:
然后注释掉开始发送其它样式的数据。
前端页面网页核心代码:
<body>
<!--AJAX 将按照以下格式插入DOM-->
<!--<table>
<tr>
<th>一行一列</th>
<th>一行二列</th>
</tr>
<tr>
<th>二行一列</th>
<th>二行二列</th>
</tr>
<tr>
<th>三行一列</th>
<th>三行二列</th>
</tr>
<tr>
<th>四行一列</th>
<th>四行二列</th>
</tr>-->
<!--</table>-->
<button id="btn">Get JSON</button>
<div id="theD"></div>
<script type="text/javascript">
var data = {
text: '第一行'
};
//$是jQueryjQuery语法。相当于dom,ready的时候调用这个函数
$(function(){
$('#btn').click(function(){//当点击时回调这个函数
$.ajax({ //$是jQuery中对象ajax 是function,函数里面是个js对象
url: './data_json',
type: 'post',
data: JSON.stringify(data),//对象转换为字符串类型,才能发送出去
dataType: 'json', //(回来的数据要什么格式) 如果Content-Type没有设定,我们也可以通过设定 dataType来实现。
success: function(data){//请求成功时调用这个函数
$('#theD').append(jsonToHTML(data));//调用了这个方法,下面有介绍,方法返回的结果append到选择的标签中
}
});
});
});//整个块分三次异步调用(什么时候调用,如何调用,当...时调用)
// parse JSON
function jsonToHTML(json){//得到的数据一步步的组装,组装完后就是上面注释中的格式。
console.log(typeof json);
var $table = $('<table></table>');//这是
$(json['LINE']).each(function(){
var $tr = $('<tr></tr>').appendTo($table);
$(this['COLUMN']).each(function(){
var $th = $('<th></th>').html(this['text']).appendTo($tr);
});
});
return $table;
}
---
这段方法的作用是将一个JSON对象转换成一个HTML表格,并返回这个表格。它用到了以下的方法:
console.log(typeof json):这个方法是打印出json对象的类型,例如object或string。
$('<table></table>'):这个方法是使用jQuery库创建一个空的表格元素,并赋值给变量$table。
$(json['LINE']).each(function(){...}):这个方法是遍历json对象中的LINE属性,它是一个数组,每个元素代表一行数据。对于每个元素,执行一个回调函数,参数是当前元素的索引和值。
$('<tr></tr>').appendTo($table):这个方法是创建一个空的行元素,并将它添加到表格元素的末尾,然后返回这个行元素,并赋值给变量$tr。
$(this['COLUMN']).each(function(){...}):这个方法是遍历当前行元素中的COLUMN属性,它也是一个数组,每个元素代表一列数据。对于每个元素,执行一个回调函数,参数是当前元素的索引和值。
$('<th></th>').html(this['text']).appendTo($tr):这个方法是创建一个空的表头单元格元素,并将它的内容设置为当前列元素中的text属性,然后将它添加到行元素的末尾,并返回这个单元格元素,并赋值给变量$th。
//appendTo() 方法在被选元素的结尾插入 HTML 元素。
//$(content).appendTo(selector)
content 必需。规定要插入的内容(必须包含 HTML 标签)。
注意:如果 content 是已存在的元素,它将从当前位置被移除,并在被选元素的结尾被插入。
selector 必需。规定把内容追加到哪个元素上。
-------!!!!!!!!当selector是创建的两个标签时,就是放到两个元素中间。!!!!!!!!!
</script>
</body>
---
是的,jQuery选择语法不仅能选择已有的HTML元素,还能创建新的HTML元素。你可以使用$()函数来创建一个新的HTML元素,并将它作为一个jQuery对象返回。例如,你可以这样创建一个新的<p>元素:
var p=(“<p></p>”); // 创建一个空的<p>元素
你也可以在$()函数中指定元素的内容或属性,例如:
var p=(“<p>Hello, world!</p>”); // 创建一个包含文本的<p>元素
var img=(“<img src=‘logo.png’ alt=‘logo’>”); // 创建一个带有src和alt属性的<img>元素
你可以使用jQuery的方法来操作或插入这些新创建的元素,例如:
$p.css(“color”, “red”); // 设置<p>元素的颜色为红色
$img.appendTo(“body”); // 将<img>元素添加到<body>元素的末尾
----
jQuery 的选择语法是基于 CSS 选择器的,所以它可以根据不同的字符串格式来判断是在选择还是在创建标签。一般来说,有以下几种情况:
如果字符串以<开头,并以>结尾,那么 jQuery 会认为它是一个 HTML 标签,例如<p></p>或<div id="container"></div>,并且会创建一个新的元素。
如果字符串是一个有效的 CSS 选择器,那么 jQuery 会认为它是一个选择器,例如#container或.item,并且会查找匹配的元素。
如果字符串是一个无效的 CSS 选择器,那么 jQuery 会抛出一个语法错误,例如Hello, world!或[name=],并且不会执行任何操作。
后端Servlet程序代码:
package myWeb;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
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.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@WebServlet("/data_json")
public class JSON extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 把AJAX发送过来的请求体变换为字符串
BufferedReader br = req.getReader();
StringBuffer bfr = new StringBuffer();
String line = null;
while((line = br.readLine()) != null){
// System.out.println(line);
bfr.append(line);
}
String inputJson = bfr.toString();
System.out.println(inputJson);
// 2. 返回JSON数据
resp.setContentType("application/json;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
// writer.println("{");
// writer.println(" \"LINE\": [");//斜杠是转义
// writer.println(" { \"COLUMN\": [");
// writer.println(" { \"text\": \"一行一列\" },");
// writer.println(" { \"text\": \"一行二列\" }]},");
// writer.println(" { \"COLUMN\": [");
// writer.println(" { \"text\": \"二行一列\" },");
// writer.println(" { \"text\": \"二行二列\" }]},");
// writer.println(" { \"COLUMN\": [");
// writer.println(" { \"text\": \"三行一列\" },");
// writer.println(" { \"text\": \"三行二列\" }]},");
// writer.println(" { \"COLUMN\": [");
// writer.println(" { \"text\": \"四行一列\" },");
// writer.println(" { \"text\": \"四行二列\" }]}");
// writer.println(" ]");
// writer.println("}");
// 3. json 字符串 =》 Java 对象(引入包中方法帮你做)
// 4. Java 对象 =》 json 字符串
// 3.1/4.1 引入相应的工具包: Jsonlib可以,但是我们用 jackson(spring默认包) 引入包,并且保证包一起被out
三个包版本号要一样,libraries中添加,artifacts中确保在outputlayout里
// 3.2/4.2 建立和Java 对象对应的类
// 3.3/4.3 初始化一个ObjectMapper(引入包里的东西)
ObjectMapper mapper = new ObjectMapper();
// 3.4 json 字符串 =》 Java 对象
// Cell c = mapper.readValue(inputJson, Cell.class);//参数一被转化的,参数二转化为那个类 ,此时数据 text: '第一行'
// System.out.println(c.getText());//输出:第一行
// json 字符串 =》 List<Java对象>
// List<Cell> cs = mapper.readValue(inputJson, new TypeReference<List<Cell>>(){});
// System.out.println(cs.get(0).getText());
//list对应的Ajax发送的数据{ [ {text: '第一行'},{text: '第二行'}]
// json 字符串 =》 Map<String, Java对象>
// Map<String, Cell> cs = mapper.readValue(inputJson, new TypeReference<Map<String, Cell>>(){});
// System.out.println(cs.get("first").getText());
//{first: {text: '第一行'},second:{text: '第二行'}}
// json 字符串 =》 Java的复杂对象
// Column cln = mapper.readValue(inputJson, Column.class);
// System.out.println(cln.getColumn().get(0).getText());
//对应的数据 {column: [ {text: '第一行'}, {text: '第二行'} ]}
// 4.4 Java对象 =》 json 字符串
//写一个table类
Cell c1 = new Cell();
c1.setText("一行一列");
Cell c2 = new Cell();
c2.setText("一行二列");
Column cln = new Column();
cln.getColumn().add(c1);
cln.getColumn().add(c2);
Table tbl = new Table();
tbl.getLine().add(cln);
c1 = new Cell();
c1.setText("二行一列");
c2 = new Cell();
c2.setText("二行二列");
cln = new Column();
cln.getColumn().add(c1);
cln.getColumn().add(c2);
tbl.getLine().add(cln);
c1 = new Cell();
c1.setText("三行一列");
c2 = new Cell();
c2.setText("三行二列");
cln = new Column();
cln.getColumn().add(c1);
cln.getColumn().add(c2);
tbl.getLine().add(cln);
c1 = new Cell();
c1.setText("四行一列");
c2 = new Cell();
c2.setText("四行二列");
cln = new Column();
cln.getColumn().add(c1);
cln.getColumn().add(c2);
tbl.getLine().add(cln);
// writeValueAsString(obj) 返回 String
// writeValue(写到哪里, obj)
// 参数一可以是: File对象 =》用来 写入文件
// 或者 Writer对象 =》 写入字符流流
// 或者 OutputStream对象 =》 写入字节流
String json = mapper.writeValueAsString(tbl);
writer.print(json);
List<Cell> cs = new ArrayList<>();
cs.add(c 1);
cs.add(c2);
System.out.println(mapper.writeValueAsString(cs));
// Annotation: @JsonIgnore 在类添加到属性前面(上一行),帮助忽略属性,使这个属性不会被转化为Json
// @JsonFormat(pattern = "格式")//也是添加到定义属性前,表示parse成某种格式。
// 比如:
//@JsonFormat(pattern = "yyyy-MM-dd")
writer.flush();
writer.close();
}
}
//如果不用别人写好的工具包,需要自己用正则表达式parse成字符串然后在构造类,有挑战也麻烦。
cell类代码:
package myWeb;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Date;
public class Cell {
private String text;
// @JsonIgnore
@JsonFormat(pattern = "yyyy-MM-dd")
private Date date = new Date();
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
Colum类代码
package myWeb;
import java.util.ArrayList;
import java.util.List;
public class Column {
private List<Cell> column = new ArrayList<Cell>();
public List<Cell> getColumn() {
return column;
}
}
Table类代码:
package myWeb;
import java.util.ArrayList;
import java.util.List;
public class Table {
private List<Column> line = new ArrayList<Column>();
public List<Column> getLine() {
return line;
}
}
引入Jackson
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
然后modules importD:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-annotations\2.3.3\jackson-annotations-2.3.3.pom
D:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-core\2.3.3\jackson-core-2.3.3.pom
D:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-databind\2.3.3\jackson-databind-2.3.3.pom
然后project页面就有了,同样的邮件add as Library
登录MD5
package myWeb;
import java.security.MessageDigest;//加密用的库
import java.security.NoSuchAlgorithmException;
public class User {
private String username;
public User(String username){
this.username = username;
}
public String getUsername(){
return this.username;
}
public void setUsername(String username){
this.username = username;
}
protected String md5(String words) throws NoSuchAlgorithmException {
byte[] passwordBytes = MessageDigest.getInstance("md5").digest(words.getBytes());//加密,获得的数据是二进制的
StringBuffer bfr = new StringBuffer();
//一个byte8位(可容纳两个16进制数),一个16进制数占4位
for(byte b: passwordBytes){
int temp = b & 255;//按位与(255二进制:11111111)
if(temp < 16 && temp >= 0) {//小于16,0~F,只有一位,补0
bfr.append("0");
bfr.append(Integer.toHexString(temp));//变成16进制
} else {
bfr.append(Integer.toHexString(temp));
}
}
return bfr.toString();
}
public boolean checkPassword(String password, String salt) {
// 假装去读取了数据库
// aaa: cccc
if(this.username.equals("aaa")){
String passwordMd5 = "";
try {
passwordMd5 = this.md5("cccc");
passwordMd5 = this.md5(passwordMd5 + salt);
} catch (NoSuchAlgorithmException e) {
System.out.println(e.toString());
}
if(password.equals(passwordMd5)){
return true;
}
}
return false;
}
}
在JS中这样使用
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
直接使用MD5方法
let passwordMd5 = md5(passwordElement.value);
passwordMd5 = md5(passwordMd5 + salt);
旅游网站
都是写死的,比较简单,稍微介绍一下
分页domain类
private int totalCount;
private int totalPage;
private int currentPage;
private int pageSize;//一页几条
private List<T> list = new ArrayList<T>();
//用于放route路线
传过来的参数是cid,与当前是第几页,与总页数,并且如果前端传值为空,这三个值是有默认值的,避免前后端互相“死锁”,也就是即时不传参数,后端默认值能够保证前端分页框架不崩溃。默认值:当前数据cid,第0条,当前页面:第一页
想获取本页内容,首先总数量是数据库查出来的。
------
@Override
public Page<Route> pageQuery(int cid, int currentPage, int pageSize) {
Page<Route> pr = new Page<>();
pr.setCurrentPage(currentPage);//传值,或在调用本方法时传默认值
pr.setPageSize(pageSize);
Map<Integer, List<Route>> allRoutes = (Map<Integer, List<Route>>) this.getApplication().getAttribute("allRoutes");
List<Route> routes = allRoutes.get(cid);//写死的,相当于数据库查的,了解
int totalCount = routes.size();//查的数据数量
pr.setTotalCount(totalCount);
int start = (currentPage - 1) * pageSize;//重要,比如一页有五条,第二页的开始下标就是(2-1)*5,第六条数据,下标是5
pr.setList(new ArrayList<Route>());
for(int i = start; i < start + pageSize && i < totalCount; i++){//i同时满足不超过总数量、不超过当前页下标(下标从0开始)
pr.getList().add(routes.get(i));
}
int totalPage = totalCount % pageSize == 0 ? (totalCount / pageSize) : (totalCount / pageSize + 1);
//重点 总页数算法设计,要学会 使用简介的三目运算代替多行判断代码。
pr.setTotalPage(totalPage);
return pr;
}
做了个ServletContextListener,只要上下文一启动,就创建一个USER类将用户名密码set到类中。放入ServletContext中,可以说是写死的,只要服务器一启动,就写死创建一个用户。
注册就是简单的,写死的代码,获取上下文中的user,如果不为空,就返回true,空,返回错误信息。
返回信息类与json格式非常有意义
public class ResultInfo {
private boolean flag;
private String errorMsg;
public ResultInfo() {
}
public boolean isFlag() {return flag;}
public void setFlag(boolean flag) { this.flag = flag;}
public String getErrorMsg() {return errorMsg;}
public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}
}
---以下是这个类的使用,用于返回信息:使用到了jackson
....省略无数行....
ResultInfo info = new ResultInfo();
if(flag){
info.setFlag(true);
} else {
info.setFlag(false);
info.setErrorMsg("注册失败");
}
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
登录
登录就是从上下文里或得到用户名密码检查一下是否相等。
req.getSession().setAttribute("user", u);//登录成功就塞进去,只有登录了才有,如果有些页面非登录用户不给看,可用filter检测session
Route,Category等实体类也一样,做了些假数据放进了上下文,模仿数据库查出来的
"/route/list"这个请求就是把所有List<Category> categories使用Jackson变成json发到前台
WEb后端项目工作流程
idea tips
maven里面的Tomcat太老了 project Structure artifact中里面两个删掉,添加一个webapplication exploded from travel 然后再像往常那样配置Tomcat,beforelaunch,将build移除,建一个Maven的goal cleancompile 然后点击箭头挪到上面一小时32分钟,比较重要,自己看看
Tips:Project settings 中modules可将文件夹设置成sources这样文件夹变成蓝色模样,可以添加Java代码。 2.Project settings facets中web resource directories中可将文件夹添加进去,这样右键可写网页
版权归原作者 听雨笑 所有, 如有侵权,请联系我们删除。