Java Web

Java Web

小羽

8 minute read

I. 简介

互联网在我们的日常生活中已经无处不在,例如我们时常在电商网站上买东西,每个人有自己的购物车,有自己的收藏夹;我们使用社交网络网站时,每个人有自己的关注、订阅已经收藏内容等。这一切都的应用都是基于动态web的技术上。不同于仅仅用html, css和js开发的静态web网站,动态web网站提供给不同用户浏览的是不同的数据。而Java Web正是Java技术中用于动态web资源开发的的技术。

定义:Java Web,使用Java技术来解决相关web互联网领域,尤其是动态web资源开发的技术栈。web由web服务器web客户端两个部分组成。Java在客户端的应用有Java Applet,不过使用的很少。但是在服务器端,Java的应用十分丰富,比如Servlet, JSP, 第三方框架等。

javaweb_原理

动态web的运作过程往往如下:

  1. 用户在客户端向服务器端发送一个请求(Request)(比如请求查看一个网页)
  2. 服务器的WebServer Plugin部分接收到客户端的请求后,如果是静态资源请求,那么直接由WebServer从服务器中将静态资源提取出来;如果是动态资源请求,由Servlet/JSP将动态资源从数据库中提取并处理好并交给WebServer(比如数据库中某个用户的详细信息)
  3. WebServer将得到的静态/动态资源作为响应(Response)发送给客户端的用户

所以说,整个流程我们需要有一个客户端、一个服务器端。而服务器端又分为Web Server Plugin, WebServer, Servlet/JSP, 静态资源和数据库这些部分。其中Web Server Plugin部分不需要考虑;而静态资源就是html、css、js、图片、音频、视频等文件;数据库各式各样,比如MySQL, Oracle, Postgresql等,由JDBC将Java项目和这些数据库连接起来,不是本文讨论的主要内容;至于WebServer,市面上有非常成熟的产品,如ASP和Tomcat。另外,在客户端与服务器端传输中我们会用到Http协议,而Web开发中,由于要导入大量的jar包文件(Servlet包、jsp包、数据库驱动包等等),而Maven技术正是方便我们导包的实用工具,本文第II部分会详细介绍它们。所以,Web开发主要是针对Servlet/JSP,以及后台Servlet和数据库之间交互的一些算法逻辑,本文的第III部分会详细介绍他们。而文章的IV部分会详细介绍目前非常成熟且实用的MVC开发架构。第V部分会介绍一些实用的用于数据传输的小工具Ajax和Json。第VI部分中,我会详细介绍一些在Java Web开发中容易踩的一些坑和一些小技巧。

II. 相关开发工具

1. Tomcat

Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,因为Tomcat 技术先进、性能稳定,而且免费,因 而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和 并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个Java初学web的人 来说,它是最佳的选择。

Tomcat 实际上运行JSP 页面和Servlet。其端口默认为8080。

Tomcat使用起来非常简单。我们只需要在其官网下载它并安装/解压。接着在项目中配置好,就可以启动了。

2. HTTP

HTTP(超文本传输协议)是一个简单的通常运行在TCP之上请求-响应协议。超文本主要指的是图片、音乐、视频、定位、地图等等。其端口默认为80。而更加安全的HTTPs的默认端口号为443。

Http主要有两个时代。HTTP1.0(HTTP/1.0)时代时,客户端与web服务器连接后只能获得一个web资源,之后便断开连接;HTTP2.0时代(HTTP/1.1),客户端可以与web服务器连接后,可以获得多个web资源。

HTTP分为请求和响应

HTTP请求由客户端发起,服务器端接收。请求的方式有GET, POST, DELETE, PUT, TRACT… 其中最重要的为GET和POST请求:

  • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
  • post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

HTTP响应由服务器端发起,客户端接收。HTTP响应携带响应状态码,其中重要的有:

  • 200:请求响应成功 200
  • 3xx:请求重定向 (重定向:你重新到我给你新位置去;)

  • 4xx:找不到资源(404:资源不存在)

  • 5xx:服务器代码错误 500 (502:网关错误)

此外,HTTP请求和响应还携带请求消息头和响应体。内容主要包括所支持的数据类型、编码格式、语言、连接状态、主机等等信息。

在浏览器中地址栏输入地址并回车的一瞬间,浏览器首先会去寻找本地有无此域名,如果没有,就在网络上寻找。

3. Maven

