JavaWEB基础知识大览

4,846 阅读16分钟

前言

Java Web是指用Java语言来解决相关Web领域的技术总和,一个Web应用程序包括Web客户端和Web服务器两个部分,即基于B/S(浏览器/服务器)架构的应用程序。

一、两端

在这里插入图片描述

1.1 Web客户端

Web客户端通常是指用户机上的浏览器,如微软的IE浏览器或火狐浏览器等。客户端不需要开发任何用户界面,而统一采用浏览器即可。

1.2 Web服务器

Web服务器是一台或多台可运行Web应用程序的计算机,通常我们在浏览器中输入的网站地址,即Web服务器的地址。当用户在浏览器的地址栏中输入网站地址并按回车键后,请求即被发送到 Web服务器。服务器接收到请求后,会返回给用户带有请求资源的响应消息。Java在服务器端的应用非常丰富,如Servlet、JSP和第三方框架等。

二、两站

2.1 静态网站

早期的Web应用主要是静态页面的浏览,即静态网站。 这些网站使用HTML语言来编写,放在Web服务器上。 用户使用浏览器通过HTTP协议请求服务器上的Web页面,Web服务器处理接收到的用户请求后发送给客户端浏览器显示给用户。工作原理如图: 在这里插入图片描述

2.2 动态网站

用户所访问的资源已不局限于服务器中保存的静态网页。更多的内容需要根据用户的请求动态生成页面信息,即动态网站。 这些网站通常使用HTML语言和动态脚本语言(如JSP、ASP或PHP等)编写,并将编写后的程序部署到Web服务器中。 由Web服务器处理动态脚本代码并将其转换为浏览器可以解析的HTML代码,最后返回客户端浏览器显示给用户,其工作流程如图: 在这里插入图片描述

三、两结构

3.1 C/S结构

客户端则需要安装专用的客户端软件。如图所示: 在这里插入图片描述

3.2 B/S结构

在B/S结构中客户端不需要开发任何用户界面,而统一采用IE或火狐等浏览器。通过Web浏览器向Web服务器发送请求,由Web服务器处理并将处理结果逐级传回客户端,如图所示。 在这里插入图片描述

内置对象

1.Request对象
Request对象是javax.servlet.http.HttpServletRequest类的实例。代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。比如表单中的数据、网页地址后带的参数等。
2.Response对象
Response对象是javax.servlet.http.HttpServletResponse类的实例。代表响应对象,主要用于向客户端发送数据。
3.Out对象
Out对象是javax.servlet.jsp.JspWriter类的实例。主要用于向客户端浏览器输出数据。
4.session对象
Session 对象是javax.servlet.http.HttpSession类的实例。主要用来保持在服务器与一个客户端之间需要保留的数据,比如在会话期间保持用户的登录信息等,会话状态维持是Web应用开发者必须面对的问题。当客户端关闭网站的所有网页或关闭浏览器时,session对象中保存的数据会自动清除。由于Htp协议是一个无状态协议,不保留会话间的数据,因此通过session对象扩展了htp的功能。比如用户登录一个网站之后,登录信息会暂时保存在session对象中,打开不同的页面时,登录信息是可以共享的,一旦用户关闭浏览器或退出登录,就会清除session对象中保存的登录信息。
5.Application对象
Application对象是javax.servlet.ServletContext类的实例。主要用于保存用户信息,代码片段的运行环境;它是一个共享的内置对象,即一个容器中的多个用户共享一个application对象,故其保存的信息被所有用户所共享。
6.PageContext对象
PageContext对象是javax.servlet.jsp.PageContext类的实例。用来管理网页属性,为JSP页面包装页面的上下文,管理对属于JSP中特殊可见部分中已命名对象的访问,它的创建和初始化都是由JSP容器来完成的。
7.Config对象
Config对象是javax.servlet.ServletConfig类的实例。是代码片段配置对象,表示Servlet的配置。
8.Page(相当于this)对象
Page对象是javax.servlet.jsp.HttpJspPage类的实例。用来处理JSP网页,它指的是JSP页面对象本身,或者说代表编译后的servlet对象,只有在JSP页面范围之内才是合法的。
9.Exception对象
Exception对象是java.lang.Throwable类的实例。处理JSP文件执行时发生的错误和异常只有在JSP页面的page指令中指定isErrorPage=“true”后,才可以在本页面使用exception对象。

四、JavaBean

4.1 JavaBean的产生背景

在JSP网页开发的初级阶段并没有框架与逻辑分层概念,需要将Java代码嵌入到网页中处理JSP页面中的一些业务逻辑,如字符串处理和数据库操作等,其开发流程如图所示。 在这里插入图片描述

