1.1、注解的概述
注释是给人看的,而注解是给程序看的!
注释是用来替代配置文件的!你回忆一下,我们以前总是要写一些配置文件,例如web.xml你还记得么?里面要写<servlet>和<servlet-mapping>!谁来读配置文件呢?当然是Tomcat!谁来写配置文件呢?当然是我们来写了!
在Servlet3.0中就可以使用使用注解来代替配置文件,开发者就不用再写配置文件了,而是写注解,然后Tomcat来读取注解。
注解也是类,需要定义了才能使用!
分别在Servlet3.0中有一个注解类为@WebServlet,然后我们就可以在Servlet中使用@WebServlet中使用这个注解了。这个注解就是用来替代<servlet>了。然后Tomcat会通过反射来读取注解中的信息!
1.2、Java中的注解
-
@Overrid:作用在方法上的注解。当方法不是重写父类的方法时会报错;
-
@Deprecated:作用在方法上。标记该方法为作废方法(已过时);
-
@SuppressWarnings:作用在方法上,压制警告。
1.3、定义注解类
定义注解类不能使用class、enum,也不能使用interface,而是使用@interface。
public @interface MyAnn{}
1.4、使用注解目标
注解可以作用在:类(接口或枚举)、属性、方法、构造器、包、参数、局部变量
@MyAnn public class MyClass { @MyAnn private int a; @MyAnn public MyClass() {} @MyAnn public void fun1() {} @MyAnn public void fun2(@MyAnn String s) { @MyAnn int n = 10; } }
1.5、注解的属性
定义注解时也可以给出属性
public @interface MyAnn { String value(); int value1(); }
其中value就是属性!你可能会说,它是一个方法!没错,它是一个方法,但我们非要称之为属性,因为把它当做属性更加好理解。
当为注解指定属性后,那么在使用注解时就必须要给属性赋值了:
@MyAnn(value1=100,value="hello") public class MyClass { }
注解的属性还可以有默认值,在使用注解时就可以不给带有默认值的属性赋值了。但没有给出默认值的属性还是要赋值的。、
public @interface MyAnn { String value() default "hello world"; int value1(); } @MyAnn(value1=100) public class MyClass { }
在使用注解时,如果只给名为value的属性赋值,那么可以不给出属性的名称直接给出值。
public @interface MyAnn { String value() default "hello world"; int value1() default 100; } @MyAnn() public class MyClass { } @MyAnn(value="hello") public class MyClass { } @MyAnn(value1=200) public class MyClass { } @MyAnn(value="hello",value1=200) public class MyClass { } @MyAnn("hello annocation") public class MyClass { } @MyAnn(300) public class MyClass { } @MyAnn("hello",value1=200) public class MyClass { }
-
注解的属性后面要有一对圆括号,而且圆括号内不能给出东西。就像是无参的方法一样;
-
注解的属性类型只能是:基本类型、String、Enum、Class、注解类型、以上类型的一维数组类型;
-
注解的属性可以有默认值,例如:int a() default 100;
-
数组的属性默认值:int[] arr() default {1,2,3},这里不能使用new int[]{1,2,3}
-
使用注解时,在给数组属性赋值时的格式:@MyAnn(arr={1,2,3});
1.6、注解的作用目标
在定义注解时可以限制注解的作用目录!例如让注解只能作用在类和方法上。
这需要使用元注解:@Target。该注解有一个属性value,类型为ElementType[],它是枚举类型。
public @interface Target { ElementType[] value(); } public enum ElementType { TYPE,FIELD,METHOD,PARAMETED,CONSTRUCTOR,LOCAL_VARIABLE,ANNOCATION_TYPE,PACKAGE }
在定义注解时,可以使用@Target注解来限制注解的作用目标:
@Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAnn { }
这样MyAnn就只能作用在类和方法上的!其中ElementType.TYPE表示类和接口。
@MyAnn() public class MyClass { @MyAnn() private int a; @MyAnn() public void fun() {} }
1.7、注解的保留策略
注解的保留策略是指,注解是只保留在源代码上,还是保留到class文件上,再或者是类在运行时,可以被类加载器加载到内存中。
如果希望注解被反射,那么注解就要保留到运行时,而不是源代码或类文件上。
指定注解的保留策略需要使用元注解@Retention,它有一个value属性,类型为RetentionPolicy类型,RetentionPolicy是枚举类型:
public @interface Retention { RetentionPolicy value(); } public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
下面代码是指定注解保留到运行时
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAnn { String value() default "hello"; int value1() default 100; }
1.8、通过反射读取注解
读取注解需要使用反射来完成
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAnn { String value() default "hello"; int value1() default 100; } @MyAnn(value="hello world", value1=200) public class MyClass { private int a; @MyAnn("myMethod") public void fun() {} } public class Demo1 { public static void main(String[] args) throws Exception { Class clazz = MyClass.class; MyAnn myAnn = (MyAnn) clazz.getAnnotation(MyAnn.class); System.out.println(myAnn.value()); System.out.println(myAnn.value1()); Method method = clazz.getMethod("fun"); MyAnn myAnn1 = method.getAnnotation(MyAnn.class); System.out.println(myAnn1.value()); System.out.println(myAnn1.value1()); } }
二、Servlet3.0新特性
2.1、Servlet3.0新特性概述
Servlete3.0的主要新特性如下三部分:
-
使用@WebServlet、@WebFilter、@WebListener三个注解来替代web.xml文件中的Servlet、Filter、Listener的配置;
-
Servlet异步处理:当Servlet处理比较费时的问题时,这会让客户感觉到很卡。当使用异常处理时可以把已经处理好的内容先一步响应给客户端浏览器,然后使用另一个线程来完成费时的操作,也就是把内容一部分一部分的显示出来;
-
上传组件:不用再使用fileupload等第三方的上传组件,使用Servlet3.0的上传组件会更方便。
2.2、@WebServlet、@WebFilter、@WebListener
@WebServlet( urlPatterns={"/AServlet"}, initParams={@WebInitParam(name="paramName",value="paramValue")}, loadOnStartup=1 ) public class AServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { System.out.println(config.getInitParameter("paramName")); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); response.getWriter().print("Hello World!"); } }
@WebFilter(urlPatterns={"/*"}, dispatcherTypes={DispatcherType.REQUEST, DispatcherType.FORWARD}) public class AFilter implements Filter { public void destroy() {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("start filter"); chain.doFilter(request, response); System.out.println("end filter"); } public void init(FilterConfig fConfig) throws ServletException {} }
@WebListener() public class AListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { System.out.println("服务器关闭了"); } public void contextInitialized(ServletContextEvent arg0) { System.out.println("服务器启动了"); } }
2.3、Servlet异步处理
Servlet异步处理就是让Servlet在处理费时的请求时不要阻塞,而是一部分一部分的显示。
也就是说,在使用Servlet异步处理之后,页面可以一部分一部分的显示数据,而不是一直卡,等到请求响应结束后一起显示。
在使用异步处理之前,一定要在@WebServlet注解中给出asyncSupported=true,不然默认Servlet是不支持异步处理的。如果存在过滤器,也要设置@WebFilter的asyncSupportedt=true。
@WebServlet(urlPatterns = {"/MyServlet"}, asyncSupported=true) public class MyServlet extends HttpServlet {…}
注意,响应类型必须是text/html,所以:response.setContentType(“text/html;charset=utf-8”);
使用异步处理大致可以分为两步:
-
Servlet正常响应数据;
-
Servlet异常响应数据
在Servlet正常响应数据时,没什么可说的,可通知response.getWriter().print()来向客户端输出,但输出后要使用response.getWriter().flush()刷新,不然数据只是在缓冲区中,不能向客户端发送数据的。
异步响应数据需要使用request.startAsync()方法获取AsyncContext对象。然后调用AsyncContext对象的start()方法启动异步响应,start()方法需要一个Runnable类型的参数。在Runnable的run()方法中给出异步响应的代码。
AsyncContext ac = request.startAsyncContext(request, response); ac.start(new Runnable() {…});
注意在异步处理线程中使用response做响应后,要使用response.getWriter().flush()来刷新流,不然数据是不能响应到客户端浏览器的。
asyncContext.start(new Runnable() { public void run() { for(char i = 'a'; i <= 'z'; i++) { try { Thread.sleep(100); asyncContext.getResponse().getWriter().print(i + " "); asyncContext.getResponse().getWriter().flush(); } catch (Exception e) { e.printStackTrace(); } } asyncContext.complete(); } });
Tomcat需要知道异步响应是否结束,如果响应不结束,虽然客户端浏览器会看到响应的数据,但是鼠标上只是有个圈圈的不行的转啊转的,表示还没有结束响应。Tomcat会等待到超时为止,这个超时的时间可以通过AsyncContext类的getTimeout()方法获取,Tomcat默认为20000毫秒。当然也可以通过setTimeOut()方法设置,以毫秒为单位。ac.setTimeout(1000*10)。
如果异步线程已经结束了响应,那么可以在异步线程中调用AsyncContext.complete()方法,这样Tomcat就知道异步线程已经完成了工作了。
@WebServlet(urlPatterns = {"/AServlet"}, asyncSupported=true) public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.println("Servlet begin <br>"); out.flush(); final AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(1000 * 20); asyncContext.start(new Runnable() { public void run() { try { Thread.sleep(1000); asyncContext.getResponse().getWriter().print("马上开始" + "<br/>"); asyncContext.getResponse().getWriter().flush(); Thread.sleep(2000); } catch (Exception e1) { } for(char i = 'a'; i <= 'z'; i++) { try { Thread.sleep(100); asyncContext.getResponse().getWriter().print(i + " "); asyncContext.getResponse().getWriter().flush(); } catch (Exception e) { e.printStackTrace(); } } asyncContext.complete(); } }); // asyncContext.start(businessHandleThread); // 也可以用这种方法启动异步线程 out.println("Servlet end <br>"); } }
2.4、文件上传
Servlet3.0提供了文件上传的处理方案。只需要在Servlet上添加@MultipartConfig注解即可。
@WebServlet(urlPatterns={"/UploadServlet"}) @MultipartConfig(maxFileSize=1024) public class UploadServlet extends HttpServlet { … }
当然也可以为@MultipartConfig注解指定属性值,它有四个属性:
-
int filesizeThreshold:指定缓存的大小,当超出这个大小后,文件会保存到磁盘上;
-
String location:指定临时文件的目录;
-
long maxFilesize:指定上传单个文件的大小限制,如果上传的谁的超出了这个大小,那么就会抛出异常;
-
long maxRequestSize:指定整个表单的大小限制。
当在Servlet上使用了@MultipartConfig注解后,那么就可以使用request.getPart(“fieldName”)来获取input:file的内容,其中Part表示一个文件表单项。
<form action="/a1/UploadServlet" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="username"/><br/> 照 片:<input type="file" name="file1" /><br/> <input type="submit" value="提交"/> </form>
@WebServlet(urlPatterns={"/UploadServlet"}) @MultipartConfig(maxFileSize=1024 * 1024) public class UploadServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); response.getWriter().print("size: " + username + "<br/>"); Part part = request.getPart("file1"); response.getWriter().print("size: " + part.getSize() + "<br/>"); response.getWriter().print("type: " + part.getContentType() + "<br/>"); response.getWriter().print("name: " + part.getName() + "<br/>"); String name = part.getHeader("content-disposition"); String fileNameTmp = name.substring(name.indexOf("filename=")+10); String fileName = fileNameTmp.substring(0,fileNameTmp.indexOf(""")); System.out.println("fileName: " + fileName); String savepath = this.getServletContext().getRealPath("/uploads"); part.write(savepath + "/" + fileName); } }