在Java开发中,我们时长要导入各种各样的工具包(jar包),其手动导入的过程非常繁琐,而Maven技术帮我们简化了这项任务。Maven是一个项目管理工具,它能够很方便的帮你管理项目报告、生成站点、管理Jar文件等等。Maven的核心思想是约定大于配置。也就是说,Maven会规定好你该如何去编写我们的Java代码,必须要按照这个规范来。

pom.xml是Maven的核心配置文件,我们一切主要的Maven配置都可以在pom.xml中完成。

III. JavaWeb原理

1. Servlet

如上述所说,真正在Java Web开发中,我们主要需要做的事情就是针对客户端的动态资源请求(Request)在服务器端编写程序提取在数据库中对应的数据,并编写相关算法生成与之对应的响应(Response)并将其返回到客户端。而Servlet程序就是用于处理服务器端的动态资源请求(Request)并返回生成的响应(Response)给WebServer,进而返回给客户端。

开发一个Servlet程序需要完成两个步骤:

  • 编写一个类,实现Servlet接口(我们往往在类中继承HttpServlet,并实现方法[doGet, doPost…]即可)
  • 把开发好的实现了Servlet接口的类部署到Web服务器中(在Web-INF文件夹下的web.xml中配置即可)

a. Servlet原理

Servlet的整个工程流程如下:

首先,客户端(浏览器)发送一个Http请求到服务器的web容器,web容器生成request和response并作为参数转交给Servlet的Service方法,接着Servlet来处理动态请求,生成与之对应的响应,在方法走完后,web容器会将响应传回给客户端。所以说,我们的工作主要是编写Servlet实现类用于:

  • 接受并处理各种Http请求(Get, Post…)
  • 给出响应的信息

javaweb_原理

b. 传输对象

web服务器在接收到客户端的http请求后,会针对这个请求分别创建一个代表请求的HttpServletRequest对象代表响应的一个HttpServletResponse对象,并请它们传递到我们编写的Servlet实现类中。其分工如下:

  • 如果要获取客户端请求过来的参数: 找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse
1) HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;

常用方法:

// req.getParameter(String name) : 用于获取request传递过来的参数,参数名为String类型,传来的参数也是String类型
// req.getParameterValues(String name):用于获取request传递过来的String数组类型的多个参数
String username = req.getParameter("username"); 
String password = req.getParameter("password"); 
String[] hobbys = req.getParameterValues("hobbys");

// req.setAttribute(String "AttributeName", String attribute): 用于将生成的参数传递回去
req.setAttribute("AttributeName", String attribute);

// req.getContextPath()用户获取当前请求页面的路径
String path = req.getContextPath();

// 通过请求转发,请求会转发到另一个页面,但是url地址不变
req.getRequestDispatcher("/success.jsp").forward(req,resp);
2) Response

常用方法:

// 负责向浏览器发送数据的方法
// ServletOutputStream getOutputStream() throws IOException;

// PrintWriter getWriter() throws IOException;
PrintWriter writer = resp.getWriter();	// 获取response PrintWriter对象
writer.write("Hello World");	// 写出到响应中

// 负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
// ...

// 重定向,将页面重定向到另一个页面
resp.sendRedirect("path");

Note: 重定向 vs 请求转发

重定向和请求转发都会实现页面转跳。不同的是,请求转发时url不会产生变化(状态码307),重定向时,url会发生变化(状态码302)。

javaweb_原理

c. 数据共享

1) ServletContext

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用,实现了数据共享。

javaweb_原理

ServletContext的使用:

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  //this.getInitParameter() 初始化参数 
  //this.getServletConfig() Servlet配置 
  //this.getServletContext() Servlet上下文
  ServletContext context = this.getServletContext();

  String username = "小明"; //数据
  context.setAttribute("username",username); //将一个数据保存在了 ServletContext中,名字为:username,值:username(小明)
	} 
}

一个会话(Session)是一个用户在Web访问中一整套动作的过程。比如一个用户打开浏览器,打开了很多网站,将商品加入到了购物车等等,这整个过程称之为一个会话。而有状态会话指的是一个用户来过这个网站,当它再来的时候,我们知道他曾经来过。

保存会话有两种技术:CookieSession

Cookie

Cookie是一种客户端技术,其被保存在客户端用户访问服务器时会携带Cookie,这样服务器就知道是谁来访问了。Cookie有一些性质:

  • 一个Cookie只能保存一个信息
  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
  • Cookie大小有限制4kb;
  • 300个cookie浏览器上限
  • 不设置有效期,关闭浏览器,Cookie自动失效;