4.2 JavaBean的作用

如果使HTML与Java代码相分离,将Java代码单独封装成为一个处理某种业务逻辑的类。然后在JSP页面中调用此类,则可以降低HTML与Java代码之间的耦合度,并且简化JSP页面,提高Java程序代码的重用性及灵活性。这种与HTML代码相分离,而使用Java代码封装的类就是一个JavaBean组件。 在Java Web开发可以使用该组件来完成业务逻辑的处理,应用JavaBean与JSP组合的开发模式如图所示。 在这里插入图片描述

4.3 JavaBean的应用

JavaBean是用Java语言所写成的可重用组件,其应用十分广泛,可以应用于系统的很多层中,如PO、VO、DTO和POJO等。

五、Servlet

用户通过单击某个链接或者直接在浏览器的地址栏中输入URL来访问Servlet,Web服务器接收到请求后,并不是将请求直接交给Servlet容器。Servlet容器实例化Servlet,调用Servlet的一个特定方法( service() ),并产生一个响应。这个响应有Servlet容器返回给Web服务器,Web服务器包装这个响应,以HTTP响应的形式发送给Web浏览器。整个过程如图: 在这里插入图片描述

六、如何让服务器知道你来过?

6.1 Cookie技术

Cookie的作用:通俗地说就是当一个用户通过HTTP协议访问一个服务器的时候,这个服务器会将一些Key/Value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户下次访问这个服务器的时候,数据又被完整地带回给服务器。 这个作用就像你去超市购物时,第一次给你办张购物卡,这个购物卡里存放了一些你的个人信息,下次你再来这个连锁超市时,超市会识别你的购物卡,下次直接购物就好了。当初W3C在设计Cookie时实际上考虑的是为了记录用户在一段时间内访问Web应用的行为路径。由于HTTP协议是一种无状态协议,当用户的一次访问请求结束后,后端服务器就无法知道下一次来访问的还是不是上次访问的用户,在设计应用程序时,我们很容易想到两次访问是同一人访问与不同的两个人访问对程序设计和性能来说有很大的不同。例如,在一个很短的时间内,如果与用户相关的数据被频繁访问,可以针对这个数据做缓存,这样可以大大提高数据的访问性能。Cookie的作用正是在此,由于是同一个客户端发出的请求,每次发出的请求都会带有第一次访问时服务端设置的信息,这样服务端就可以根据Cookie值来划分访问的用户了。 在这里插入图片描述

@WebServlet("/CookieTest")
public class CookieTest extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		Cookie[] cookies = request.getCookies();
		
		PrintWriter out = response.getWriter();
		
		if(cookies!=null){
			out.println("上次访问的时间是:");
			for (Cookie cookie : cookies) {
				if("lastTime".equals(cookie.getName())){
					long lastTime = Long.parseLong(cookie.getValue());
					Date date = new Date(lastTime);
					out.println(date.toLocaleString());
				}
			}
		}else{
			out.println("你是第一来");
		}
		
		Cookie cookie = new Cookie("lastTime", String.valueOf(System.currentTimeMillis()));
		//给cookie设置一些信息
		//cookie.setMaxAge(500); //有效期
		//cookie.setPath(uri);
		//服务器端给客户端一个Cookie
		response.addCookie(cookie);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

解码与编码(与Cookie无关,只是解决乱码的一种方式)

//传中文,避免乱码。可以进行编码
Cookie cookie = new Cookie("lastTime",URLEncoder.encode("尚", "utf-8"));
//取出Cookie值,需要解码
URLDecoder.decode(cookie.getValue(),"utf-8");

6.2 Session技术

为什么需要Session?
前面已经介绍了Cookie可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,这无形地增加了客户端与服务端的数据传输量,而Session的出现正是为了解决这个问题。 同一个客户端每次和服务端交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID,这个ID是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NANE为JSESIONID的一个Cookie。 在这里插入图片描述

一个浏览器去服务器租房子,服务器记录一下浏览器的行为和数据,然后给了浏览器一把房间的钥匙 然后,每次浏览器可以使用自己的钥匙去打开自己的房间,使用房间的所有东西。(当然,你不能去开别人的房间,何况也打不开)


七、上下文

7.1 SeveletContext或者ApplicationContext的由来

浏览器想锻炼身体,愉悦心情。服务器心想我不能给你们每一个人的房间增加一套体育设备吧,那我的经济压力多大。服务器想了想,决定建设一个公开场所,体育馆,所有浏览器都可以使用这些共享资源

