一、Servlet
1、servlet概述
model1和model2的最大区别就是model2开发模式中控制器的代码是完全写在Java类中的,而这个用作控制器的java类叫做Servlet。
Servlet是在服务器端运行的Java程序,可以接收客户端请求并作出响应;Servlet可以动态生成需要的数据对客户端进行响应。
- JSP和Servlet的关系
JSP在运行期间因为内部包含java代码需要编译,所以最终JSP会被翻译成一个class执行,而JSP继承与org.apache.jasper.runtime.HttpJspBase
类,而HttpJspBase继承自HttpServlet类,因此JSP在运行时会被web容器翻译成一个Servlet。
2、Servlet运行原理
客户端发出请求,Web容器解析请求,开始创建Servlet实例,调用init()方法,调用service()方法(doXXX()方法),服务器响应到客户端,容器使用完毕后,调用destory()方法关闭。
3、servlet的创建
- 打开eclipse,选择new -> Other… -> Web -> Servlet ,next打开创建Servlet对话框
- 输入包名和类名(类名要和表单提交的地址一致),因为URL映射必须和表单提交地址一致,否则无法拦截。
- 在Servlet文件中,默认有doGet和doPost方法,用于执行get和post请求的,doPost方法默认执行了doGet方法,因此将业务逻辑写入doGet方法中即可。
4、Servlet的结构
HttpServlet是继承自GenericServlet并且实现了Servlet和ServletConfig接口,是专门用来处理HTTP请求的控制器。
- servlet接口:定义了servlet标准
方法 | 说明 |
---|---|
void init(ServletConfig config) | 由Servlet容器调用,用于完成Servlet对象在处理客户请求前的初始化工作 |
void destory() | 由Servlet容器调用,释放Servlet对象所使用的资源 |
void service(ServletRequet req, ServletResponse res) | 由Servlet容器调用,用来处理客户端的请求 |
ServletConfig getServletConfig() | 返回ServletConfig对象,该对象包含此Servlet的初始化和启动参数。返回的ServletConfig对象是传递给init()方法的对象 |
String getServletInfo() | 返回有关Servlet的信息,如:作者、版本、版权等。返回的字符串是纯文本,而不是任何标记(HTML、XML等) |
- servletConfig接口:在Servlet初始化过程中获取配置信息,一个Servlet只有一个ServletConfig对象
方法 | 说明 |
---|---|
String getInitParameter(String name) | 获取web.xml中设置的以name命名的初始化参数值 |
ServletContext getServletContext() | 返回Servlet的上下文对象引用 |
- GenericServlet抽象类:提供了Servlet与ServletConfig接口的默认实现(Service()方法除外)
方法 | 说明 |
---|---|
public void init(ServletConfig config) | 对Servlet接口中init()方法的实现,对ServletConfig实例进行保存。此方法还有一个无参的重载方法,可用于子类中重写 |
public String getInitParameter(String name) | 返回web.xml中名称为name的初始化参数的值 |
public ServletContext getServletContext() | 返回ServletContext对象的引用 |
- HttpServlet抽象类:继承自GenericServlet,用于处理HTTP协议的请求和响应
方法 | 说明 |
---|---|
public void service(ServletRequet req, ServletResponse res) | 由对GenericServlet类中service()方法的实现,将请求分发给protected void service(ServletRequet req, ServletResponse res) |
protected void service(ServletRequet req, ServletResponse res) | 接收HTTP请求,并将它们分发给此类中定义的doXXX()方法 |
protected void doXXX(ServletRequet req, ServletResponse res) | 根据请求方式的不同分别调用响应的处理方法,如doGet()、doPost()等 |
5、servlet小案例
用户登录界面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户登录</title>
</head>
<body>
<form action="LoginAction" method="post">
用户名:<input type="text" name="loginName"/><br/>
密码:<input type="password" name="loginPass" /><br/>
<input type="submit" value="登录" />
</form>
</body>
</html>
控制器界面:
package com.gaj.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginAction
*/
public class LoginAction extends HttpServlet {
private static final long serialVersionUID = 1L;
// 处理get请求
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("utf-8");
// 接收loginName和loginPass
String loginName = request.getParameter("loginName");
String loginPass = request.getParameter("loginPass");
// 逻辑判断
if(!loginName.isEmpty() && !loginPass.isEmpty()){
if(loginName.equals("admin") && loginPass.equals("admin")){
request.setAttribute("msg", "登陆成功!");
}else{
request.setAttribute("msg", "用户名或密码错误!");
}
}else{
request.setAttribute("msg", "用户名或密码为空!");
}
// 转发
request.getRequestDispatcher("result.jsp").forward(request, response);
}
// 处理post请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 处理get请求
doGet(request, response);
}
}
登录结果界面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录结果</title>
</head>
<body>
<h1>${msg }</h1>
</body>
</html>
二、XML
1、XML概述
XML:(extensible Markup Language)可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。
XML被设计用来传输和存储数据,HTML其实就是一种特殊的XML。
2、XML示例
当前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">
<!--webapp的项目名称 -->
<display-name>javaweb_tp5</display-name>
<!-- webapp的欢迎界面(默认加载这些页面) -->
<!-- 如果地址栏只写到项目名,后面没有指定具体JSP或HTML,服务器会默认加载这些页面 -->
<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>
<!-- servlet描述 -->
<description></description>
<display-name>LoginAction</display-name>
<!-- servlet名 -->
<servlet-name>LoginAction</servlet-name>
<!-- servlet全限定名,可用于反射 -->
<servlet-class>com.gaj.controller.LoginAction</servlet-class>
</servlet>
<!-- servlet映射-->
<servlet-mapping>
<servlet-name>LoginAction</servlet-name>
<!-- servlet的URL映射,应该与form表单一致,当form表单提交时,被服务器拦截并处理 -->
<url-pattern>/LoginAction</url-pattern>
</servlet-mapping>
</web-app>
分析:
- <url-pattern>/LoginAction</url-pattern> 配置的是页面form表单中action的地址,注意此处需要加/拦截
浏览器提交地址:http://127.0.0.1:8080/javaweb_tp5/LoginAction - <servlet-class>com.gaj.controller.LoginAction</servlet-class> 配置servlet类的全限定名称,java可以反射生成这个类的对象
调用这个类中的doGet() 或者doPost()方法 - 一个webapp项目都有一个XML配置文件,做一些XML的配置工作
- 一个类如果能称之为是一个Servlet小程序的话必须满足两点 : 一个是继承HttpServlet,另外一个就是要在XML中配置当前的servlet
三、注解
1、注解概述
注解(Annotation):也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
简单理解:注解就是在将普通类标记成特殊功能的一种Java标记技术,所以 注解 XML其实都是一回事,只不过注解会更简单。
- 注解是以”@注解名“在代码中存在的。
- 根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。
- 它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。
- 可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。
2、注解作用分类
编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
3、小案例
删除web.xml中的servlet配置,在servlet类上配置@WebServlet("/form表单中action名")
package com.gaj.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginAction
*/
@WebServlet("/LoginAction")
public class LoginAction extends HttpServlet {
private static final long serialVersionUID = 1L;
// 处理get请求
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("utf-8");
// 接收loginName和loginPass
String loginName = request.getParameter("loginName");
String loginPass = request.getParameter("loginPass");
// 逻辑判断
if(!loginName.isEmpty() && !loginPass.isEmpty()){
if(loginName.equals("admin") && loginPass.equals("admin")){
request.setAttribute("msg", "登陆成功!");
}else{
request.setAttribute("msg", "用户名或密码错误!");
}
}else{
request.setAttribute("msg", "用户名或密码为空!");
}
// 转发
request.getRequestDispatcher("result.jsp").forward(request, response);
}
// 处理post请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 处理get请求
doGet(request, response);
}
}
四、过滤器
1、过滤器概述
Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。
2、过滤器作用
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。所以可以利用过滤器,在每次真正处理页面请求之前,设定request的编码
3、过滤器的常用方法
方法 | 说明 |
---|---|
void init(FilterConfig fConfig) | 过滤器初始化方法,FilterConfig可以获取一些初始化参数 |
void destroy() | 过滤器销毁方法 |
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 过滤器的核心方法,可以对request,response对象进行设置 |
- init()方法:
由web容器调用,指示将放入服务中的过滤器。servlet容器只在实例化过滤器之后调用一次init方法。如果init方法抛出ServletException或没有在Web容器定义的时间段内返回,则Web容器无法将过滤器放入服务中。这个和Servlet的生命周期有点类似,也是只初始化一次,destroy()也是执行一次。 - destroy()方法:
由Web容器调用,指示将从服务中取出的过滤器。此方法仅在过滤器的doFilter方法中的所有线程都已退出之后调用一次,或者在过了超时期之后调用。在调用此方法之后,Web 容器不会再对此过滤器实例调用doFilter方法。此方法为过滤器提供了一个清除持有的所有资源(比如内存、文件句柄和线程)的机会,并确保任何持久状态都与内存中该过滤器的当前状态保持同步。 - doFilter()方法:
每次由于对链末尾的某个资源的客户端请求二通过链传递请求/响应时,容器都会调用Filter的doFilter方法。传入此方法的FilterChain允许Filter将请求和响应传递到链中的下一个实体。
遵循以下模式:
1.检查请求
2.有选择地将带有自定义实现的请求对象包装到用于输入过滤的过滤器内容或头中
3.有选择地将带有自定义实现的响应对象包装到用于输出过滤的过滤器内容或头中
4.既可以使用 FilterChain 对象 (chain.doFilter()) 调用链中的下一个实体,也可以不将请求/响应对传递给过滤器链中的下一个实体,从而阻塞请求处理
5.在调用过滤器链中的下一个实体之后直接设置响应上的头。
4、过滤器的创建
- 打开eclipse,选择New -> Other… -> Web -> Filter,点击next
- 在创建Filter界面中,输入包名和过滤器的类名,点击next
- 修改过滤器的映射为/*,就能拦截任何请求,点击finsh
- 在Filter页面中的doFilter()方法里设置编码,并设置过滤链即可。
5、过滤器的例子
- 创建login.jsp
<form action="LoginAction" method="post">
用户名:<input type="text" name="loginName"/><br/>
密码:<input type="password" name="loginPass" /><br/>
请将验证码带到下一个页面:<input type="text" name="loginVerification" value="我是中文验证码" readonly /><br>
<input type="submit" value="登录" />
</form>
- 创建LoginAction控制器
package com.gaj.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginAction
*/
public class LoginAction extends HttpServlet {
private static final long serialVersionUID = 1L;
// 处理get请求
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
// request.setCharacterEncoding("utf-8");
// 接收loginName和loginPass
String loginName = request.getParameter("loginName");
String loginPass = request.getParameter("loginPass");
String loginVerification = request.getParameter("loginVerification");
// 逻辑判断
if(!loginName.isEmpty() && !loginPass.isEmpty()){
if(loginName.equals("admin") && loginPass.equals("admin")){
request.setAttribute("msg", "登陆成功!" + loginVerification);
}else{
request.setAttribute("msg", "用户名或密码错误!" + loginVerification);
}
}else{
request.setAttribute("msg", "用户名或密码为空!" + loginVerification);
}
// 转发
request.getRequestDispatcher("result.jsp").forward(request, response);
}
// 处理post请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 处理get请求
doGet(request, response);
}
}
- 创建result.jsp
<h1>${msg }</h1>
- 创建过滤器
package com.gaj.controller;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Servlet Filter implementation class EncodingFilter
*/
public class EncodingFilter implements Filter {
public void destroy() {
System.out.println("【重要】过滤器停止工作...");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("【重要】过滤器开始工作...");
}
}
- 修改xml拦截规则
<filter>
<display-name>EncodingFilter</display-name>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.gaj.controller.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<!-- 只拦截这一个请求 -->
<!-- <url-pattern>/LoginAction</url-pattern> -->
<!-- 拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
url-pattern设置拦截规则/*代表任何对servlet的请求,过滤器都要先拦截处理后再交给具体servlet
6、过滤器在druidweb监控的使用
druid数据源不光有出色的数据库连接的管理功能,而且还有很强悍的web管理功能。
- 修改数据库连接工具类的sqlweb统计功能
dataSource.addFilters(“stat”);
package com.gaj.utils;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
/**
* jdbc工具类
* @author Jan
*
*/
public class JDBCUtil {
// 定义连接属性
private static final String CONN_DRIVER = "com.mysql.jdbc.Driver";
private static final String CONN_URL = "jdbc:mysql://localhost:3306/jdbcdb?characterEncoding=utf-8";
private static final String CONN_USER = "root";
private static final String CONN_PASS = "root";
// 创建Druid数据源对象
private static DruidDataSource dataSource = new DruidDataSource();
// 数据源绑定属性
static{
dataSource.setDriverClassName(CONN_DRIVER);
dataSource.setUrl(CONN_URL);
dataSource.setUsername(CONN_USER);
dataSource.setPassword(CONN_PASS);
// 开启Sql语句的统计功能
try {
dataSource.addFilters("stat");
} catch (SQLException e) {
e.printStackTrace();
}
}
// 返回dataSource对象
public static DataSource getDataSouce(){
return dataSource;
}
}
- web.xml中加入druid的核心servlet控制器和过滤器
<!-- Druid,监控数据库,以及WEB访问连接信息 -->
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置 Druid 监控信息显示页面 -->
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<!-- 允许清空统计数据 -->
<param-name>resetEnable</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 用户名 -->
<param-name>loginUsername</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<!-- 密码 -->
<param-name>loginPassword</param-name>
<param-value>admin</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
- 输入地址:
http://localhost:8080/javaweb_tp3/druid即可进入登录页面,输入web.xml中对应的用户名和密码既可完成登录
- 查看sql监控页面(可以直到哪些sql消耗性能比较高需要优化)