javaweb_原理

Cookie使用:

Cookie cookie = new Cookie("JSESSIONID",sessionId); 
resp.addCookie(cookie);

Session(重点)

Session是一种持久网络协议。Session有以下性质:

  • 服务器会给每一个用户(浏览器)创建一个Session对象
  • 每一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
  • 用户登录之后,整个网站它都可以访问! (保存用户的信息;保存购物车的信息…..)

Session的使用场景:

  • 保存一个登录用户的信息
  • 购物车信息
  • 在整个网站中经常会使用的数据,我们将它保存在Session中;

javaweb_原理

Session和Cookie的区别:

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存 (可以保存多个)
  • Session把用户的数据写到用户独占Session中,服务器端保存 (保存重要的信息,减少服务器资 源的浪费)
  • Session对象由服务创建;

Session的使用:

//得到Session
HttpSession session = req.getSession(); 

//往Session中存东西
session.setAttribute("name",new User("小明",18));

//get Session中的数据
Person person = (Person) session.getAttribute("person");

// 删除Session数据
session.removeAttribute("person");

// 手动注销Session
session.invalidate();

//获取Session的ID
String sessionId = session.getId();

//判断Session是不是新创建 
if (session.isNew()){
	resp.getWriter().write("session创建成功,ID:"+sessionId); 
}else {
	resp.getWriter().write("session已经在服务器中存在了,ID:"+sessionId);
}

web.xml配置Session自动过期

<!--设置Session默认的失效时间--> 
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
    <session-timeout>15</session-timeout>
</session-config>

d. 过滤器和监听器

1) 过滤器(Filter)

过滤器是在Web Server和Servleit中间的一层,用来过滤网站的数据。(如处理中文乱码,登录验证)

编写过滤器也需要两步:

  • 编写继承了javax.servlet.Filter接口的实现类(注意包不要导错了)。
  • 在web.xml中配置过滤器

过滤器实现类编写步骤及案例:

public class CharacterEncodingFilter implements Filter {
	//初始化:web服务器启动,就以及初始化了,随时等待过滤对象出现!
  public void init(FilterConfig filterConfig) throws
ServletException {
	System.out.println("CharacterEncodingFilter初始化"); 
    }
  
	//Chain : 链
	/*
	1. 过滤中的所有代码,在过滤特定请求的时候都会执行 2. 必须要让过滤器继续同行
        chain.doFilter(request,response);
     */
	public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain) throws IOException, ServletException {
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=UTF-8");
		System.out.println("CharacterEncodingFilter执行前....");
    chain.doFilter(request,response); //让我们的请求继续走,如果不写,程序到这里就被拦截停止!
		System.out.println("CharacterEncodingFilter执行后...."); }
  
  //销毁:web服务器关闭的时候,过滤会销毁 
  public void destroy() {
		System.out.println("CharacterEncodingFilter销毁"); 
	}
}

Note: 一定要执行chain.doFilter(request, response);,如果不写程序就被拦截停止了。

2) 监听器

用于监听,比如用于统计在线Session个数。实现步骤和Filter类似(编写Listener实现类并在web.xml中配置)。

e. Servlet配置文件web.xml

Java Web项目中所有Servlet, Filter, Listener, Session属性的配置均在web.xml中完成。其主要配置类的位置以及映射的路径,常用的配置代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--web.xml头文件-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

<!--    配置过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.soul.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
<!--    设置首页-->
    <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
    </welcome-file-list>

<!--    配置Servlet-->
    <servlet>
        <servlet-name>LoginServlet</servlet-name> <!--配置Servlet名字,与下面mapping的名字对应,一个servlet可以有				多个<servlet-mapping>-->
        <servlet-class>com.soul.servlet.user.LoginServlet</servlet-class> <!--配置Servlet 实现类的位置-->
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name> <!--配置Servlet名字,与上面servlet的名字对应-->
        <url-pattern>/login.do</url-pattern>	<!--配置Servlet实现类映射的url-->
    </servlet-mapping>

<!--    默认Session过期时间(真实业务需求)-->
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
</web-app>

2. JSP

a. JSP定义

JSP(Java Server Pages)是由SUn公司主导创建的一种动态网页技术标准。写JSP就像是在HTML中嵌入java代码,从而为用户提供动态数据。

b. JSP原理