@WebServlet("/SessionTest1")
public class SessionTest extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		out.write("Session的ID为:");
		// 获取 Session 
		HttpSession session = request.getSession();
		out.write(session.getId());
		session.setAttribute("name", "shang");
		//设置当前会话多久结束,单位秒。如果设置的值为零或负数,则表示会话将永远不会超时。常用于设置当前会话时间。
//		session.setMaxInactiveInterval(1);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
@WebServlet("/SessionTest2")
public class SessionTest2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		
		HttpSession session = request.getSession();
		//移除session的数据
		session.removeAttribute("name");
		//手动注销当前会话
		session.invalidate();
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

7.2 Servlet上下文

运行在Java虚拟机中的每一个Web应用程序都有一个与之相关的Servlet上下文。Java Servlet API提供了一个ServletContext接口用来表示上下文。在这个接口中定义了一组方法,Servlet可以使用这些方法与它的Servlet容器进行通信,例如,得到文件的MIME类型,转发请求,或者向日志文件中写入日志消息。 ServletContext对象是Web服务器中的一个已知路径的根。 比如,Servlet上下文被定位于http://localhost:8080/ch02。以/ch02请求路径(称为上下文路径)开始的所有请求被发送到与此ServletContext关联的Web应用程序。再比如,我们平常使用的http://localhost:8080/。以/请求路径(称为上下文路径)开始的所有请求被发送到与此ServletContext关联的Web应用程序。

在这里插入图片描述 ServletContext: 这个是来自于servlet规范里的概念,它是servlet用来与容器间进行交互的接口的组合,也就是说,这个接口定义了一系列的方法,servlet通过这些方法可以很方便地与自己所在的容器进行一些交互。在一个应用中(一个JVM),servlet容器可以有多个,而所有的servlet容器共享一个ServletContext。 在这里插入图片描述

八、两个时代

8.1 Model1时代

最初的JSP开发模式为Model 1模式:JSP+JavaBean 在这里插入图片描述

8.2 Model2时代

慢慢演变成了Model 2模式:JSP+Servlet+JavaBean

模型2符合MVC架构模式,MVC即模型-视图-控制器(Model-View-Controller)。

  • 模型代表应用程序的数据以及用于访问控制和修改这些数据的业务规则。当模型发生改变时,它会通知视图,并为视图提供查询模型相关状态的能力。同时,它也为控制器提供访问封装在模型内部的应用程序功能的能力。
  • 视图用来组织模型的内容。它从模型那里获得数据并指定这些数据如何表现。当模型变化时,视图负责维护数据表现的一致性。视图同时将用户的请求通知控制器。
  • 控制器定义了应用程序的行为。它负责对来自视图的用户请求进行解释,并把这些请求映射成相应的行为,这些行为由模型负责实现。在独立运行的GUI客户端,用户的请求可能是一些鼠标单击或是菜单选择操作。在一个Web应用程序中,它们的表现形式可能是一些来自客户端的GET或POST的HTTP请求。模型所实现的行为包括处理业务和修改模型的状态。根据用户请求和模型行为的结果,控制器选择一个视图作为对用户请求的响应。如图所示: 在这里插入图片描述 在这里插入图片描述

九、文件的上传下载

9.1 文件的上传

  1. 导入jar包commons-io.jarcommons-fileupload.jar
  2. 表单一定要标记enctype="multipart/form-data"
  <%--
  ${pageContext.request.contextPath}:保证我发布在项目在服务器上也能被访问
  --%>

  <form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
    上传用户:<input type="text" name="username"><br/>
    <input type="file" name="file1"><br/>
    <input type="submit" value="提交"> | <input type="reset" value="重置">
  </form>
  1. ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象, 在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。 所以,我们需要在进行解析工作前构造好DiskFileItemFactory()对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
package com.shang.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.UUID;

