Spring注解驱动开发之web
Posted qzlcl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring注解驱动开发之web相关的知识,希望对你有一定的参考价值。
前言:现今SpringBoot、SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解、原理,比如@Conditional、@Import、@EnableXXX等。如果掌握这些底层原理、注解,那么我们对这些高层框架就能做到高度定制,使用的游刃有余
一、servlet3.0规范
1、新增的注解支持
在servlet3.0之前的话,我们要添加Servlet、Filter、Listener都需要在web.xml中注册,而在servlet3.0添加了注解支持:
@WebServlet: 用于将一个类声明为 Servlet,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为 Servlet,如:
@WebServlet(urlPatterns = "/simple", asyncSupported = true, loadOnStartup = -1, name = "SimpleServlet", displayName = "ss", initParams = @WebInitParam(name = "username", value = "tom") ) public class SimpleServlet extends HttpServlet …
@WebFilter: 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器;
@WebListener:该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现对应的监听器接口
@WebInitParam:该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数,这等价于 web.xml 中 <servlet> 和 <filter> 的 <init-param> 子标签。
2、runtimes pluggability(运行时插件能力)
在使用实现了servlet3.0规范的servlet容器中,Servlet容器启动会扫描当前应用里面每一个jar包的ServletContainerInitializer的实现,前提是ServletContainerInitializer的实现类必须绑定在META-INF/services/javax.servlet.ServletContainerInitializer中,文件的内容就是ServletContainerInitializer实现类的全类名
//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来; //传入感兴趣的类型; @HandlesTypes(value=HelloService.class) public class MyServletContainerInitializer implements ServletContainerInitializer /** * 应用启动的时候,会运行onStartup方法; * * Set<Class<?>> arg0:感兴趣的类型的所有子类型; * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext; * * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener) * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件; * 必须在项目启动的时候来添加; * 1)、ServletContainerInitializer得到的ServletContext; * 2)、ServletContextListener得到的ServletContext; */ @Override public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException // TODO Auto-generated method stub System.out.println("感兴趣的类型:"); for (Class<?> claz : arg0) System.out.println(claz); //注册组件 ServletRegistration ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet()); //配置servlet的映射信息 servlet.addMapping("/user"); //注册Listener sc.addListener(UserListener.class); //注册Filter FilterRegistration FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class); //配置Filter的映射信息 filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
使用该特性,现在我们可以在不修改已有 Web 应用的前提下,只需将按照一定格式打成的 JAR 包放到 WEB-INF/lib 目录下,即可实现新功能的扩充(比如注册三大组件),不需要额外的配置;
二、SpringMVC注解开发
在之前使用SpringMVC时,很多时候都是在web.xml中配置的方式来启动,而从SpringMVC 3.1开始就使用了servlet3.0的插件机制,可通过配置类的方式来启动SpringMVC
1、SpringServletContainerInitializer
在spring的web模块的jar包下存在META-INF/services/javax.servlet.ServletContainerInitializer,该文件中指定ServletContainerInitializer的实现类为SpringServletContainerInitializer,可知在web容启动时会加载这个类,来看看这个类:
@HandlesTypes(WebApplicationInitializer.class)//容器启动的时候会将WebApplicationInitializer类型下面的子类(实现类,子接口等)传递过来 public class SpringServletContainerInitializer implements ServletContainerInitializer
//webAppInitializerClasses就是WebApplicationInitializer类型 @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); if (webAppInitializerClasses != null) for (Class<?> waiClass : webAppInitializerClasses) // 将webAppInitializerClasses集合中的非抽象,不是接口类型的class实例化并添加到initializer中 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; servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
//遍历执行initializers集合中WebApplicationInitializer.onStartup(servletContext)方法 for (WebApplicationInitializer initializer : initializers) initializer.onStartup(servletContext);
接下来看看SpringServletContainerInitializer使用@HandlesTypes引入的WebApplicationInitializer接口(只定义了一个onStartup方法)的子类:
2、AbstractContextLoaderInitializer
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); //该方法会在web容器启动时SpringServletContainerInitializer.onStartup中被调用 @Override public void onStartup(ServletContext servletContext) throws ServletException
//注册加载上下文的监听器 registerContextLoaderListener(servletContext); protected void registerContextLoaderListener(ServletContext servletContext)
//调用createRootApplicationContext()创建根容器,需要具体的实现类去实现该抽象方法获取根容器 WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null)
//创建监听器,并将根容器传入 ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
//设置上下文初始化器 listener.setContextInitializers(getRootApplicationContextInitializers());
//添加监听器 servletContext.addListener(listener); else logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not return an application context"); //抽象方法,子类必须实现 protected abstract WebApplicationContext createRootApplicationContext(); //默认返回空 子类可重写 protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() return null;
AbstractContextLoaderInitializer主要的功能:
调用创建createRootApplicationContext()创建根容器,
注册了监听器ContextLoaderListener(extends ContextLoader implements ServletContextListener)
3、AbstractDispatcherServletInitializer:
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer /** * The default servlet name. Can be customized by overriding @link #getServletName. */ public static final String DEFAULT_SERVLET_NAME = "dispatcher"; //重写了AbstractContextLoaderInitializer.onStartup(ServletContext) @Override public void onStartup(ServletContext servletContext) throws ServletException
//维持父类的实现 super.onStartup(servletContext);
//添加了注册DispatcherServlet的步骤 registerDispatcherServlet(servletContext); protected void registerDispatcherServlet(ServletContext servletContext) String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return empty or null"); //调用createServletApplicationContext()创建web的ioc容器(管理Controller等springmvc的组件),需要子类去实现该抽象方法去获取web容器 WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() did not return an application " + "context for servlet [" + servletName + "]"); //创建了前端控制器DispatcherServlet FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); //使用servletContext添加DispatcherServlet ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); Assert.notNull(registration, "Failed to register servlet with name ‘" + servletName + "‘." + "Check if there is another servlet registered under the same name."); registration.setLoadOnStartup(1);
//具体的路径映射规则需要子类实现getServletMappings() registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported()); Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) for (Filter filter : filters) registerServletFilter(servletContext, filter); customizeRegistration(registration); protected String getServletName() return DEFAULT_SERVLET_NAME; protected abstract WebApplicationContext createServletApplicationContext(); protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) return new DispatcherServlet(servletAppContext); protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() return null; protected abstract String[] getServletMappings(); protected Filter[] getServletFilters() return null; protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) String filterName = Conventions.getVariableName(filter); Dynamic registration = servletContext.addFilter(filterName, filter); if (registration == null) int counter = -1; while (counter == -1 || registration == null) counter++; registration = servletContext.addFilter(filterName + "#" + counter, filter); Assert.isTrue(counter < 100, "Failed to register filter ‘" + filter + "‘." + "Could the same Filter instance have been registered already?"); registration.setAsyncSupported(isAsyncSupported()); registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName()); return registration; private EnumSet<DispatcherType> getDispatcherTypes() return (isAsyncSupported() ? EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)); protected boolean isAsyncSupported() return true; protected void customizeRegistration(ServletRegistration.Dynamic registration)
AbstractDispatcherServletInitializer 的主要功能:
创建一个web的ioc容器:createServletApplicationContext();
创建了DispatcherServlet:createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中,并设置路径映射等;
4、AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer //实现了AbstractContextLoaderInitializer.createRootApplicationContext(),创建根容器 @Override protected WebApplicationContext createRootApplicationContext()
//获取根容器的配置类 Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses))
//创建ioc容器 AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
//注册组件 rootAppContext.register(configClasses); return rootAppContext; else return null; //实现了AbstractDispatcherServletInitializer.createServletApplicationContext(),创建web的ioc容器 @Override protected WebApplicationContext createServletApplicationContext()
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
//获取web ioc容器的配置类 Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses))
// servletAppContext.register(configClasses); return servletAppContext; //抽象方法 子类实现 返回根容器的配置类 protected abstract Class<?>[] getRootConfigClasses(); //抽象方法 子类实现 返回web ioc容器的配置类 protected abstract Class<?>[] getServletConfigClasses();
AbstractAnnotationConfigDispatcherServletInitializer (注解方式配置的DispatcherServlet初始化器) 主要作用:
创建根容器:createRootApplicationContext(),调用getRootConfigClasses()获取配置类
创建web的ioc容器: createServletApplicationContext(),调用getServletConfigClasses()获取配置类
5、以注解方式来启动SpringMVC:
上面我们分析了三个抽象类的功能,最终需要我们需要继承AbstractAnnotationConfigDispatcherServletInitializer,实现对应抽象方法来指定DispatcherServlet的配置信息
//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器 public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer //获取根容器的配置类;(Spring的配置文件) 父容器; @Override protected Class<?>[] getRootConfigClasses() // TODO Auto-generated method stub return new Class<?>[]RootConfig.class; //获取web容器的配置类(SpringMVC配置文件) 子容器; @Override protected Class<?>[] getServletConfigClasses() // TODO Auto-generated method stub return new Class<?>[]AppConfig.class; //获取DispatcherServlet的映射信息 // /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp; // /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的; @Override protected String[] getServletMappings() // TODO Auto-generated method stub return new String[]"/";
以上是关于Spring注解驱动开发之web的主要内容,如果未能解决你的问题,请参考以下文章