当JSP页面被访问时,Tomcat服务器会在其work目录下生成一个与之对应的.java和.class文件,里面的代码是等价于此JSP文件的Servlet实现类,而服务器执行的其实就是这个Servlet实现类。因此,JSP本质上就是一个Servlet,其目的是为了简化开发,写JSP和写Servlet等价

javaweb_原理

c.JSP基础语法

任何语言都有自己的语法,java中有。JSP作为java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!),并被java支持。

// JSP表达式

<%--JSP表达式 作用:用来将程序的输出,输出到客户端 <%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>
  
// JSP脚本片段
<%--jsp脚本片段--%> <%
  int sum = 0;
  for (int i = 1; i <=100 ; i++) {
sum+=i; }
  out.println("<h1>Sum="+sum+"</h1>");
%>
  
<%
  int x = 10;
  out.println(x);
%> 
<p>这是一个JSP文档</p>
<%
int y = 2;
  out.println(y);
%>
<hr>
<%--在代码嵌入HTML元素--%>
<%
  for (int i = 0; i < 5; i++) {
%>
  <h1>Hello,World  <%=i%> </h1>
<% }
%>
  
// JSP声明
// JSP声明:会被编译到JSP生成Java的类中!其他的,就会被生成到_jspService方法中!
<%! static {
    System.out.println("Loading Servlet!");
  }
  private int globalVar = 0;
public void kuang(){ System.out.println("进入了方法Kuang!");
} %>
  
// JSP的注释,不会在客户端显示,HTML就会!
  
// JSP指令
<%@page args.... %>
<%@include file=""%>
<%--@include会将两个页面合二为一--%> <%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%> <hr>
<%--jSP标签 jsp:include:拼接页面,本质还是三个 --%>
<jsp:include page="/common/header.jsp"/> 
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"/>

d. JSP九大内置对象

  • PageContext 存东西
  • Request 存东西
  • Response
  • Session 存东西
  • Application 【SerlvetContext】 存东西
  • config 【SerlvetConfig】
  • out
  • page ,不用了解
  • exception
pageContext.setAttribute("name1","小明1号");//保存的数据只在一个页面中有效
request.setAttribute("name2","小明2号");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","小明3号");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","小明4号"); //保存的数据只在服务器中有效,从打开服务器到关闭服务器
  • request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
  • session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
  • application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如: 聊天数据;

e. JSP标签、JSTL标签、EL表达式

配置pom.xml:

<!-- JSTL表达式的依赖 --> <dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>
<!-- standard标签库 --> <dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

EL表达式${ }用来:

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象

JSP标签用来获取或传输动态数据:

<%--jsp:include--%>

<jsp:forward page="/jsptag2.jsp">
    <jsp:param name="name" value="xiaoming"></jsp:param>
    <jsp:param name="age" value="12"></jsp:param>
</jsp:forward>

JSTL表达式

JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样!

JSTL标签库使用步骤:

  • 引入对应的 taglib
  • 使用其中的方法
  • 在Tomcat也需要引入jstl的包,否则会报错:JSTL解析错误

IV. MVC架构

1. MVC定义

MVC(Model: 业务模型, View: 视图, Controller: 控制器)是一种三层架构的软件设计模式。其分工如下:

Model

  • 一般分为业务层(Service)和Dao层(Dao)以及JavaBean实体类(Entity)

  • 业务处理 :业务逻辑(Service层)

  • 数据持久层:CRUD (Dao层)

View(JSP, HTML, JS, CSS…)

  • 展示数据
  • 提供链接发起Servlet请求 (a,form,img…)

Controller (Servlet)

  • 接收用户的请求 :(req:请求参数、Session信息….)
  • 交给业务层处理对应的代码
  • 控制视图的跳转

mvc框架

MVC架构项目的工作流程如下(用户登录案例):

  1. 客户端用户向服务器(View层)端发起登录请求
  2. View层接收请求并转交给Controller(Servlet)
  3. Controller将请求发送给Model(Service层)处理登录请求
  4. Service层调用Dao层对象方法调用数据
  5. Dao层使用JDBC从数据库中提取出对应数据并返回给Service层
  6. Service层判断用户名,密码是否正确,并将结果返回给Controller层
  7. Controller将对应的Response发送给View并转交给客户端的用户

2. MVC开发流程