public class FileServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //判断上传的表单是普通表单还是带文件的表单
        if(!ServletFileUpload.isMultipartContent(request)){
            return;
        }
        //创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if(!uploadFile.exists()){
            uploadFile.mkdir();
        }

        //缓存,临时文件
        //临时路径,假如文件超出了预期的大小,我们就把他放到一个临时文件中,过几天自动删除,或者提醒用户转存为永久
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if(!tmpFile.exists()){
            tmpFile.mkdir();
        }

        //处理上传的文件,一般都需要通过流来获取,我们可以使用request.getInputStream(),原生态的文件上传流获取,十分麻烦。但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
        //1. 创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的
        DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
        //2.获取ServletFileUpload
        ServletFileUpload upload = getServletFileUpload(factory);
        //3.处理上传的文件
        String msg = null;
        try {
            msg = uploadParseRequest(upload, request, response, uploadPath);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
        //servlet请求转发消息
        request.setAttribute("msg", msg);
        request.getRequestDispatcher("info.jsp").forward(request, response);

    }

    protected void doGet( HttpServletRequest request,  HttpServletResponse response) throws  ServletException, IOException {  
 }

    public  static DiskFileItemFactory getDiskFileItemFactory(File file){
        //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(1024 * 1024);//缓冲区大小为1M
        factory.setRepository(file);//临时目录的保存目录,需要一个File
        return factory;
    }

    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
        ServletFileUpload upload = new ServletFileUpload(factory);
        //监听文件上传进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            //pBytesRead:已经读取到的文件大小
            //pContentLength:文件大小
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
            }
        });
        //处理乱码问题
        upload.setHeaderEncoding("UTF-8");
        //设置单个文件的最大值
        upload.setFileSizeMax(1024 * 1024 * 10);
        //设置总共能够上传文件的大小
        //1024 = 1kb * 1024 =1M * 10 =10M
        upload.setSizeMax(1024 * 1024 * 10);
        return upload;
    }

    public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,HttpServletResponse response,String uploadPath) throws FileUploadException, IOException {
        String msg = "";

        //把前端请求解析,封装成一个FileItem对象
        List<FileItem> fileItems = upload.parseRequest(request);
        for (FileItem fileItem : fileItems) {
            if(fileItem.isFormField()){ //判断上传文件是普通表单还是带文件的表单
                //getFiledName指的是前端表单控件的name
                String name = fileItem.getFieldName();
                String value = fileItem.getString("UTF-8"); //处理乱码
                System.out.println(name+":"+value);
            }else {//判断它是上传文件
                //========处理文件======
                String uploadFileName = fileItem.getName();
                System.out.println("上传的文件名:"+uploadFileName);

                if(uploadFileName.trim().equals("")||uploadFileName==null){
                    continue;
                }
                //获取上传的文件名 /images/boy/cool.jpg
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                //获取文件的后缀
                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                /*
                *  如果文件后缀名 fileExtName 不是我们所需要的,
                *  就直接return,不处理,告诉用户文件类型不对
                * */
                System.out.println("文件信息 [件名:"+fileName+"----文件类型"+fileExtName+"]");

                //可以使用UUID(唯一识别的通用码),保证文件的唯一
                //,UUID.randomUUID(),随机生成一个唯一识别的通用码
                String uuidPath = UUID.randomUUID().toString();

                //===========================处理文件完毕========
                //存到哪? uploadPath
                //文件真实存在的路径 realPath
                String realPath = uploadPath +"/"+uuidPath;
                //给每个文件创建一个对应的文件夹
                File realPathFile = new File(realPath);
                if(!realPathFile.exists()){
                    realPathFile.mkdir();
                }

                //存放地址完毕

                //=======文件传输=====
                //获取文件的上传流
                InputStream inputStream = fileItem.getInputStream();
                //创建文件输出流
                //realPath 真实的文件夹
                //差一个文件;加上输出的名字+"/"+uuidFileName
                FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);

                //判断是否读取完毕
                int len = 0;
                //创建一个缓冲区
                byte[] buffer = new byte[1024*1024];
                //如果不等于-1说明还存在数据
                while((len = buffer.length)!= -1){
                    fos.write(buffer, 0, len);
                }

                //关闭流
                fos.close();
                inputStream.close();
                msg = "文件上传成功!";
                fileItem.delete();// 上传成功,清除临时文件
            }
        }
        return msg;
    }
}

9.2 文件的下载

public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取下载文件的路径
        //String path = this.getServletContext().getRealPath("/1.jpg");
        String path = "G:\\MyIDEProject\\JavaWeb\\UploadFile\\resources\\1.jpg";
        System.out.println("要下载的文件路径"+path);
        //2.下载的文件名
        String filename = path.substring(path.lastIndexOf("\\") + 1);
        //3.设置让浏览器支持我们要下载的东西
        response.setHeader("Content-Disposition", "attachment;filename="+filename);
        //4.获取下载文件的输入流
        File file = new File(path);
        FileInputStream fis = new FileInputStream(file);
        //5.创建缓冲区
        byte[] buffer = new byte[1024];
        int len = 0;
        //6.获取OutputStream对象
        ServletOutputStream fos = response.getOutputStream();
        //7.将FileOutputStream流写入到buffer缓冲区
        while((len = buffer.length)!=-1){
            //8.使用OutputStream将缓冲区中的数据输入到客户端
            fos.write(buffer, 0, len);
        }
        //9. 关闭流
        fos.close();
        fis.close();
    }
}