web(24)_web注解开发&使用web3.0完成文件上传功能
一、注解
【注解的概述】
1.注解(annotation)与注释的区别
>注释是给开发人员看的,说明某个代码的功能
>注解是给虚拟机看的,让虚拟机看到程序中的注解,代表程序的一些特殊的功能.
比如,@override代表重写,虚拟机看到@override就会检测该方法是否与
父类的方法名,返回值类型,参数一样。一样就是子类的重写。
【JDK提供的注解】
1.@override:
重写:给一个方法添加@override以后,虚拟机就会检查该方法的方法名,返回值,参数是否与父类的方法一样,不一样就会报错。添加override能防止写错。
1.6及其以后的版本,注解可以用在类的继承和类的接口实现上面。
》ps:重载是函数名字一样。参数或返回值类型不一样
2.@SuppressWarnings("unused") 压制警告(小黄色感叹号)
比如 int i;没有初始化会显示黄色的感叹号,项目依旧能运行。加上@SuppressWarnings("unused")
在方法上,可以消除黄色感叹号
双引号""中有很多值。记住一个"all":@SuppressWarnings("all")就可以消除所有的感叹号
3.@DEprecated:描述方法过时
比如,date.toLocaleString():是个过时的方法
可以自己写一个方法,加上@DEprecated 表示这个方法过时
4.自定义注解:
1)格式:@interface 注解名称
比如 @interface Anono1{}
然后再其他方法中就可以写@Anono1,虽然没有意义
2)自定义注解的属性:注意是用()
{int a();String s()}
只有基本数据类型,class,String,enumeration..才允许定义在注解中
3)使用:@myAnono1(a=1,s="abc") 给注解的属性赋值.不然会报错
给属性赋初始值:
{int a() default 2;
boolean b() defalut false;
}
4)自定义属性为class类型的对象
{Class c()}
使用:@Anono(c=User.class)
5)只有一个属性时,并且当注解中的属性名为"value"时,在引用时可以省略名字
比如:@Anno2("abc");
@interface Anno2
{String value();}
6)引用注解的多个属性要用","逗号隔开
了解注解是为了在web3.0中支持用注解代替xml配置。另外在以后的三大框架中也支持注解开发。
二、案例1:模仿Junit自定义测试注解@Test
核心分析【注解的寿命】
1.自定义的存在阶段:
在java中,一个类有三个阶段:
源码阶段:A.java
class阶段:A.java经过编译后变成A.class
运行阶段:JVM通过ClassLoader类加载器执行程序
2.默认情况下,自定义的注解存在于源码阶段,在生成Class和运行阶段就不存在了。
这时候,要想延长自定义注解的寿命,要使用元注解Retention(元注解:用于修饰其他的注解)
@Retention(value=RetentionPolicy.RUNTIME)
设置成运行阶段.
RetentionPolicy:是一个枚举类型->枚举:有限个数据
代码1:【案例1:模仿Junit自定义测试注解@Test】
AnnotationDemo3.java:
public class AnnotationDemo3 {
@MyTest
public void demo1() throws Exception {
System.out.println("demo1运行了...");
}
@MyTest
public void demo2() throws Exception {
System.out.println("demo2运行了...");
}
public void demo3() throws Exception {
System.out.println("demo3运行了...");
}
}
MyTest.java
/*
* 自定义注解MyTest,模仿Junit的@Test运行测试类
*使用@Retention注解将注解的寿命设置延长至运行阶段
*/
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyTest {
}
CoreRunner.java
/*
* 核心运行类:
* 通过反射获得测试类的Class
* 遍历每个方法,查看每个方法上面是否有MyTest注解
* 有@MyTest注解,就执行这个方法
*
* 规定:测试的方法必须时public类型
*/
public class CoreRunner {
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class clazz = AnnotationDemo3.class;
Method[] methods = clazz.getMethods();
for(Method method:methods)
{
//核心方法:判断这个方法是否含有注解@myTest
/*
* 注意默认情况下注解设置自定义注解的寿命
* isAnnotationPresent(MyTest.class)
* 表示是否存在某个指定的注解
*
*/
boolean b = method.isAnnotationPresent(MyTest.class);
if(b)
{
method.invoke(clazz.newInstance(),null);
}
}
}
}
三、案例2:WEB3.0文件上传功能
1.【文件上传概述】》文件上传:QQ头像,简历上传,文件发送等多方面使用到。在网站维护中,上传商品的图片到服务器中。
>在技术方面,文件上传指的是将本地的文件变成流写到服务器上。
>文件上传技术:
*利用servlet 3.0:比servlet2.5多的功能:注解开发,文件上传,异步请求(用的不多)
*JSPSmartUpload: 嵌入到JSP中,用的不多。
*FileUpload:来自Apache的文件上传组件
*Struts框架:已经封装好了文件上传功能。底层是FileUpload;
>文件上传的要素:
*表单提交的方式必须是POST
理由是POST上传方式没有数据的大小限制
*表单中需要有文件上传的表单元素:这个表单中必须有name属性和值:<input type="file" name="upload">
*表单的enctype属性的值必须是multipart/form-data
2.【Servlet3.0注解开发】
Servlet3.0提供了三个新的特性:
1)*注解开发:
(1)Servlet注解配置:
[1]Servlet配置:
在2.5中,要在web.xml中写一堆才能访问到Servlet.
在3.0中,在类上面添加一个注解:@webServlet就可以访问了
a.@WebServlet(urlPatterns="/ServletDemo1")
b.@WebServlet("/ServletDemo1")
[2]属性配置:
a.启动时就会创建Servlet属性:
@WebServlet(urlPatterns="/ServletDemo1",loadOnStartup=2)
2表示数值越大优先级别越高
启动服务器直接创建好Servlet,可以在init()方法中打印看到
b.配置初始化参数:
@WebServlet(initParams={@WebInitParams(name="username",value="root")});
配置多个初始化参数的话,逗号隔开多个@WebInitParams数组注解
[3]监听器配置:
@WebListener使用这个注解就好
记住,监听器有三类:例如创建MyServletContextListener,要实现接口 ServletContextListener
[4]过滤器配置:
@WebFilter(urlPatterns="/*")
记得在doFilter()方法中执行过滤器链放行方法
chain.doFilter(req,res);不然访问不到的。
2)*文件开发
3)*异步请求
3.【文件上传的抓包分析】
1.新建一个文件上传upload.jsp:
表单要满足三个条件:
1)表单上传方式必须是post方法
2)表单元素必须要有name和值
3)表单的属性enctype="multipart/form-data"
2.抓包分析:没有设置enctype的时候,会是怎么样的?
火狐或者谷歌访问页面,按F12 点击上传
没有设置enctype头:抓到的包显示的是一堆请求头,在请求体上只有文件名称,而没有文件内容
设置enctype属性:获得到文件名,文件内容。有了文件内容,我们可以直接写一个流
4.【文件上传原理分析】
1)在抓包分析中,如果有设置enctype="multipart/form-data",我们可以得到一个请求头:
multipart/form-data; boundary=---------------------------61421105010721
用request.getHeader("Content-Type");
截取boundary得到分割线:---------------------------61421105010721
2).由于上面使用了enctype属性,所以只能用request.getInputStream()得到这个请求体的内容的流形式
将流转换成String形式,得到整个请求体的内容。然后我们可以领用分割线来切割字符串。
3)当然,在web3.0中 。上述的工作都已经完成了。不用我们去做
5.【文件上传的代码实现】
【步骤一:】设置一个文件上传页面
【步骤二:】点击提交按钮,提交到Servlet中
【步骤三:】在Servlet中接收参数
【步骤四:】获得服务器文件上传的路径
【步骤五:】通过流写到该路径下就OK了
1)首先在UploadServlet中加一个注解:
@MultipartConfig才可以使用方法
【案例2:WEB3.0文件上传功能】代码
upload.jsp:
<%@ 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>
<!--
*表单提交的方式必须是POST
*表单中需要有文件上传的表单元素:这个表单中必须有name属性和值:<input type="file" name="upload">
*表单的enctype属性的值必须是multipart/form-data
-->
<h1>文件上传页面</h1>
<form method="post" action="${pageContext.request.contextPath}/UploadServlet" enctype="multipart/form-data">
<table border="1" width=600px>
<tr>
<td>文件描述:</td>
<td><input type="text" name="filedesc" /></td>
</tr>
<tr>
<td>文件上传:</td>
<td><input type="file" name="upload" /></td>
</tr>
<td colspan=2><input type="submit" value="上传" /></td>
</tr>
</table>
</form>
</body>
</html>
UploadServlet.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
*
*/
@WebServlet("/UploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收普通数据:
request.setCharacterEncoding("UTF-8");
String filedesc = request.getParameter("filedesc");
//接收文件
//得到name=upload的那块请求体内容
//也就是包含文件名和文件内容的那块分割线
Part part = request.getPart("upload");
long size = part.getSize();
//提供API不全,没法获得文件名。自己截取
String header=part.getHeader("content-Disposition");
//在头字符串中找到filename
int idx = header.lastIndexOf("filename=\"");
//filename="aaa.txt"
//idx+10:filname 8; =" 2;8+2=10
String filename=header.substring(idx+10, header.length()-1);
//获得文件内容:
InputStream is = part.getInputStream();
//开始获得文件的服务器路径:
String path = this.getServletContext().getRealPath("/upload");
OutputStream os = new FileOutputStream(path+"/"+filename);
//有输入流和输出流,两个流对接就好了
byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes))!=-1)
{
os.write(bytes, 0, len);
}
is.close();
os.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}