a. 项目搭建准备工作

  1. 搭建一个maven web项目

  2. 配置Tomcat

  3. 测试项目是否能在Tomcat上成功跑起来

  4. 导入必要的dependencies(jar包:jsp, Servlet, mysql驱动,jstl, standard…)[note: 同样要导入jar包到Tomcat中]

  5. 创建项目包结构

    • dao (接口,实现类)
    • filter
    • pojo
    • service(接口,实现类)
    • servlet
    • util (静态固定参数)
  6. 编写实体类(pojo)

  7. 编写基础公共类(dao)

    1. 数据库配置文件
    1. 编写数据库的公共类
  8. 编写必要的Filter(e.g. 字符编码过滤器)

  9. 导入静态资源(html, js, css, jsp, image…)

基础公共类(dao)模板

package com.soul.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

// 操作数据库公共类
public class BaseDao {
    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    // 静态代码块,类加载的时候就初始化了
    static {
        Properties properties = new Properties();
        // 通过类加载器读取对应的资源
        InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }

        driver = properties.getProperty("driver");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
    }

    // 获取数据库的链接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    // 编写查询公共方法
    public static ResultSet execute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet, String sql, Object [] params) throws SQLException {
        // 预编译的sql,在后面直接执行不用传参就可以了
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            // setObject, 占位符从1开始,但是我们的数组是从0开始的
            preparedStatement.setObject(i+1, params[i]);
        }
        resultSet = preparedStatement.executeQuery();
        return resultSet;
    }

    // 编写增删改公共方法
    public static int execute(Connection connection, PreparedStatement preparedStatement, String sql, Object [] params) throws SQLException {
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            // setObject, 占位符从1开始,但是我们的数组是从0开始的
            preparedStatement.setObject(i+1, params[i]);
        }
        int updateRows = preparedStatement.executeUpdate();
        return updateRows;
    }

    // 释放资源
    public static boolean closeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
        boolean flag = true;
        if (resultSet != null) {
            try {
                resultSet.close();
                // GC回收
                resultSet = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }

        if (preparedStatement != null) {
            try {
                preparedStatement.close();
                // GC回收
                preparedStatement = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }

        if (connection != null) {
            try {
                connection.close();
                // GC回收
                connection = null;
            } catch (SQLException throwables) {
                throwables.printStackTrace();
                flag = false;
            }
        }
        return true;
    }
}

数据库(MySQL)配置文件db.properties模板

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbms?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
password=123456

b. 实现一个功能步骤

  1. 写dao层
  2. 写service层
  3. 写servlet层
  4. 在web.xml中配置servlet
  5. 写junit进行单元测试

案例(登录)代码

1) dao层

// 编写UserDao接口
public interface UserDao {
    // 得到要登录的用户
    public User getLoginUser(Connection connection, String userCode) throws SQLException;
}

// 编写UserDao接口的实现类
public class UserDaoImpl implements UserDao{
    @Override
    public User getLoginUser(Connection connection, String userCode) throws SQLException {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        User user = null;

        if (connection != null) {
            String sql = "select * from smbms_user where userCode=?";	// 编写sql语句
            Object[] params = {userCode};	// 设置sql语句中?值

            rs = BaseDao.execute(connection, pstm, rs, sql, params);	// 执行sql语句并将结果存放到rs对象中
            if (rs.next()) {	// 从rs对象中取出user对象
                user = new User();
                user.setId(rs.getInt("id"));
                user.setUserCode(rs.getString("userCode"));
                user.setUserName(rs.getString("userName"));
                user.setUserPassword(rs.getString("userPassword"));
                user.setGender(rs.getInt("gender"));
                user.setBirthday(rs.getDate("birthday"));
                user.setPhone(rs.getString("phone"));
                user.setAddress(rs.getString("address"));
                user.setUserRole(rs.getInt("userRole"));
                user.setCreatedBy(rs.getInt("createdBy"));
                user.setCreationDate(rs.getTimestamp("creationDate"));
                user.setModifyBy(rs.getInt("modifyBy"));
                user.setModifyDate(rs.getTimestamp("modifyDate"));
            }
            BaseDao.closeResource(null, pstm, rs);	// 关闭资源
        }
        return user;	// 返回user对象
    }
}

2) service层

// 编写UserService接口
public interface UserService {
    // 用户登录
    public User login(String userCode, String password);
}

// 编写UserService接口的实现类
public class UserServiceImpl implements UserService{
    // 业务层都会调用dao层,所以我们要引入Dao层
    private UserDao userDao;

