3.JSP实战优化:Servlet基础
3.1 Servlet概述
3.1.1 为什么需要Servlet
总结我们JSP控制器的写法,把Java代码写到JSP中,虽然简单方便但是编写期间也有很多的问题,比如:
- JSP中的Java代码不会报语法错误;
- JSP中自动导包老是会忘记
- 运行报错提示也不友好;
- 用作控制器的JSP只是编写Java代码,但是我们的JSP还是希望能用在展示数据上;
所以综上所述,我们此时就有了一个想法,JSP的控制器能不能用java类的形式进行编写呢?答案是可以的
之前我们已经说了JavaWEB的开发模式 有model1 和 model2 模式,那么现在我们就要抛弃model1转而投向model2开发了;
model1 和 model2的最大区别就是 model2 开发模式中控制器的代码是完全写在Java类中的,而这个用作控制器的java类叫做Servlet;
3.1.2 什么是Servlet
Servlet是在服务器端运行的Java程序,可以接收客户端请求并做出响应
Servlet可以动态生成需要的数据对客户端进行响应
3.1.3 JSP和Servlet的关系
根据JSP的运行原理可知,JSP在运行期间因为内部包含java代码需要编译,所以最终JSP会被编译成一个class执行,而JSP继承于org.apache.jasper.runtime.HttpJspBase类,
而HttpJspBase又是继承自HttpServlet的类,由此可以得出一个结论,就是JSP在运行时会被Web容器翻译为一个Servlet
3.2 Servlet使用
3.2.1 简单使用
假设我们现在完成有一个页面登录效果 :
输入正确的用户名密码,提示登录成功;
完成上面的效果我们需要完成以下几步操作
3.2.2 编写需求页面
3.2.2.1 登录页面 :
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户登录</title>
</head>
<body>
<div>
<h1>servlet小案例:用户登录</h1>
<form action="userlogin" method="post">
<p>
请输入您的用户名 :<input type="text" name="loginName" />
</p>
<p>
请输入您的密码 : <input type="password" name="loginPass" />
</p>
<p>
<button type="submit">登录</button>
</p>
</form>
</div>
</body>
</html>
3.2.2.2 登录成功:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录成功</title>
</head>
<body>
<div>
<marquee>
<h1>恭喜,登录成功!</h1>
</marquee>
</div>
</body>
</html>
3.2.2.3 登录失败:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录失败</title>
</head>
<body>
<div>
<marquee>
<h1>很遗憾,登录失败!</h1>
</marquee>
</div>
</body>
</html>
3.2.3 创建Servlet
- 创建 com.hnxy.controller 包 当然如果感觉controller难写的话可以创建com.hnxy.web
2. 在创建好的包上右键选择 “new”–>“other”
- 弹出的框中选择servlet
- 给Servlet起个名字,我们这里叫做UserLoginAction 代表是用户登录的控制器 Action 代表动作,这里指代控制器
- 主要是填写一些Servlet的描述信息,因为怎么说Servlet也是一个小程序,所以此处可以填写一些程序的描述信息
- 但是此处需要注意的是URL mappings 这个选项,此处一定要和页面action的地址保持一致,这样JSP才能通过提交标签的action地址找到这个servlet,这个servlet才能处理请求
- 选择创建servlet的时候保留哪些方法,我们此处只保留doGet 和 doPost方法,因为这两个方法一个帮助我们处理get请求 一个帮助我们处理post请求
这样一个Servlet就创建好了
package com.hnxy.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserLoginAction extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
稍加修改就可以使用了
package com.hnxy.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 用户登录的控制器
* @author My
*
*/
public class UserLoginAction extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设定编码
request.setCharacterEncoding("UTF-8");
// 获取页面数据
String loginName = request.getParameter("loginName");
String loginPass = request.getParameter("loginPass");
// 业务判断
if("admin".equals(loginName)&&"admin".equals(loginPass)){
request.getRequestDispatcher("success.jsp").forward(request, response);
}else{
response.sendRedirect("error.jsp");
}
}
}
- 调试运行
成功的时候
3.2.4 Servlet群组体系
3.2.4.1 Servlet的结构 :
我们用的HttpServlet是继承自GenericServlet并且实现了Servlet 和 ServletConfig接口;专门用来处理HTTP请求的一种控制器;
此处我们来看下这些个接口与抽象类的主要方法:
1) servlet接口:
定义了一个servlet标准,它告诉开发者什么样的类才能称之为是servlet程序,所以servlet接口定义了一些servlet必要的方法
我们的HttpServlet帮助我们实现了原始servlet的所有方法,所以我们在用servlet的时候就不用再实现这些方法了,很开心
Servlet 里的init在程序第一次执行时就会被执行
Servlet 里的destroy在程序被正常关闭时会被执行
2) servletConfig 接口 :
在Servlet初始化过程中获取配置信息,一个Servlet只有一个ServletConfig对象,它的常用方法有
3) GenericServlet抽象类 :
提供了Servlet与ServletConfig接口方法的默认实现(service( )方法除外)
4) HttpServlet抽象类 :
继承自GenericServlet,处理HTTP协议的请求和响应
3.2.5 Servlet运行原理
一个servlet的主要执行步骤
详细解释 :
首先tomcat开启,运行我们的web应用
访问登录页面
http://127.0.0.1:8080/javaweb_class3_tp1/
① 当点击登录按钮的时候tomcat会创建request对象向服务器地址发起一次请求
服务器地址
http://127.0.0.1:8080/javaweb_class3_tp1/userlogin
携带数据
loginName=admin loginPass=admin
当我将form改成get请求就可以具体看到
http://127.0.0.1:8080/javaweb_class3_tp1/userlogin?loginName=admin&loginPass=admin
② tomcat需要接收这个请求进行解析
怎么解析呢?
http://127.0.0.1:8080 确定是哪台tomcat接收
javaweb_class3_tp1 确定那个webapp处理这个请求
userlogin 这是客户端制定需要服务端那个服务来处理这个请求
这里有一个问题,我们有这个服务么?答案是有的!请打开项目的WEB-INF文件夹里面有一个web.xml文件
双击打开
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>javaweb_class3_tp1</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>UserLoginAction</display-name>
<servlet-name>UserLoginAction</servlet-name>
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserLoginAction</servlet-name>
<url-pattern>/userlogin</url-pattern>
</servlet-mapping>
</web-app>
3.2 什么是XML?
XML 指可扩展标记语言(eXtensible Markup Language)。
XML 被设计用来传输和存储数据。
XML 很重要,也很容易学习。
HTML其实就是一种特殊的XML
这个XML在我们的项目中主要负担一个web项目的配置工作,比如上面的web.xml中就对我们的web项目进行了一些配置
比如
<!-- webapp的项目名称 -->
<display-name>javaweb_class3_tp1</display-name>
<!-- webapp的欢迎页面 也就是说如果访问 http://127.0.0.1:8080/javaweb_class3_tp1 没有指定要访问的JSP的话 tomcat会默认跳转到下面存在的一个页面中去 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
上面的都是不重要的 下面才是重要的
<servlet>
<description></description>
<display-name>UserLoginAction</display-name>
<servlet-name>UserLoginAction</servlet-name>
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserLoginAction</servlet-name>
<url-pattern>/userlogin</url-pattern>
</servlet-mapping>
这个是我们UserLoginAction这个Servlet的核心配置部分,不知道大家还有没有印象刚才我们通过页面请求已经分析到
客户端要请求 http://127.0.0.1:8080 这台tomcat 里面的 javaweb_class3_tp1 这个webapp处理这个请求
并且还指定了需要 javaweb_class3_tp1这个webapp中的 userlogin 这个服务来处理请求
那么我们观察一下 userlogin 是不是和 servlet配置中的
<servlet-mapping>
<servlet-name>UserLoginAction</servlet-name>
<url-pattern>/userlogin</url-pattern>
</servlet-mapping>
这段里面的 <url-pattern>/userlogin</url-pattern>
是保持一致的,这样页面提交的userlogin这个请求就被UserLoginAction拦截并处理
那么UserLoginAction有是个啥呢?我们再往上看
<servlet>
<description></description>
<display-name>UserLoginAction</display-name>
<servlet-name>UserLoginAction</servlet-name>
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
</servlet>
servlet-mapping中的 UserLoginAction 和 servlet中的<servlet-name>UserLoginAction</servlet-name>
又互相匹配
匹配上了之后我们观察
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
这句话,这句话是一个类的全限定名称,我们知道JVM运行期间可以通过类名反射生成这个类的实例,
③④⑤ 所以你现在要请求这个类,那么tomcat就会根据你的页面请求 userlogin 到 web.xml中找到相应的 servletmapping 标签下的 url-pattern 与之匹配
匹配上之后再根据servletname找到具体的servlet 然后根据servlet-class创建这个servlet的实例(对象)
创建了这个对象之后,tomcat会根据你页面的请求方式来调用这个servlet对象的doGet() 或者doPost()方法来处理这个业务;
⑥ 具体的servlet对象在处理完这个业务之后然后保存结果,最后在转发或者重定向到下一个页面 完成这次转发与请求的工作
总结 :
这个过程中需要有几个地方大家注意
<servlet>
<description></description>
<display-name>UserLoginAction</display-name>
<servlet-name>UserLoginAction</servlet-name>
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserLoginAction</servlet-name>
<url-pattern>/userlogin</url-pattern>
</servlet-mapping>
每个servlet都有一个这个的配置,需要注意的是:
-
<url-pattern>/userlogin</url-pattern>
配置的是页面form表单中action的地址,注意此处需要加/拦截
为什么呢
仔细观察我们的提交地址
http://127.0.0.1:8080/javaweb_class3_tp1/userlogin -
<servlet-class>com.hnxy.controller.UserLoginAction</servlet-class>
这个配置servlet类的全限定名称,这样java才能反射生成这个类的对象
调用这个类中的doGet() 或者doPost()方法 -
关于XML配置我们以后会详细说,此处我们需要注意的就是一个webapp项目都有一个XML配置文件,这里面做一些XML的配置工作
-
一个类如果能称之为是一个Servlet小程序的话必须满足两点 : 一个是继承 HttpServlet 另外一个就是要在XML中配置这个servlet
-
那么有的同学就会问了,有没有什么简单的Servlet配置方案呢? 当然有!
如果想简单配置,可以删掉web.xml中的servlet配置
在servlet类上配置 @WebServlet("/form表单中action的名字")
这样一个servlet就生效了,这个叫做注解配置
那么什么又是注解的? 其实注解是一种流体XML也可以叫 软体XML(当然只有我这么称呼注解,请大家不要往外传播)
3.3 Java 注解
3.3.1 注解基础知识点
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
3.3.2 介绍
Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。
注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:
标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。
另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。
3.4 元数据的作用
如果要对于元数据的作用进行分类,还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
编写文档:通过代码里标识的元数据生成文档。
代码分析:通过代码里标识的元数据对代码进行分析。
编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
说白了 注解即使在将普通类标记成特殊功能的一种Java标记技术,所以 注解 XML 其实都是一回事,只不过注解会更简单,注解也是我们后期主要学习的一种技术,此处大家只需要知道注解如何标记一个servlet就可以了!