Java Web
Java Web
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, 第三方框架等。
动态web的运作过程往往如下:
- 用户在客户端向服务器端发送一个请求(Request)(比如请求查看一个网页)
- 服务器的WebServer Plugin部分接收到客户端的请求后,如果是静态资源请求,那么直接由WebServer从服务器中将静态资源提取出来;如果是动态资源请求,由Servlet/JSP将动态资源从数据库中提取并处理好并交给WebServer(比如数据库中某个用户的详细信息)
- 由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…)
- 给出响应的信息
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)。
c. 数据共享
1) ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用,实现了数据共享。
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(小明)
}
}
2) 会话(Cookie, Session)
一个会话(Session)是一个用户在Web访问中一整套动作的过程。比如一个用户打开浏览器,打开了很多网站,将商品加入到了购物车等等,这整个过程称之为一个会话。而有状态会话指的是一个用户来过这个网站,当它再来的时候,我们知道他曾经来过。
保存会话有两种技术:Cookie和Session。
Cookie
Cookie是一种客户端技术,其被保存在客户端。用户访问服务器时会携带Cookie,这样服务器就知道是谁来访问了。Cookie有一些性质:
- 一个Cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- Cookie大小有限制4kb;
- 300个cookie浏览器上限
- 不设置有效期,关闭浏览器,Cookie自动失效;
Cookie使用:
Cookie cookie = new Cookie("JSESSIONID",sessionId);
resp.addCookie(cookie);
Session(重点)
Session是一种持久网络协议。Session有以下性质:
- 服务器会给每一个用户(浏览器)创建一个Session对象
- 每一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
- 用户登录之后,整个网站它都可以访问! (保存用户的信息;保存购物车的信息…..)
Session的使用场景:
- 保存一个登录用户的信息
- 购物车信息
- 在整个网站中经常会使用的数据,我们将它保存在Session中;
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等价。
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架构项目的工作流程如下(用户登录案例):
- 客户端用户向服务器(View层)端发起登录请求
- View层接收请求并转交给Controller(Servlet)
- Controller将请求发送给Model(Service层)处理登录请求
- Service层调用Dao层对象方法调用数据
- Dao层使用JDBC从数据库中提取出对应数据并返回给Service层
- Service层判断用户名,密码是否正确,并将结果返回给Controller层
- Controller将对应的Response发送给View并转交给客户端的用户
2. MVC开发流程
a. 项目搭建准备工作
搭建一个maven web项目
配置Tomcat
测试项目是否能在Tomcat上成功跑起来
导入必要的dependencies(jar包:jsp, Servlet, mysql驱动,jstl, standard…)[note: 同样要导入jar包到Tomcat中]
创建项目包结构
- dao (接口,实现类)
- filter
- pojo
- service(接口,实现类)
- servlet
- util (静态固定参数)
编写实体类(pojo)
编写基础公共类(dao)
- 数据库配置文件
- 编写数据库的公共类
编写必要的Filter(e.g. 字符编码过滤器)
导入静态资源(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. 实现一个功能步骤
- 写dao层
- 写service层
- 写servlet层
- 在web.xml中配置servlet
- 写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固定步骤:
- 加载驱动
- 连接数据库,代表数据库3. 向数据库发送SQL的对象S
- tatement : CRUD
- 编写SQL (根据业务,不同的SQL)
- 执行SQL
- 关闭连接
代码演示:
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开发常见坑
- 如果junit成果但是tomcat失败,一般就是tomcat没有添加依赖造成的
- jsp路径和tomcat配置的项目url路径要对应 (用 req.getContextPath() + path可以解决)
- 关闭资源的时候,这个方法开了什么就关什么,没开的就不要关。
- 要熟悉request和response各自的功能
- 要熟悉request提取数据的各个作用域(Session, Request 以及 ContentText)
- 有些前端代码可以用layui来自动实现
VII. 总结
JavaWeb是Java中用于动态Web开发的核心技术,我们使用它可以完全实现所有Web应用。
JavaWeb是Java工程师从初级到高级必须走的第一步。在学习完JavaSE的全部知识后,JavaWeb便是在其之上很好的实战。虽然今天在大部分应用中JavaWeb的代码已经被各种成熟的框架如SSM, Spring Boot, Spring Cloud等所取代了,但是这些框架的核心思想和代码还是基于JavaWeb之上的。他们的底层代码还是使用JavaWeb来实现的。因此,想要理解和优化一个SSM或Spring Boot项目就必须要完全掌握JavaWeb的知识。一个优秀的工程师和调参员(码农)之间的差别就在于此。因此,JavaWeb之间依旧是Java工程师必须掌握的内容且必须要迈出的一步。
Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Pinterest
Email