昨日重点提点:
1)javaEE经典三层架构:
servlet:控制服务流程
service:服务业务
dao:dao操作数据库
2)我们写的所有的servlet都会编译到web-inf文件夹下面的classes去(或者是工程下面的build文件夹下的classes中去)
web-inf文件夹下面的文件原则上不可访问,是受保护的(但还是有方法可以访问的,但是只允许服务器内部进行访问)
斜线其实就代表的是webContent文件夹下的文件
路径问题:
ServletContext.getRealPath(路径):路径写法从webContent文件夹下面开始写。
ServletContext.getResource(路径):路径写法也是从webContent文件夹下面开始写。
response
本质上是:HttpServletResponse接口对象
response操作的三大部分:
操作响应行;
操作响应头;
操作响应体;
(一)操作响应行:
常见api:
response.setStatus(int status)
针对没有问题的情况,1XX,2XX,3XX
response.setError(int Status)
response.setError(int Status,String 描述);
针对4XX,5XX
状态码单纯设置是没有太大意义的,要结合响应头设置一起使用。
(二)操作响应头:
格式:key:value(可能有多个)
重要的api
response.setHeader(key , value) //如果这个key有值,会被覆盖,重新设置一个值。
了解的api
response.setIntHeader(key ,value)
response.setDateHeader(key ,value )
response.addHeader(Key , value) //添加一个key的多个value值的时候使用
response.addIntHeader(key ,value )
response.addDataHeader(key ,value )
常见的响应头:
content-type:响应的内容的类型
text/html;charset=utf-8;
location:重定向的时候,需要节后302状态码使用
refresh:告诉浏览器刷新去哪里
秒数;url=连接的地址
content-disposition:告诉客户端如何处理内容
attachment;filename=文件名(附件下载)
重定向
//重定向的两种方法
//重定向
response.setStatus(302);
response.setHeader("location", "/Day15_0622_03MyLoginDemo/successLogin");
response.sendRedirect("路径") 重定向的简便操作!
response.sendRedirect("/Day15_0622_03MyLoginDemo/successLogin" )
//刷新到别的页面
respoonse.getWriter().println("页面3秒后跳转")
response.setHeader("refresh","1;url=/Day16_response/dajiang.html")
和重定向的区别,状态码不会出现302,正常情况下应该全部是200的状态码
1)状态码不一样,重定向的状态码是302,使用刷新方法的状态码是200;
2)设置的response响应体的内容不同;
重定向是设置状态码setStatus结合响应头中的location设置的自动跳转;
刷新方法使用的仅仅是响应头中的refresh,设置了跳转秒数和跳转url
3)重定向方法用户不易察觉,两次请求间隔时间很短;刷新方法可以指定跳转间隔时间;
其实重定向方法和刷新方法总的来说都是发送了两次请求。
刷新的另一种方式:(常用)
在html头中写meta标签
<meta http-equiv="refresh" content="3;url=地址">
//中文编码问题
response.setContentType(“text/html;charset=utf-8”)
setContentType方法里面其实有两步
分别设置了编码规则和浏览器的解码规则。
(三)操作响应体:向页面上输出的内容
PrinterWriter:输出字符(常用)
ServletOutputStream:输出字节
(输出的内容如果不是文本就用这个)
1)PrinterWriter
Printer writer = response.getWriter();
//返回值就是PrinterWriter
writer.println("hello print writer");
//向页面输出一个复杂内容,比如表格
writer.println("<table border='1px'>");
writer.println("<tr>");
writer.println("<td>");
writer.println("username");
writer.println("</td>");
writer.println("</tr>");
writer.println("</table>");
//以后可以通过jsp来写,jsp本质上也是一个servlet
//Java server page
2)ServletOutputStream
用于输出二进制的数据内容的时候使用
不能够和PrintWriter同时使用,会illegal,报500异常
ServletOutputStream os = response.getOutputStream();
os.print(“hello outputStream”); //纠正,以后不要写成把print写成了write了
//这种方法是二进制的写法,不能够直接输出中文
//可以看看PrintWriter的print方法的源码,只要不是规定的编码格式iso_8859_1就报异常
//这种方式要这样输出中文
response.setContentType(“text/html;charset=utf-8”);
os.write(“张三”.getBytes(“utf-8”)); //这里就要写成write,因为转换成了二进制数据输出了
//这种写法方式绕过了print底层源码的只要不是is0_8859_1编码就报错的方式。直接改为二进制输出了。
PrintWriter和ServletOutputStream:
1)两个流不能够同时使用,报500异常。互斥;
!关于换行问题:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType(“text/html;charset=utf-8”);
javax.servlet.ServletOutputStream os = response.getOutputStream();
os.println("outputStream success!");
os.println("outputStream success!");
os.println();
os.write("哈哈哈".getBytes("utf-8"));
}
执行结果:
上述代码是表示用html格式打开文件,需要换行的时候需要手动加上一个br标签
(加了Contenttype里面有一个话,text/html,告诉程序以html格式打开文件,而html中擅自加换行是没有用的,没有显示效果,需要加标签)
重要案例: 下载文件
下载方式一: 超链接下载方式
浏览器智能后的后果,浏览器能够直接解析,就会直接显示。不会下载了。要下载就要目标另存为
经测试,txt文件和img文件是可以直接在浏览器中打开的;
doc文件和zip等文件链接之后是需要下载的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div>
<a href="/Day16_0623_02DownloadDemo/Files/file1.doc">file1</a><br/>
<a href="/Day16_0623_02DownloadDemo/Files/img1.jpg">img1</a><br/>
<a href="/Day16_0623_02DownloadDemo/Files/txt1.txt">txt1</a><br/>
</div>
</body>
</html>
下载方式二:用编码方式下载文件:
download为一个servlet程序,后面接的?表示默认的get请求,后面接提交参数
编码方式:涉及到两个头一个流
1)流:servletOutputStream
2)content-type:指定文件类型
3)content-disposition:内容的处理方式
步骤:
获取要下载的文件的名称
设置content-type
设置content-disposition
输出流,从文件输入流中获取数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取要下载的文件的名称
String name = request.getParameter("name");
ServletContext servletContext = this.getServletContext();
//编码下载方式需要设置两个头一个流
String mimeType = servletContext.getMimeType(name);
//设置一个头:ContentType
response.setContentType(mimeType);
//设置一个头:Content-disposition
response.setHeader("content-disposition", "attachment;filename="+name);
//一个流:getOutputStream
//获得输出流
ServletOutputStream os = response.getOutputStream();
//从文件的输入流中获取数据
InputStream is = servletContext.getResourceAsStream("/Files/"+name);
//拷贝
byte[] bytes = new byte[8192];
int len=0;
while((len = is.read(bytes))!=-1){
os.write(bytes, 0, len);
}
//关闭流,os流是不用关闭的,服务器会自动调用完成后关闭。
is.close();
}
这个案例的问题:
所有的文件名字都是数字和中文
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//两个流一个头
ServletContext servletContext = request.getServletContext();
//获取文件名
String name = request.getParameter("name");
System.out.println(name);
//获取文件的mime类型
String mimeType = servletContext.getMimeType(name);
//演示一下为了避免中文乱码的问题:
String encode = URLEncoder.encode("下载.jpg", "utf-8");
//设置头:contentType
response.setContentType(mimeType);
//设置头:content-disposition
response.setHeader("content-disposition", "attachment;filename="+encode);
//输入流
InputStream is = servletContext.getResourceAsStream("/Files/"+name);
//输出流
ServletOutputStream os = response.getOutputStream();
//拷贝文件
byte[] bytes = new byte[8192];
int len = 0;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
is.close();
}
//案例说明:
这个案例用URLEncoder.encode()修改的,只是下载后显示的文件的名字,如上例,下载后显示的文件名称会变成“下载.jpg”;但是连接中的文件名字和取web项目中查找的name都是没有改变的。
这种将中文转化成为utf-8的编码方式的形式适用于chrome、ie等浏览器,但是不包括火狐浏览器,因为火狐采用的不是utf-8的编码方式
利用工具类,根据响应的浏览器选择响应的编码方式处理中文乱码问题
public class Img1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//两个流一个头
ServletContext servletContext = request.getServletContext();
//获取文件名
String name = request.getParameter("name");
System.out.println(name);
//获取文件的mime类型
String mimeType = servletContext.getMimeType(name);
//演示一下为了避免中文乱码的问题:
// String encode = URLEncoder.encode("下载.jpg", "utf-8");
//获得request中的请求头中的user-agent
String agent = request.getHeader("user-agent");
//利用工具类,根据不同的浏览器对含有中文的文件名进行编码
String name2 = DownLoadUtils.getName(agent, "测试.jpg");
//设置头:contentType
response.setContentType(mimeType);
//设置头:content-disposition
response.setHeader("content-disposition", "attachment;filename="+name2);
//输入流
InputStream is = servletContext.getResourceAsStream("/Files/"+name);
//输出流
ServletOutputStream os = response.getOutputStream();
//拷贝文件
byte[] bytes = new byte[8192];
int len = 0;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
is.close();
}
案例:点击切换验证码
作用:防止暴力攻击
方式一:(low的方式)
利用给的servlet文件CodeServlet.java
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>点击切换验证码</title>
<script>
function changeCode(){
document.getElementById("img1").src="/Day16_0624_01ChangeCodeDemo/code?i="+Math.random();
}
</script>
</head>
<body>
<div>
验证码<img id="img1" src="/Day16_0624_01ChangeCodeDemo/code" onclick="changeCode()" />
</div>
</body>
</html>
方式二:
使用第三方的jar包
有两个包:commons-io-1.4.jar ValidateCode.jar
public class Code2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ValidateCode validateCode = new ValidateCode(300, 120, 4, 1500);
//获得随机生成的code,后期校验使用
String code = validateCode.getCode();
//输出生成的验证码图片
validateCode.write(response.getOutputStream());
}
内省技术:Introspector
内省(Introspector)是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。
一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。
面试:请说一下内省和反射的区别:
1)内省底层还是使用的反射技术, 只是做了一层封装, 主要是用来操作标准javabean, (标准javabean里面有get/set方法 );操作的范围比反射要小。
2)内省里面一个重要的东西是:PropertyDescriptor 属性描述器
3)简单来说:内省主要是操作javabean属性,get/set方法
反射: 可以操作类里面所有的字段,属性,方法,构造
两者都是通过字节码文件实现的。