Servlet3.0新特性&动态代理

ervlet3.0新特性



1 Servlet3.0新特性概述

   Servlete3.0的主要新特性如下三部分:

 使用@WebServlet、@WebFilter、@WebListener三个注解来替代web.xml文件中的Servlet、Filter、Listener的配置;

 Servlet异步处理:当Servlet处理比较费时的问题时,这会让客户感觉到很卡。当使用异常处理时可以把已经处理好的内容先一步响应给客户端浏览器,然后使用另一个线程来完成费时的操作,也就是把内容一部分一部分的显示出来;

 上传组件:不用再使用fileupload等第三方的上传组件,使用Servlet3.0的上传组件会更方便。



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("服务器启动了");

  }

 }



3 Servlet异步处理

Servlet异步处理就是让Servlet在处理费时的请求时不要阻塞,而是一部分一部分的显示。

 也就是说,在使用Servlet异步处理之后,页面可以一部分一部分的显示数据,而不是一直卡,等到请求响应结束后一起显示。

 在使用异步处理之前,一定要在@WebServlet注解中给出asyncSupported=true,不然默认Servlet是不支持异步处理的。如果存在过滤器,也要设置@WebFilter的asyncSupportedt=true。

@WebServlet(urlPatterns = {"/MyServlet"}, asyncSupported=true)

 public class MyServlet extends HttpServlet {…}



使用异步处理大致可以分为两步:

 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 + "&nbsp;");

       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 + "&nbsp;");

       asyncContext.getResponse().getWriter().flush();

      } catch (Exception e) {

       e.printStackTrace();

      }

     }

     asyncContext.complete();

    }

   });

   // asyncContext.start(businessHandleThread);

   // 也可以用这种方法启动异步线程

  out.println("Servlet end <br>");

  }

 }



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);

  }

 }



动态代理(AOP)



1 学习动态代理的目的

 动态代理技术都是在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了动态代理技术。如果你不想自己写个框架,那么你基本上是用上不动态代理技术的。

 我们学习动态代理技术的目的是为了更好的理解框架内部的原理,也就是说是为了将来我们学习框架打基础!

 动态代理技术有点小难度!而且明白了动态代理技术可能一时也想不到他适合在什么情况下使用它。这些东西都会在学习框架时渐渐明白。



2 运行时实现指定的接口

 想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:

public interface MyInterface {

  void fun1();

  void fun2();

 }

 public class MyInterfaceImpl implements MyInterface {

  public void fun1() {

   System.out.println("fun1()");

  }



 public void fun2() {

   System.out.println("fun2()");

  }

 }



上面的代码对我们来说没有什么新鲜感,我们要说的是动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象。

  Class[] cs = {MyInterface.class};

   MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);



上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。没错,返回值mi是MyInterface接口的实现类。你不要问这个类是哪个类,你只需要知道mi是MyInterface接口的实现类就可以了。你现在也不用去管loader和h这两个参数是什么东东,你只需要知道,Proxy类的静态方法newProxyInstance()方法返回的方法是实现了指定接口的实现类对象,甚至你都没有看见实现类的代码。

 动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。



3 newProxyInstance()方法的参数

Proxy类的newInstance()方法有三个参数:

 ClassLoader loader:它是类加载器类型,你不用去理睬它,你只需要知道怎么可以获得它就可以了:MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,没错,只要你有一个Class对象就可以获取到ClassLoader对象;

 Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,没错,可以指定多个接口,例如上面例子只我们只指定了一个接口:Class[] cs = {MyInterface.class};

 InvocationHandler h:它是最重要的一个参数!它是一个接口!它的名字叫调用处理器!你想一想,上面例子中mi对象是MyInterface接口的实现类对象,那么它一定是可以调用fun1()和fun2()方法了,难道你不想调用一下fun1()和fun2()方法么,它会执行些什么东东呢?其实无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!



 public static void main(String[] args) {

   Class[] cs = {MyInterface.class};

   ClassLoader loader = MyInterface.class.getClassLoader();

   InvocationHandler h = new InvocationHandler() {

    public Object invoke(Object proxy, Method method, Object[] args)

      throws Throwable {

     System.out.println("无论你调用代理对象的什么方法,其实都是在调用invoke()...");

     return null;

    }

   };

   MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

   mi.fun1();

   mi.fun2();

  }



  InvocationHandler接口只有一个方法,即invoke()方法!它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。



想象中的类:

class X implements MyInterface {

  private InvocationHandler h;

  public X(InvocationHandler h) {

   this.h = h;

  }

  

  public void fun1() {

   h.invoke();

  }

  public void fun2() {

   h.invoke();

  }

 }



  注意,X类是我们用来理解代理对象与InvocationHandler之间的关系的,但它是不存在的类。是我们想象出来的!也就是说,它是用来说明,无论你调用代理对象的哪个方法,最终调用的都是调用处理器的invoke()方法。



4 InvocationHandler的invoke()方法

InvocationHandler的invoke()方法的参数有三个:

 Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;

 Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;

 Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。

   最后要说的是invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null了。

 public static void main(String[] args) {

   Class[] cs = {MyInterface.class};

   ClassLoader loader = MyInterface.class.getClassLoader();

   InvocationHandler h = new InvocationHandler() {

    public Object invoke(Object proxy, Method method, Object[] args)

      throws Throwable {

     System.out.println("当前调用的方法是:" + method.getName());

     return null;

    }

   };

   MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

   mi.fun1();

   mi.fun2();

  }

当前调用的方法是:fun1

当前调用的方法是:fun2



5 动态代理的用途

 动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。

 下面我们用一个例子来说明动态代理的用途!

 我们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:

public interface Waiter {

  public void serve();

 }

 public class MyWaiter implements Waiter {

  public void serve() {

   System.out.println("服务...");

  }

 }



现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。

public class MainApp1 {

  public static void main(String[] args) {

   ClassLoader loader = MainApp1.class.getClassLoader();

   Class[] cs = {Waiter.class};

   Waiter target = new MyWaiter();

   MyInvocationHandler h = new MyInvocationHandler(target);

   Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);

   waiter.serve();

  }

 }



class MyInvocationHandler implements InvocationHandler {

  public Waiter target;

  public MyInvocationHandler(Waiter target) {

   this.target = target;

  }

  public Object invoke(Object proxy, Method method, Object[] args)

    throws Throwable {

   System.out.println("您好!");

   Object result = method.invoke(target, args);

   System.out.println("很高兴为您服务!");

   return result;

  }

 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值