重要的API
config
- init()和init(ServletConfig config)
getInitParameter():String
getServletConfig().getInitParameter("height")
response响应
HttpServletResponse接口属于Servlet规范,存在于servlet-api.jar中,由服务器提供接口的实现类,主要用于封装服务器的响应信息,可以将doGet或doPost的响应信息写出到【响应体】中
- ServletResponse隐藏了向浏览器发送响应的复杂过程
响应头的相关操作
addHeader(String name, String value) / addIntHeader(String name, int value) /addDateHeader(String name, long date)
- Content-Disposition Expires Cache-Control
setHeader(String name, String value) / setDateHeader(String name, long date) / setIntHeader(String name, int value)
其中,add表示添加,而set表示设置
响应输出流的操作
PrintWriter getWriter()获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲区中,随后Tomcat会将response缓冲区中的内容组装成Http响应返回给浏览器端。
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
out.println("<html>....</html>"); //可以输出html文档
out.flush();
out.close();
ServletOutputStream getOutputStream()获得字节流,通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,再由Tomcat服务器将字节内容组成Http响应返回给浏览器。
需求:动态生成验证码—防止机器人
具体应用使用组件
public class PicServlet extends HttpServlet {
private int width=120,height=40;
//允许在web.xml中针对图片的高和宽进行配置
public void init() throws ServletException {
String ss=this.getServletConfig().getInitParameter("width");
try{
width=Integer.parseInt(ss);
}catch (Exception e){
width=120;
}
ss=this.getServletConfig().getInitParameter("height");
try{
height=Integer.parseInt(ss);
}catch (Exception e){
height=40;
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedImage img=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics paint=img.getGraphics();
paint.setColor(new Color(200,200,100));
paint.fillRect(0,0,width,height);
paint.setColor(Color.black);
paint.drawRect(2,2,width-6,height-6);
//绘制动态验证码
String checkcode=this.generateCode(6);
System.out.println(checkcode);
paint.setColor(Color.red);
paint.setFont(new Font("宋体",Font.BOLD,28));//设置绘制字符所使用的字体
paint.drawString(checkcode,10,height-10);
//绘制杂点或者扰动线,避免OCR图像识别
paint.dispose();
//告知浏览器如何处理响应内容
response.setContentType("image/jpeg");
ServletOutputStream sos=response.getOutputStream();
//输出为JPG格式
ImageIO.write(img,"jpg",sos); //通过输出的字节流输出jpg格式的图片
sos.flush();
sos.close();
}
//生态动态验证码
private String source="abcdefghijklmnopqrstuvwxyz1234567890";//在验证码中允许出现的字符范围
private String generateCode(int len){
Random r=new Random();
StringBuilder sb=new StringBuilder();
for(int i=0;i<len;i++){
int pos=r.nextInt(source.length());
sb.append(source.charAt(pos));
}
return sb.toString();
}
}
web.xml的Servlet和Servlet初始化参数的配置
<?xml version="1.0" encoding="UTF-8"?>
<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">
<servlet>
<servlet-name>PicServlet</servlet-name>
<servlet-class>com.yan.action.PicServlet</servlet-class>
<init-param>
<param-name>width</param-name>
<param-value>200</param-value>
</init-param>
<init-param>
<param-name>height</param-name>
<param-value>60</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>PicServlet</servlet-name>
<url-pattern>/pic.do</url-pattern>
</servlet-mapping>
</web-app>
客户端缓存
思路:使每次访问URL都不一致,引入一个没有用的额外参数
<body onload="ff()">
<form action="login.do" method="post">
<input name="checkcode"/>
<img id="img1"/>
<script>
function ff(){
document.getElementById("img1").src='pic.do?q='+Math.random();
}
</script>
</form>
</body>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
服务器端设置避免客户端缓存
response.setContentType("image/jpeg");
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires",0);
ServletOutputStream sos=response.getOutputStream();
... ...
其它操作
指定浏览器解析页面的编码方式setContentType(String type),这里的类型定义type采用的是MIME格式的规范
- text/html表示是一个html格式的文本文档
- image/jpeg表示是一个jpg格式的图片文档
response.setContentType("text/html;charset=utf-8");
设置响应行状态码setStatus(int sc)
- 200 OK
- 404
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(404);
}
sendError(int sc, String msg)设置报错状态码,例如404,String是自定义的报错信息
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(404,"张前端来了!");
}
如何自定义报错页面
- 一般是应用完成后给客户提交之前配置的,主要作用是隐藏各种报错信息
- 如果开发过程中不建议配置
web.xml中允许配置在当前应用中报错处理页面
<!--在当前应用中如果出现404异常时,自动跳转abc.html页面-->
<error-page>
<error-code>404</error-code>
<location>/abc.html</location>
</error-page>
可以在当前应用中全局配置针对指定异常的报错页面
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int k=0;
System.out.println(10/k);
}
默认报错为【HTTP状态 500 - 内部服务器错误】
<!-- 在当前应用中如果出现Exception类型的异常则自动跳转bbb.html页面-->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/bbb.html</location>
</error-page>
request请求
HttpServletRequest接口类型,属于Servlet规范,存在于servlet-api.jar中,由服务器提供接口的具体实现类
- 主要用于封装用户的请求数据
- Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。其中,ServletRequest对象内封装了关于这个请求的许多详细信息
- request对象是从发起请求开始创建,生成响应完毕后销毁
请求头数据
long getDateHeader(String name) / String getHeader(String name) /int getIntHeader(String name)
Enumeration getHeaderNames() / Enumeration getHeaders(String name)
referer头的作用:执行该此访问的的来源,做防盗链
- 图片水印
- 盗链页面
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果地址栏中直接访问,则返回为null;如果有上一个页面,则返回该页面的地址,例如http://localhost:8080/demo3_war_exploded/login.html
String referer=request.getHeader("referer");
System.out.println(referer);
}
###请求参数
get采用的是协议头的方式传递数据,数据格式为abc.do?username=zhangsan&password=123
post采用的是协议体的方式传入数据,请求体中的内容是通过post提交的请求参数,格式是:username=zhangsan&password=123
String getParameter(String name)
String sage=request.getParameter("age"); //传递各种数据类型时只能接收到String或者String[]类型
Integer age=null;
try{
age=Integer.parseInt(sage);
}catch(Exception e){
age=null;
}
//if(age==null) 如果要求必须正确的age提交参数的报错处理,如果age值可有可无,则不做处理
String[] getParameterValues(String name)
Enumeration getParameterNames()
Map<String,String[]> getParameterMap()
Request乱码问题的解决方法
在service中使用的编码解码方式默认为ISO-8859-1编码,但此编码并不支持中文,因此会出现乱码问题,所以我们需要手动修改编码方式为UTF-8编码,才能解决中文乱码问题
中文乱码的3种解决方案:
- 针对post请求
<meta charset="GBK">
<form action="test.do" method="post">
<input name="name"/>
<input type="submit" value="提交数据"/>
</form>
java编程接收数据的处理
//设置请求编码字符集必须在所有获取请求参数之前,否则设置无效
request.setCharacterEncoding("GBK"); //这里编码字符集必须和提交数据的页面编码字符集一致
String ss=request.getParameter("name");
System.out.println(ss);
- 针对get请求
修改Tomcat的配置server.xml,添加一个配置参数URIEncoding=“GBK”
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="GBK" redirectPort="8443" />
- 通用解决方案
String ss=request.getParameter("name");
ss=new String(ss.getBytes("ISO-8859-1"),"GBK");
System.out.println(ss);
###其它方法
获得客户端的请求方式:String getMethod()
参照HttpServlet类中service方法的实现—模板模式
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod(); //获取请求方法,按照http1.1协议有8种不同的请求方法,例如get、post、delete、put等
if (method.equals(METHOD_GET)) {
doGet(req, resp);//如果请求方法是GET时,则调用doGet,在HttpServlet类中声明了方法,但是没有具体的实现,实现延迟到具体子类中进行覆盖定义
} else if (method.equals(METHOD_HEAD)) {
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
if(action==null ||action.trim().length()<1)
action="show";
try {
Class clz = this.getClass();
Method method = clz.getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this,req,resp);
} catch (Exception e) {
throw new ServletException(e);
}
}
public abstract void show(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}
用户CRUD操作
public class UserServlet extends BaseServlet {
@Override
public void show(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//路径是user.do?action=show或者user.do
}
public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//路径是user.do?action=add
}
public void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//路径是user.do?action=del
}
public void load(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//路径是user.do?action=load&id=1
}
public void modify(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//路径是user.do?action=modify
}
}
OOP编程原则:
- 要求类内高内聚、类间弱耦合
- 将一个用户的相关操作定义在4个不同的Servlet类中
- 解决方案:引入一个额外的动作参数
获得请求的资源:
String getRequestURI() /demo3/test.do 不会包含get请求的参数
StringBuffer getRequestURL() http://localhost:8080/demo3/test.do
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getRequestURI());
}
String getContextPath() —web应用的名称 /demo3
String getQueryString() ---- get提交url地址后的参数字符串 //id=123&name=yanjun
Java反射基础
构建对象
//构建一个对象,类名称是作为字符串类型的参数进行指定
Object obj=Class.forName("com.yan.action.Test2").newInstance(); //等价于new Test2();
调用方法
//构建一个对象,类名称是作为字符串类型的参数进行指定
Object obj=Class.forName("com.yan.action.Test2").newInstance();
//获取类的引用
Class clz=obj.getClass();
//查找指定的方法,参数1:方法名称为字符串类型,后续参数就是该方法的参数类型
Method method=clz.getDeclaredMethod("pp",Integer.class);
//调用查找到的方法,参数1是方法所在的对象,如果有参数才有后续的参数,后续参数就是调用方法时的实参
Object res=method.invoke(obj,10);
System.out.println(res); //null
JSP技术
JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它是由Sun Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准,它实现了在HTML标签中使用Java代码
JSP是一种动态网页技术标准,也是JavaEE的标准。JSP和Servlet一样,是在服务器端执行的。JSP是在Servlet技术发展之后为了让开发者写html标签更方便而发展起来的技术,JSP实际上就是Servlet。但是通常把Servlet作为Web应用中的控制组件来使用,只负责响应请求产生数据,并把数据通过转发技术带给jsp,而把jsp技术作为数据显示模板来使用。这样使程序结构更清晰,可读性和可维护性更高。
Hello JSP
究其本质jsp页面就是包含Java代码的html网页
1、编写一个html网页
- 执行访问不会在work文件夹下生成任何内容
2、将html后缀修改为.jsp
- 自动在work下生成了两个文件,一个名为index_jsp.java,另外一个名为index_jsp.class
- jsp并不能直接执行,需要先通过jsp引擎将jsp文件转换为servlet的类文件,然后通过Servlet引擎将servlet类文件编译为.class字节码文件,然后才能执行并生成html文档
3、可以在jsp文件中使用java语言编写相关的脚本程序
<table width="80%">
<caption>99</caption>
<%
for(int i=1;i<10;i++){
%>
<tr>
<%
for(int k=1;k<=i;k++){
%>
<td><%=k%>*<%=i%>=<%=i*k%></td>
<%}%>
</tr>
<%
}
%>
</table>
JSP基础语法
###注释
从软件工程化管理上说,不写注释是不合理的,一般建议编程中20%-30%左右的编码量为注释
JSP注释
<%-- 多行内容 --%>
会被编译器所忽略,其中可能有语法错误,但是不影响编译
html注释
<!--多行内容-->
其中的内容会被编译执行,只是在浏览器中不进行显示
###模板元素
直接写在jsp页面的html内容称之为jsp页面的模板元素;模板元素在翻译过来的servlet中被out.write()原样输出到浏览器中
out.write("<table width=\"80%\">\r\n");
out.write("\t<caption>99</caption>\r\n");
out.write("\t");
for(int i=1;i<10;i++){
out.write("\r\n");
out.write("\t<tr>\r\n");
out.write("\t\t");
for(int k=1;k<=i;k++){
out.write("\r\n");
out.write("\t\t<td>");
out.print(k);
out.write('*');
out.print(i);
out.write('=');
out.print(i*k);
out.write("</td>\r\n");
out.write("\t\t");
}
out.write("\r\n");
out.write("\t</tr>\r\n");
out.write("\t");
}
out.write("\r\n");
out.write("</table>");
###JSP表达式 --取值
常量 : <%=“hello”%>
<% int k=100; %>
<%=k%> 转换的结果为out.write(k);
- 不能定义合法的java代码,因为out.write()的原因
- 不能定义方法,因为代码段中的内容会出现在
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
- 可以通过匿名内部类的方式定义类
- 注意:默认不能有;号
变量 :<%String name=“aa”;%> //定义变量
<%=name%> //取变量的值
<%
和%>
之间应该是合法的java语句,允许定义多行程序- 不允许直接定义方法,不允许定义属性,可以定义临时变量,可以通过内部类的方式定义类【方法中允许定义类】
<%
int res=0;
for(int i=1;i<=100;i++)
res+=i;
%>
<h3>1+2+3+...+100=<%=res%></h3>
对应编译的结果为
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException {
... ...
int res=0;
for(int i=1;i<=100;i++)
res+=i;
out.write("\r\n");
out.write("<h3>1+2+3+...+100=");
out.print(res);
out.write("</h3>");
....
}
代码段的编译结果会出现在_jspService方法内部
声明:
语法规则: <%! 多行声明属性或者方法之类的内部成员 %>
<%!
private int counter=0;
public void cc(){}
%>
编译生成的类定义
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private int counter=0;
public void cc(){}
- 允许定义方法或者属性以及内部类
- 允许定义静态代码块或者非静态代码块
- 不允许直接写执行语句,例如
out.println(100);
- 不能定义临时变量
需求:
页面计数器:统计当前页面的访问次数
默认Servlet的运行特征为单实例多线程的方式运行service方法,而jsp运行前需要编译为类,所以运行特征也应该是单实例多线程的方式运行_jspService方法
<%@ page contentType="text/html;charset=utf-8" %>
<%! private int counter=0; %>
您是第<%=++counter%>位访问者
因为_jspService是以多线程的方式运行,修改counter可能会导致安全问题
<%@ page contentType="text/html;charset=utf-8" %>
<%! private static int counter=0;
public synchronized void pp(){
counter++;
}
%>
<% pp(); %>
您是第<%=counter%>位访问者,IP地址为<%=request.getRemoteAddr()%>
###3大JSP页面指令
位置: 可以写在JSP页面的任意位置
格式:<%@ 指令名称 若干属性声明%>
- 使用IDEA开发页面的默认存储位置为src/main/webapp/目录下,允许自定义目录
####page指令 --用来声明当前JSP页面的基本属性
<%@ page language="java" %>
– 指定当前JSP使用的语言是java
####pageEncoding和contentType
<%@ page pageEncoding="UTF-8"%>
– 用来通知JSP解析引擎使用指定的编码来翻译JSP
注意:如果想防止JSP乱码,应该要保证JSP文件保存时的编码和pageEncoding指定的编码保持一致
<%@ page contentType="text/html;charset=utf-8" %>
类似于response.setContentType(“text/html;charset=utf-8”)
从语义上说两个属性不是一回事,但是经常会相互替代
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
当前页面的异常处理
<%@ page errorPage="bbc.jsp"%>
如果在当前页面中出现异常自动跳转的页面
JSP中9大默认对象:exception
<%@ page isErrorPage=“true” %>表示在当前bbc.jsp页面中可以通过exception对象获取前面出现的异常
具体实现有异常的页面bbb.jsp
<%@ page contentType="text/html;charset=UTF-8" errorPage="bbc.jsp" language="java" %>
<%
int k=0;
out.println(10/k);
%>
定义bbc.jsp页面用于显示bbb.jsp页面种出现的异常
-
要求必须设置参数
<%@ page contentType="text/html;charset=UTF-8" isErrorPage="true" language="java" %>
,如果不配置isErrorPage则不能使用exception默认对象 -
显示方式1:
<%=exception%>
-
显示方式2:
<% exception.printStackTrace(); %>
在控制台上显示报错信息 -
显示方式3:在页面上显示调用栈
<% exception.printStackTrace(new PrintWriter(out)); %>