JAVA WEB部分
Web开发 端口
不同的进程有不同的端口号,用来区分不同的软件
范围:0~65535
TCP,UDP分别由65536个端口号,两个协议的端口不冲突
端口分类:
公有端口0~1023:
http:80
https:443
ftp:21
telent:23
程序注册端口:1024~49151
Tomcat:8080
MySQL:3306
Oracle:1521
动态、私有:49152~65535
1 2 3 netstat -ano # 查看所有端口 netstat -ano|findstr "5900" #查看指定端口 tasklist|findstr "8696" #查看指定端口的进程
Web基础 对于Browser来说,请求页面的流程如下:
与服务器建立TCP连接;
发送HTTP请求;
收取HTTP响应,然后把网页在浏览器中显示出来。
浏览器发送的HTTP请求如下:
1 2 3 4 5 GET / HTTP/1.1 Host: www.sina.com.cn User-Agent: Mozilla/5.0 xxx Accept: */* Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8
其中,第一行表示使用GET
请求获取路径为/
的资源,并使用HTTP/1.1
协议,从第二行开始,每行都是以Header: Value
形式表示的HTTP头,比较常用的HTTP Header包括:
Host: 表示请求的主机名,因为一个服务器上可能运行着多个网站,因此,Host表示浏览器正在请求的域名;
User-Agent: 标识客户端本身,例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79
,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko
;
Accept:表示浏览器能接收的资源类型,如text/*
,image/*
或者*/*
表示所有;
Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate, br
。
HTTP目前有多个版本,1.0
是早期版本,浏览器每次建立TCP连接后,只发送一个HTTP请求并接收一个HTTP响应,然后就关闭TCP连接。由于创建TCP连接本身就需要消耗一定的时间,因此,HTTP 1.1允许浏览器和服务器在同一个TCP连接上反复发送、接收多个HTTP请求和响应 ,这样就大大提高了传输效率。
我们注意到HTTP协议是一个请求-响应协议,它总是发送一个请求,然后接收一个响应。能不能一次性发送多个请求,然后再接收多个响应呢?HTTP 2.0可以支持浏览器同时发出多个请求 ,但每个请求需要唯一标识,服务器可以不按请求的顺序返回多个响应,由浏览器自己把收到的响应和请求对应起来。可见,HTTP 2.0进一步提高了传输效率,因为浏览器发出一个请求后,不必等待响应,就可以继续发下一个请求。
HTTP 3.0为了进一步提高速度,将抛弃TCP协议,改为使用无需创建连接的UDP协议 ,目前HTTP 3.0仍然处于实验阶段。
TCP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.io.IOException;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class Server { public static void main (String[] args) throws IOException { ServerSocket ss = new ServerSocket(8080 ); System.out.println("server is running..." ); for (; ; ) { Socket sock = ss.accept(); System.out.println("connected from " + sock.getRemoteSocketAddress()); Thread t = new Handler(sock); t.start(); } } } class Handler extends Thread { Socket sock; public Handler (Socket sock) { this .sock = sock; } public void run () { try (InputStream input = this .sock.getInputStream()) { try (OutputStream output = this .sock.getOutputStream()) { handle(input, output); } } catch (Exception e) { try { this .sock.close(); } catch (IOException ioe) { } System.out.println("client disconnected." ); } } private void handle (InputStream input, OutputStream output) throws IOException { System.out.println("Process new http request..." ); var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)); boolean requestOk = false ; String first = reader.readLine(); if (first.startsWith("GET / HTTP/1." )) { requestOk = true ; } for (; ; ) { String header = reader.readLine(); if (header.isEmpty()) { break ; } System.out.println(header); } System.out.println(requestOk ? "Response OK" : "Response Error" ); if (!requestOk) { writer.write("HTTP/1.0 404 Not Found\r\n" ); writer.write("Content-Length: 0\r\n" ); writer.write("\r\n" ); writer.flush(); } else { String data = "<html><body><h1>Hello, world!</h1></body></html>" ; int length = data.getBytes(StandardCharsets.UTF_8).length; writer.write("HTTP/1.0 200 OK\r\n" ); writer.write("Connection: close\r\n" ); writer.write("Content-Type: text/html\r\n" ); writer.write("Content-Length: " + length + "\r\n" ); writer.write("\r\n" ); writer.write(data); writer.flush(); } } }
聊天应用 Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class TcpServerDemo1 { public static void main (String[] args) { try (ServerSocket serverSocket = new ServerSocket(9999 )) { try (Socket socket = serverSocket.accept()) { try (InputStream inputStream = socket.getInputStream()) { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();) { byte [] buffer = new byte [1024 ]; int len; while ((len = inputStream.read(buffer)) != -1 ) { byteArrayOutputStream.write(buffer, 0 , len); } String s = byteArrayOutputStream.toString(); System.out.println(s); } } catch (IOException e) { e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } } }
Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException;import java.nio.charset.StandardCharsets;public class TcpClientDemo1 { public static void main (String[] args) { try { InetAddress iadress = InetAddress.getByName("127.0.0.1" ); int port = 9999 ; Socket socket = new Socket(iadress, port); OutputStream outputStream = socket.getOutputStream(); outputStream.write("欢迎你" .getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { e.printStackTrace(); } } }
传文件 server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mmm.tcp;import java.io.FileOutputStream;import java.io.FilterOutputStream;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class TcpServerDemo2 { public static void main (String[] args) { try (ServerSocket serverSocket = new ServerSocket(9999 )) { try (Socket socket = serverSocket.accept()){ try (InputStream inputStream = socket.getInputStream()){ try (FileOutputStream fileOutputStream = new FileOutputStream("test.iml" )){ inputStream.transferTo(fileOutputStream); } } } } catch (Exception e) { e.printStackTrace(); } } }
Client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mmm.tcp;import java.io.FileInputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;public class TcpClientDemo2 { public static void main (String[] args) { try (Socket socket = new Socket(InetAddress.getByName("127.0.0.1" ), 9999 )) { try (OutputStream outputStream = socket.getOutputStream()) { try (FileInputStream fileInputStream = new FileInputStream("kuangDemo.iml" )) { fileInputStream.transferTo(outputStream); } } } catch (Exception e) { e.printStackTrace(); } } }
UDP 消息发送 Client
1 2 3 4 5 6 7 8 9 10 11 12 13 public class UdpClient { public static void main (String[] args) { try (DatagramSocket datagramSocket = new DatagramSocket()) { String msg = "你好啊" ; InetAddress inetAddress = InetAddress.getByName("localhost" ); int port = 9090 ; DatagramPacket packet = new DatagramPacket(msg.getBytes(StandardCharsets.UTF_8), 0 , msg.getBytes(StandardCharsets.UTF_8).length, inetAddress, port); datagramSocket.send(packet); } catch (Exception e) { e.printStackTrace(); } } }
Server
1 2 3 4 5 6 7 8 9 10 11 12 public class UdpServer { public static void main (String[] args) { try (DatagramSocket datagramSocket = new DatagramSocket(9090 )){ byte [] bytes = new byte [1024 ]; DatagramPacket datagramPacket = new DatagramPacket(bytes, 0 , bytes.length); datagramSocket.receive(datagramPacket); System.out.println(new String(datagramPacket.getData(),0 ,datagramPacket.getLength())); }catch (Exception e){ e.printStackTrace(); } } }
聊天 TalkReiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class TalkReceiver implements Runnable { DatagramSocket datagramSocket = null ; private int port; private String msgFrom; public TalkReceiver (int port,String msgFrom) { this .port = port; this .msgFrom = msgFrom; try { datagramSocket = new DatagramSocket(this .port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run () { try { while (true ) { byte [] buffer = new byte [1024 ]; DatagramPacket datagramPacket = new DatagramPacket(buffer, 0 , buffer.length); datagramSocket.receive(datagramPacket); System.out.println(msgFrom+":" +new String(datagramPacket.getData())); } } catch (Exception e) { e.printStackTrace(); } } }
TalkSender
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class TalkSender implements Runnable { DatagramSocket datagramSocket = null ; BufferedReader bufferedReader = null ; int fromPort; int toPort; String toIp; public TalkSender (int fromPort, String toIp, int toPort) { this .fromPort = fromPort; this .toPort = toPort; this .toIp = toIp; try { datagramSocket = new DatagramSocket(this .fromPort); bufferedReader = new BufferedReader(new InputStreamReader(System.in)); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run () { try { while (true ) { String data = bufferedReader.readLine(); byte [] datas = data.getBytes(StandardCharsets.UTF_8); DatagramPacket datagramPacket = new DatagramPacket(datas, 0 , datas.length, InetAddress.getByName(this .toIp), this .toPort); datagramSocket.send(datagramPacket); } }catch (Exception e){ e.printStackTrace(); } } }
Student
1 2 3 4 5 6 public class Student { public static void main (String[] args) { new Thread(new TalkSender(4444 ,"localhost" ,6666 )).start(); new Thread(new TalkReceiver(8888 ,"老师" )).start(); } }
Teacher
1 2 3 4 5 6 public class Teacher { public static void main (String[] args) { new Thread(new TalkSender(9999 ,"localhost" ,8888 )).start(); new Thread(new TalkReceiver(6666 ,"老师" )).start(); } }
URL 1 2 3 4 5 6 7 8 9 10 11 12 public class UrlDown { public static void main (String[] args) throws Exception { URL url = new URL("http://xuexuan.site/images/avatar.png" ); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try (InputStream inputStream = urlConnection.getInputStream()) { try (FileOutputStream os = new FileOutputStream("avatar.png" )){ inputStream.transferTo(os); } } urlConnection.disconnect(); } }
Servlet 基础 1.Maven:注意打包方式为war,dependencies中的scope为provided
,确保只在编译时使用,不会打包到war文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.itranswarp.learnjava</groupId > <artifactId > web-servlet-hello</artifactId > <packaging > war</packaging > <version > 1.0-SNAPSHOT</version > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <maven.compiler.source > 11</maven.compiler.source > <maven.compiler.target > 11</maven.compiler.target > <java.version > 11</java.version > </properties > <dependencies > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.0</version > <scope > provided</scope > </dependency > </dependencies > <build > <finalName > hello</finalName > </build > </project >
2.最简单的Servlet:类必须继承自HttpServlet,然后通过覆写doGet或doPost来实现请求处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @WebServlet(urlPatterns = "/") public class HelloServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html" ); PrintWriter pw = resp.getWriter(); pw.write("<h1>Hello, world!</h1>" ); pw.flush(); } }
3.之后,需要在工程目录下src/main/webapp/WEB-INF
目录下,创建一个web.xml
描述文件。文件内容可以固定如下:
1 2 3 4 5 6 <!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> </web-app>
4.需要先启动web服务器,然后运行war,让服务器加载Servlet来处理请求。
常用的支持Servlet API的Web服务器有:Tomcat,Jetty,GlassFish
下载Tomcat 后,解压,将打包好的hello.war
复制到webapps目录下,然后进入bin目录,执行startup文件。
通过localhost:8080/hello
便可以进行网站的访问。
环境快速搭建
Idea 可以直接在Configuration中进行配置
Tomcat实际也是Java程序,启动流程如下:
启动JVM并执行Tomcat的main()
方法;
加载war并初始化Servlet;
正常服务。
可以将Tomcat的jar包全部引进来,然后自己编写一个main
方法,先启动Tomcat,然后加载webapp
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.itranswarp.learnjava</groupId > <artifactId > web-servlet-embedded</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <maven.compiler.source > 11</maven.compiler.source > <maven.compiler.target > 11</maven.compiler.target > <java.version > 11</java.version > <tomcat.version > 9.0.26</tomcat.version > </properties > <dependencies > <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-core</artifactId > <version > ${tomcat.version}</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-jasper</artifactId > <version > ${tomcat.version}</version > <scope > provided</scope > </dependency > </dependencies > </project >
Tomcat中包含了Servlet API引用
main方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Main { public static void main (String[] args) throws Exception { Tomcat tomcat = new Tomcat(); tomcat.setPort(Integer.getInteger("port" , 8080 )); tomcat.getConnector(); Context ctx = tomcat.addWebapp("" , new File("src/main/webapp" ).getAbsolutePath()); WebResourceRoot resources = new StandardRoot(ctx); resources.addPreResources( new DirResourceSet(resources, "/WEB-INF/classes" , new File("target/classes" ).getAbsolutePath(), "/" )); ctx.setResources(resources); tomcat.start(); tomcat.getServer().await(); } }
下载
进阶 HttpServletRequest HttpServletRequest
封装了一个HTTP请求,它实际上是从ServletRequest
继承而来。最早设计Servlet时,设计者希望Servlet不仅能处理HTTP,也能处理类似SMTP等其他协议,因此,单独抽出了ServletRequest
接口,但实际上除了HTTP外,并没有其他协议会用Servlet处理,所以这是一个过度设计。
我们通过HttpServletRequest
提供的接口方法可以拿到HTTP请求的几乎全部信息,常用的方法有:
getMethod():返回请求方法,例如,"GET"
,"POST"
;
getRequestURI():返回请求路径,但不包括请求参数,例如,"/hello"
;
getQueryString():返回请求参数,例如,"name=Bob&a=1&b=2"
;
getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数;
getContentType():获取请求Body的类型,例如,"application/x-www-form-urlencoded"
;
getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串""
;
getCookies():返回请求携带的所有Cookie;
getHeader(name):获取指定的Header,对Header名称不区分大小写;
getHeaderNames():返回所有Header名称;
getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body;
getReader():和getInputStream()类似,但打开的是Reader;
getRemoteAddr():返回客户端的IP地址;
getScheme():返回协议类型,例如,"http"
,"https"
;
HttpServletResponse HttpServletResponse
封装了一个HTTP响应。由于HTTP响应必须先发送Header,再发送Body,所以,操作HttpServletResponse
对象时,必须先调用设置Header的方法,最后调用发送Body的方法。
常用的设置Header的方法有:
setStatus(sc):设置响应代码,默认是200
;
setContentType(type):设置Body的类型,例如,"text/html"
;
setCharacterEncoding(charset):设置字符编码,例如,"UTF-8"
;
setHeader(name, value):设置一个Header的值;
addCookie(cookie):给响应添加一个Cookie;
addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header;
写入完毕后调用flush是必须的,因为大部分Web服务器都基于HTTP/1.1协议,会复用TCP连接。
写入完毕后不要调用close
重定向与转发 重定向 1 2 3 4 5 6 7 8 9 10 @WebServlet(urlPatterns = "/hi") public class RedirectServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name" ); String redirectToUrl = "/hello" + (name == null ? "" : "?name=" + name); resp.sendRedirect(redirectToUrl); } }
重定向有两种:一种是302响应,称为临时重定向,一种是301响应,称为永久重定向。两者的区别是,如果服务器发送301永久重定向响应,浏览器会缓存/hi
到/hello
这个重定向的关联,下次请求/hi
的时候,浏览器就直接发送/hello
请求了。
redirect()
方法用于实现302重定向。
301重定向的实现方式:
1 2 resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); resp.setHeader("Location" ,"/hello" );
Forward Forward指内部转发。一个Servlet将请求转发给另一个Servlet。
1 2 3 4 5 6 @WebServlet(urlPatterns="/morning") public class ForwardServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/hello" ).forward(req, resp); } }
使用Session和Cookie Session 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @WebServlet(urlPatterns="/signin") public class SignInServlet extends HttpServlet { private Map<String, String> users = Map.of("bob" , "bob123" , "alice" , "alice123" , "tom" , "tomcat" ); protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html" ); PrintWriter pw = resp.getWriter(); pw.write("<h1>Sign In</h1>" ); pw.write("<form action=\"/signin\" method=\"post\">" ); pw.write("<p>Username: <input name=\"username\"></p>" ); pw.write("<p>Password: <input name=\"password\" type=\"password\"></p>" ); pw.write("<p><button type=\"submit\">Sign In</button> <a href=\"/\">Cancel</a></p>" ); pw.write("</form>" ); pw.flush(); } protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("username" ); String password = req.getParameter("password" ); String expectedPassword = users.get(name.toLowerCase()); if (expectedPassword != null && expectedPassword.equals(password)) { req.getSession().setAttribute("user" , name); resp.sendRedirect("/" ); } else { resp.sendError(HttpServletResponse.SC_FORBIDDEN); } } } @WebServlet(urlPatterns = "/") public class IndexServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String user = (String) req.getSession().getAttribute("user" ); resp.setContentType("text/html" ); resp.setCharacterEncoding("UTF-8" ); resp.setHeader("X-Powered-By" , "JavaEE Servlet" ); PrintWriter pw = resp.getWriter(); pw.write("<h1>Welcome, " + (user != null ? user : "Guest" ) + "</h1>" ); if (user == null ) { pw.write("<p><a href=\"/signin\">Sign In</a></p>" ); } else { pw.write("<p><a href=\"/signout\">Sign Out</a></p>" ); } pw.flush(); } } @WebServlet(urlPatterns = "/signout") public class SignOutServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().removeAttribute("user" ); resp.sendRedirect("/" ); } }
JSP开发 JSP(Java Server Pages),文件必须放到/src/main/webapp
下,文件名必须以.jsp
结尾,文件与HTML的差别不大,但是需要插入变量,或者动态输出的地方,使用特殊指令<%...%>
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <html> <head> <title>Hello World - JSP</title> </head> <body> <%-- JSP Comment --%> <h1>Hello World!</h1> <p> <% out.println("Your IP address is " ); %> <span style="color:red" > <%= request.getRemoteAddr() %> </span> </p> </body> </html>
整个JSP的内容实际上是一个HTML,但是稍有不同:
包含在<%--
和--%>
之间的是JSP的注释,它们会被完全忽略;
包含在<%
和%>
之间的是Java代码,可以编写任意Java代码;
如果使用<%= xxx %>
则可以快捷输出一个变量的值。
JSP页面内置的几个变量:
out:表示HttpServletResponse的PrintWriter
session:表示当前的HttpSession对象。
request:表示HttpSevletRequest对象。
JSP可以通过page
指令
1 2 <%@ page import ="java.io.*" %> <%@ page import ="java.util.*" %>
这样后续的Java代码才能引用简单类名而不是完整类名。
使用include
指令可以引入另一个JSP文件:
1 2 3 4 5 6 <html> <body> <%@ include file="header.jsp" %> <h1>Index Page</h1> <%@ include file="footer.jsp" %> </body>
MVC开发
Servlet适合编写Java代码,实现各种复杂的业务逻辑,但不适合输出复杂的HTML;
JSP适合编写HTML,并在其中插入动态内容,但不适合编写复杂的Java代码。
将两者结合起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class User { public long id; public String name; public School school; } public class School { public String name; public String address; } @WebServlet(urlPatterns = "/user") public class UserServlet extends HttpServlet { protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { School school = new School("No.1 Middle School" , "101 South Street" ); User user = new User(123 , "Bob" , school); req.setAttribute("user" , user); req.getRequestDispatcher("/WEB-INF/user.jsp" ).forward(req, resp); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <%@ page import ="com.itranswarp.learnjava.bean.*" %> <% User user = (User) request.getAttribute("user" ); %> <html> <head> <title>Hello World - JSP</title> </head> <body> <h1>Hello <%= user.name %>!</h1> <p>School Name: <span style="color:red" > <%= user.school.name %> </span> </p> <p>School Address: <span style="color:red" > <%= user.school.address %> </span> </p> </body> </html>
把user.jsp
放到/WEB-INF/
目录下,是因为WEB-INF
是一个特殊目录,Web Server会阻止浏览器对WEB-INF
目录下任何资源的访问,这样就防止用户通过/user.jsp
路径直接访问到JSP页面;
我们把UserServlet
看作业务逻辑处理,把User
看作模型,把user.jsp
看作渲染,这种设计模式通常被称为MVC:Model-View-Controller,即UserServlet
作为控制器(Controller),User
作为模型(Model),user.jsp
作为视图(View),整个MVC架构如下:
1 2 3 4 5 6 7 8 9 10 11 12 ┌───────────────────────┐ ┌────>│Controller: UserServlet│ │ └───────────────────────┘ │ │ ┌───────┐ │ ┌─────┴─────┐ │Browser│────┘ │Model: User│ │ │<───┐ └─────┬─────┘ └───────┘ │ │ │ ▼ │ ┌───────────────────────┐ └─────│ View: user.jsp │ └───────────────────────┘
使用MVC模式的好处是,Controller专注于业务处理,它的处理结果就是Model。Model可以是一个JavaBean,也可以是一个包含多个对象的Map,Controller只负责把Model传递给View,View只负责把Model给“渲染”出来,这样,三者职责明确,且开发更简单,因为开发Controller时无需关注页面,开发View时无需关心如何创建Model。
MVC模式广泛地应用在Web页面和传统的桌面程序中,我们在这里通过Servlet和JSP实现了一个简单的MVC模型,但它还不够简洁和灵活,后续我们会介绍更简单的Spring MVC开发。
MVC高级开发
https://www.liaoxuefeng.com/wiki/1252599548343744/1337408645759009
直接把MVC搭在Servlet和JSP之上还是不太好,原因如下:
Servlet提供的接口仍然偏底层,需要实现Servlet调用相关接口;
JSP对页面开发不友好,更好的替代品是模板引擎;
业务逻辑最好由纯粹的Java类实现,而不是强迫继承自Servlet。
通过普通的Java类实现MVC的Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class UserController { @GetMapping("/signin") public ModelAndView signin () { ... } @PostMapping("/signin") public ModelAndView doSignin (SignInBean bean) { ... } @GetMapping("/signout") public ModelAndView signout (HttpSession session) { ... } }
需要进一步的深入研究
使用Filter