  	// 构造函数
    public UserServiceImpl() {
        userDao = new UserDaoImpl();
    }

    @Override
    public User login(String userCode, String password) {
        Connection connection = null;
        User user = null;

        try {
            connection = BaseDao.getConnection();	// 连接数据库
            // 通过业务层调用对应的具体的数据库操作
            user = userDao.getLoginUser(connection, userCode);	// 调用userDao对象的方法获取user对象
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            BaseDao.closeResource(connection, null, null);	// 关闭资源
        }
      
        // 判断密码是否正确,并将user返回数据到Servlet层
        if (user != null) {
            if (user.getUserPassword().equals(password)) {
                return user;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
}

3) servlet层

public class LoginServlet extends HttpServlet {
    // Servlet:控制层, 调用业务层代码

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取用户名和密码
        String userCode = req.getParameter("userCode");
        String userPassword = req.getParameter("userPassword");

        // 和数据库中的密码进行对比,调用业务层
        UserService userService = new UserServiceImpl();
        User user = userService.login(userCode, userPassword);  // 这里已经把登录的人给查出来了

        if (user != null) { // 查有此人,可以登录
            // 将用户的信息放到Session中;
            req.getSession().setAttribute(Constants.USER_SESSION, user);
            // 跳转到主页
            resp.sendRedirect("/jsp/frame.jsp");
        } else {    // 查无此人,无法登录
            // 转发回登录页面,顺带提示它,用户名或者密码从无
            req.setAttribute("error", "用户名或密码错误");
            req.getRequestDispatcher("login.jsp").forward(req, resp);
        }

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

4) 在web.xml中配置servlet

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.soul.servlet.user.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login.do</url-pattern>
    </servlet-mapping>

5) 写junit进行单元测试

    @Test
    public void testLogin() {
        UserService userService = new UserServiceImpl();
        User admin = userService.login("admin", "1234567");
        System.out.println(admin.getUserPassword());
    }

V. 常用工具及概念

1. Ajax

Ajax即Asynchronous Javascript And XML(异步JavaScript和XML)是一项用于网页应用快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面的技术。比如微博刷新不需要刷新整个页面,而只用重新载入部分更新内容即可,这便是应用到了Ajax技术。

代码案例:

$.ajax({
  type:"GET",//请求类型
  url:path+"/jsp/user.do",//请求的url
  data:{method:"getrolelist"},//请求参数
  dataType:"json",//ajax接口(请求url)返回的数据类型
  success:function(data){//data:返回数据(json对象)
    if(data != null){
      userRole.html("");
      var options = "<option value=\"0\">请选择</option>";
      for(var i = 0; i < data.length; i++){
        options += "<option value=\""+data[i].id+"\">"+data[i].roleName+"</option>";
      }
      userRole.html(options);
    }
  },
  error:function(data){//当访问时候,404,500 等非200的错误状态码
    validateTip(userRole.next(),{"color":"red"},imgNo+" 获取用户角色列表error",false);
  }
});

2. json

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

例子:

// 表示变量
 {"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}

// 表示基础变量数组
[3, 1, 4, 1, 5, 9, 2, 6]

// 表示对象
{"firstName": "Brett", "lastName": "McLaughlin"}                                        

// 表示数组
{ 
"people":[ 
    {
      "firstName": "Brett",            
      "lastName":"McLaughlin"        
    },      
    {        
      "firstName":"Jason",
      "lastName":"Hunter"
    }
  ]
}

3. JDBC

Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。

由于每一种数据库都有自己的驱动方式,如果我们每连接一个数据库就自己写一个数据库连接实现,那么工程过于繁琐。JDBC就是在Java应用和数据库之间用于解决此问题的中间层接口。这使得我们只需要使用JDBC就可以连接Java应用和各种数据库。

jdbc_原理

JDBC固定步骤

  1. 加载驱动
  2. 连接数据库,代表数据库3. 向数据库发送SQL的对象S
  3. tatement : CRUD
  4. 编写SQL (根据业务,不同的SQL)
  5. 执行SQL
  6. 关闭连接

代码演示:

public class TestJdbc {
  public static void main(String[] args) throws ClassNotFoundException,
SQLException { //配置信息
		//useUnicode=true&characterEncoding=utf-8 解决中文乱码
  	String url="jdbc:mysql://localhost:3306/jdbc?
		useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "123456";
  
		//1.加载驱动 
  	Class.forName("com.mysql.jdbc.Driver"); 
  	//2.连接数据库,代表数据库
		Connection connection = DriverManager.getConnection(url, username, password);
		//3.向数据库发送SQL的对象 Statement,PreparedStatement : CRUD 
  	Statement statement = connection.createStatement();
		//4.编写SQL
		String sql = "select * from users";
		//5.执行查询SQL,返回一个 ResultSet : 结果集 ResultSet rs = statement.executeQuery(sql);
    while (rs.next()){
      System.out.println("id="+rs.getObject("id"));
      System.out.println("name="+rs.getObject("name"));
      System.out.println("password="+rs.getObject("password"));
      System.out.println("email="+rs.getObject("email"));
      System.out.println("birthday="+rs.getObject("birthday"));
		}
		//6.关闭连接,释放资源(一定要做) 先开后关 rs.close();
		statement.close(); connection.close();
		} 
}

JDBC预编译SQL:

public class TestJDBC2 {
	public static void main(String[] args) throws Exception {
    //配置信息
    //useUnicode=true&characterEncoding=utf-8 解决中文乱码 String url="jdbc:mysql://localhost:3306/jdbc?
    useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "123456";
    //1.加载驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2.连接数据库,代表数据库
    Connection connection = DriverManager.getConnection(url, username, password);
    //3.编写SQL
    String sql = "insert into  users(id, name, password, email, birthday) values (?,?,?,?,?);";
    //4.预编译
    PreparedStatement preparedStatement =
    connection.prepareStatement(sql);
    preparedStatement.setInt(1,2);//给第一个占位符? 的值赋值为1;
    preparedStatement.setString(2,"狂神说Java");//给第二个占位符? 的值赋值为 狂神说Java;
    preparedStatement.setString(3,"123456");//给第三个占位符? 的值赋值为 123456;
    preparedStatement.setString(4,"24736743@qq.com");//给第四个占位符? 的 值赋值为1;
    preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));//给第五个占位符? 的值赋值为new 					Date(new 	 java.util.Date().getTime());
    //5.执行SQL
		int i = preparedStatement.executeUpdate();
    if (i>0){ 
      System.out.println("插入成功@");
    }
		//6.关闭连接,释放资源(一定要做) 先开后关 preparedStatement.close(); connection.close();
	}
}

