究 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用

       (同样写于11年12月20日左右的,转导入此)

        大家应该都已经知道Spring 3.1对无web.xml式基于代码配置的servlet3.0应用。通过spring的api或是网络上高手们的博文,也一定很快就学会并且加到自己的应用中去了。PS:如果还没,也可以小小参考一下鄙人的上一篇文章<<探 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用>>。 

      经过一天的深度research, 我了解,理解以及重现了springframework的那一小段代码。
 

       OK,第一步,入手点,WebApplicationInitializer接口。因为我们只需实现这个接口覆写它的一个方法,就可以做到配置web.xml同样的功效。看它的源码,其实看和不看没什么两样: 

package org.springframework.web;  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
public interface WebApplicationInitializer {  
    void onStartup(ServletContext servletContext) throws ServletException;  
} 

       就这么点儿,有效代码5行,弄地我一头雾水,就是一个普通接口,声明了一个方法。连注解都没有,server是怎么找到实现了它的类的?如果这样,何不找我定义的其它接口(的实现类完成配置工作)呢。可见现在java的解耦技术,真令人汗颜。 


   第二步,这个接口旁边(同包)有个SpringServletContainerInitializer, 看下它是何方神圣吧:
 

package org.springframework.web;  
  
import java.lang.reflect.Modifier;  
import java.util.Collections;  
import java.util.LinkedList;  
import java.util.List;  
import java.util.ServiceLoader;  
import java.util.Set;  
import javax.servlet.ServletContainerInitializer;  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import javax.servlet.annotation.HandlesTypes;  
  
import org.springframework.core.annotation.AnnotationAwareOrderComparator;  
  
@HandlesTypes(WebApplicationInitializer.class)  
public class SpringServletContainerInitializer implements ServletContainerInitializer {  
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)  
            throws ServletException {  
  
        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();  
        if (webAppInitializerClasses != null) {  
            for (Class<?> waiClass : webAppInitializerClasses) {  
                // Be defensive: Some servlet containers provide us with invalid classes,  
                // no matter what @HandlesTypes says...  
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&             WebApplicationInitializer.class.isAssignableFrom(waiClass)) {  
                    try {  
                        initializers.add((WebApplicationInitializer) waiClass.newInstance());  
                    }  
                    catch (Throwable ex) {  
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);  
                    }  
                }  
            }  
        }  
  
        if (initializers.isEmpty()) {  
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");  
            return;  
        }  
  
        Collections.sort(initializers, new AnnotationAwareOrderComparator());  
        servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);  
  
        for (WebApplicationInitializer initializer : initializers) {  
            initializer.onStartup(servletContext);  
        }  
    }  
  
}  

     以上的有效代码28行。刚看时也很迷茫,其实慢慢就理解了。拟个伪代码吧,方便大家理解: 
      1,定义一个类SpringServletContainerInitializer,并标明该类要操作的一个类WebApplicationInitializer 
      2, 该类会行使ServletContainerInitializer接口的一个行为onStartup,从而将一个集合中的初始化设置 全部配置到ServletContext的实例中。 
      3,具体的onStartup方法中,建立合格配置列表, 
      4,如果确定集合中有配置,逐一检查配置是否是合格配置,具体判断依据:这个类不是接口,不是抽象类,而且是所要操作的那个接口的一个实现类。满足此依据,合格。将合格的配置类实例化放入合格配置列表。过程中有错要通知控制台。 
     5,如若执行完步骤4,发现没有合格配置,在ServletContext记录该结果,并结束onStartup行为。 
     6,将找到配置按一定排列方式(AnnotationAwareOrder)排序。 
     7,在ServletContext中记录找到结果。 
     8,逐一执行配置。 即驱动每一个WebApplicationInitializer的实现类行使其onStartup行为。 

     第三步很明显了,去research 接口ServletContainerInitializer和注解HandleType。在这里:
http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html 

    该接口允许一个库或运行时,(运行时应该指server)声明为一个web程序的启动状态,并执行任何所需的程序中注册的servlet,filter,listener来响应它...... 
     其它也就不用看了,可以想象得到支持Servlet3机制的服务器,会找到这样接口的实现类,执行onStartup行为。至于如何找,无非也是这样一系列的反射机制的应用。自己做一个试试吧: 
     自定义的WebApplicationInitializer:
 

package com.gxino.imagecapture.cfg;  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
  
public interface WebParameter {  
    public void loadInfo(ServletContext servletContext) throws ServletException;  
}  

     自定义的ServletContainerInitializer,我做得很简单,直接去执行找到配置类中的loadInfo方法 

package com.gxino.imagecapture.cfg;  
  
import java.lang.reflect.Modifier;  
import java.util.Set;  
  
import javax.servlet.ServletContainerInitializer;  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import javax.servlet.annotation.HandlesTypes;  
  
@HandlesTypes(WebParameter.class)  
public class WebConfiguration implements ServletContainerInitializer {  
  
    @Override  
    public void onStartup(Set<Class<?>> webParams, ServletContext servletCtx)  
            throws ServletException {  
        if (webParams != null) {  
            for (Class<?> paramClass : webParams) {  
                if (!paramClass.isInterface() && !Modifier.isAbstract(paramClass.getModifiers()) &&  
                        WebParameter.class.isAssignableFrom(paramClass)) {  
                    try {  
                        ((WebParameter) paramClass.newInstance()).loadInfo(servletCtx);  
                    }  
                    catch (Throwable ex) {  
                        throw new ServletException("Failed to instantiate WebParam class", ex);  
                    }  
                }  
            }//loop  
        }//Web Params  
    }//onStartup  
  
}  

      写个测试Servlet: 

package com.gxino.imagecapture.ctrl;  
  
import java.io.IOException;  
  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import com.gxino.imagecapture.cfg.WebParameter;  
  
public class TestServlet extends HttpServlet {  
      
    public void doGet(HttpServletRequest req, HttpServletResponse resp){  
        System.out.println("Some client access once");  
        try {  
            req.getRequestDispatcher("/index.jsp").forward(req, resp);  
        } catch (ServletException | IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
      
}  

       实现WebParam配置接口来配置刚才的Servlet: 

package com.gxino.imagecapture.cfg;  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRegistration;  
  
  
public class ServletParameter implements WebParameter {  
  
    @Override  
    public void loadInfo(ServletContext servletContext) throws ServletException {  
        ServletRegistration.Dynamic testServlet=servletContext.addServlet("test","com.gxino.imagecapture.ctrl.TestServlet");  
        testServlet.setLoadOnStartup(1);  
        testServlet.addMapping("/index.html");  
    }  
  
} 

        启动服务器,访问http://localhost:xxxx/xxxxx/index.html 
  
     失败。Debug. 发现没有走这些代码。应该还差关键环节。看来还得知道Servlet3中是怎么找ServletContainerInitializer的。再回刚才ServletContainerInitializer的api有这样一句:该接口的实现必须声明一个JAR资源放到程序中的META-INF/services下,并且记有该接口那个实现类的全路径,才会被运行时(server)的查找机制或是其它特定机制找到。那篇api需要仔细阅读啊。 


     到org.springframework.web-3.0.1.RELEASE.jar中能找到META-INF/services下的javax.servlet.ServletContainerInitializer文件,内容为org.springframework.web.SpringServletContainerInitializer同样,我们专门作这样一个包,在mkdir好的META-INF/services下vi 一个文件命名为javax.servlet.ServletContainerInitializer,内容为自定的那个WebConfiguration的全路径类名。 然后在META-INF的parent路径下运行jar cvf test.jar META-INF。一切完毕,将其放到WEB-INF/lib下。启动。
 
     
     
这回大功告成。 
     
     访问http://localhost:xxxx/xxxxx/index.html。页面跳到了index.jsp下。 
     并且控制台打出: Some client access once 


     再使个劲,将Servlet和Servlet配置合二为一:
 

package com.gxino.imagecapture.ctrl;  
  
import java.io.IOException;  
  
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRegistration;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import com.gxino.imagecapture.cfg.WebParameter;  
  
public class TestServlet extends HttpServlet implements WebParameter{  
  
    @Override  
    public void loadInfo(ServletContext servletContext) throws ServletException {  
        ServletRegistration.Dynamic testServlet=servletContext.addServlet("test", "com.gxino.imagecapture.ctrl.TestServlet");  
        testServlet.setLoadOnStartup(1);  
        testServlet.addMapping("/index.html");  
    }  
    public void doGet(HttpServletRequest req, HttpServletResponse resp){  
        System.out.println("Some client access once");  
        try {  
            req.getRequestDispatcher("/index.jsp").forward(req, resp);  
        } catch (ServletException | IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
      
}  

    这回我们看到,配置文件与servlet放到了一起。这样将回节省大量时间。 

    以后直接运用Spring Framework的WebApplicationInitializer也知道是怎么一回事儿了。而且可以将Spring 的applicationContext.xml与web.xml融合在一个类中。即注解为@Configuration,并实现WebApplicationInitializer.回头试试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot并不强制要求使用web.xml文件配置过滤器,而是推荐使用Java配置类或注解的方配置过滤器。 如果您仍然想使用web.xml文件来配置过滤器,可以将它放置在src/main/webapp/WEB-INF目录下,并在应用程序的启动类中添加@ServletComponentScan注解,以启用Servlet和过滤器的自动注册。 例如,以下是一个使用web.xml文件配置过滤器的示例: 1. 在src/main/webapp/WEB-INF目录下创建web.xml文件,并添加以下内容: ``` <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <filter> <filter-name>myFilter</filter-name> <filter-class>com.example.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> ``` 2. 创建一个实现javax.servlet.Filter接口的过滤器类,例如: ``` package com.example; import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化过滤器 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 执行过滤操作 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { // 销毁过滤器 } } ``` 3. 在启动类上添加@ServletComponentScan注解,例如: ``` package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 这样,在启动应用程序时,Spring Boot将自动加载web.xml文件,并注册myFilter过滤器。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值