4. JUnit

JUnit是一个Java语言的单元测试框架。在导入JUnit包后,只需要在方法或者类上方加入@Test标签即可使用。

5. JavaBean

JavaBean是一种可重用的Java组件。实际上,JavaBean是一种有特定规范的类,一般用于映射数据库中数据表的数据。因此JavaBean往往被用作实体类来使用。成为JavaBean的类的特定规范如下:

  • 类必须是public
  • 必须有一个public的无参构造方法(可以有附加的其它构造方法)
  • 变量必须为private
  • 每一个变量必须有public的getter和setter
  • JavaBean一定要放在包内,使用package进行自定义
  • 对于部署好的JavaBean修改是,一定要重新编译节码文件,同时启动Tomcat服务器,之后便能够生效

VI. Java Web开发常见坑

  1. 如果junit成果但是tomcat失败,一般就是tomcat没有添加依赖造成的
  2. jsp路径和tomcat配置的项目url路径要对应 (用 req.getContextPath() + path可以解决)
  3. 关闭资源的时候,这个方法开了什么就关什么,没开的就不要关。
  4. 要熟悉request和response各自的功能
  5. 要熟悉request提取数据的各个作用域(Session, Request 以及 ContentText)
  6. 有些前端代码可以用layui来自动实现

VII. 总结

JavaWeb是Java中用于动态Web开发的核心技术,我们使用它可以完全实现所有Web应用。

JavaWeb是Java工程师从初级到高级必须走的第一步。在学习完JavaSE的全部知识后,JavaWeb便是在其之上很好的实战。虽然今天在大部分应用中JavaWeb的代码已经被各种成熟的框架如SSM, Spring Boot, Spring Cloud等所取代了,但是这些框架的核心思想和代码还是基于JavaWeb之上的。他们的底层代码还是使用JavaWeb来实现的。因此,想要理解和优化一个SSM或Spring Boot项目就必须要完全掌握JavaWeb的知识。一个优秀的工程师和调参员(码农)之间的差别就在于此。因此,JavaWeb之间依旧是Java工程师必须掌握的内容且必须要迈出的一步。

VIII. 参考