编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

SpringMVC源码解析 springmvc源码解析 博客园

wxchong 2024-12-23 14:10:52 开源技术 85 ℃ 0 评论

servlet3.0解析

  1. 在Servlet3.0中可以动态注册Servlet,Filter,Listener,在ServletContext对应注册API为:

addServlet(); addFilter();addListener()

2.从servlet3.0开始,web容器启动时为提供给第三方组件机会做一些初始化的工作;

例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

动态注册Servlet,但不要希望太高,只能在初始化时进行注册。在运行时为了安全原因,无法完成注册。在初始化情况下的注册Servlet组件有两种方法:
1.实现ServletContextListener接口,在contextInitialized方法中完成注册.
2.在jar文件中放入实现ServletContainerInitializer接口的初始化器

tomact 遵循servlet规范 springweb使用的就是servlet规范

tomcat 7 servlet< 3.0规范

tomcat 8 servlet >3.0规范

META-INF /services/javax.servlet.ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer


@HandlesTypes的实现原理:


他的作用是将注解指定的Class对象作为参数传递到onStartup(ServletContainerInitializer)方法中;

其中@HandlesTypes注解表示SpringServletContainerInitializer 可以处理的类,在onStartup 方法中,可以通过Set<Class<?>> c 获取得到。

然而这个注解是要留给用户扩展的,他指定的Class对象并没有要继承ServletContainerInitializer,更没有写入META-INF/services/的文件(也不可能写入)中,那么Tomcat是怎么扫描到指定的类的呢。
答案是Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta 项目的一部分,作用同ASM类似,是字节码操纵框架。

SpringServletContainerInitializer
通过源码发现,配合注解@HandlesTypes它可以将其指定的Class对象作为参数传递到onStartup方法中。进而在onStartup方法中获取Class对象的具体实现类,进而调用实现类中的具体方法。SpringServletContainerInitializer类中@HandlesTypes指定的是Class对象是WebApplicationInitializer.Class。利用这个机制,若实现WebApplicationInitializer这个接口,我们就可以自定义的注入Servlet,或者Filter,即可以不再依赖web.xml的配置。


@Override
public void onStartup(ServletContext servletContext) throws ServletException {
   registerContextLoaderListener(servletContext);
}

/**
 * org.springframework.web.context.AbstractContextLoaderInitializer implements WebApplicationInitializer
 * registerContextLoaderListener
 */
protected void registerContextLoaderListener(ServletContext servletContext) {
      //WebApplicationContext  web容器创建
    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");
     }
}

@RequestMapping解析

RequestMappingHandlerMapping实现了InitializingBean接口,因此,在初始化并装配该Bean实例时,执行到上述代码是,便会执行他的afterPropertySet方法。我们接下来看看他的afterPropertySet方法:

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #isHandler(Class)
 * @see #getMappingForMethod(Method, Class)
 * @see #handlerMethodsInitialized(Map)
 */
protected void initHandlerMethods() {
   String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);

         for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
               Class<?> beanType = null;
               try {
                 
                  beanType = obtainApplicationContext().getType(beanName);
               }
               catch (Throwable ex) {
                  // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                  if (logger.isTraceEnabled()) {
                     logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
                  }
               }
               if (beanType != null && isHandler(beanType)) {
                    //遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod  
                  detectHandlerMethods(beanName);
               }
            }
   }
  //后置处理什么也没有做
   handlerMethodsInitialized(getHandlerMethods());
}

它直接调用了initHandlerMethods()方法,并且该方法被描述为:扫描ApplicationContext中的beans,检测并注册处理器方法。we are close。

检测@RequestMapping


//RequestMappingHandlerMapping#isHandler
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

看看有没有被@Controller或者@RequestMapping注解标记

//org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping.detectHandlerMethods
protected void detectHandlerMethods(final Object handler) {
       Class<?> handlerType = (handler instanceof String beanName ? obtainApplicationContext().getType(beanName) : handler.getClass());
       if (handlerType != null) {
          final Class<?> userType = ClassUtils.getUserClass(handlerType);
          Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
          methods.forEach((method, mapping) -> {
             //根据方法上的@RequestMapping来创建RequestMappingInfo实例。
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             //注册请求映射
             registerHandlerMethod(handler, invocableMethod, mapping);
          });
       }
}


先获取方法上的@RequestMapping信息,然后获取类级别上的@RequestMapping 信息,然后将两者结合,这里我们有必要再了解下怎样创建RequestMappingInfo对象的(包括他的内部结构),以及怎样将类级别的request mapping信息和方法级别的进行结合的?

/**
 * 获取 RequestMappingInfo 信息
 * 获取@RequestMapping注解
 *提供一个自定义类型级别的请求条件。 getCustomTypeCondition
 * 提供一个自定义方法级请求条件 getCustomMethodCondition
 * @param element
 * @return
 */
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
   RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
   RequestCondition<?> condition = (element instanceof Class ?
         getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
   return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。

//org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping.MappingRegistry.register(T, java.lang.Object, java.lang.reflect.Method)
public void register(T mapping, Object handler, Method method) {
   this.readWriteLock.writeLock().lock();
   try {
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      validateMethodMapping(handlerMethod, mapping);

      Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
      for (String path : directPaths) {
         this.pathLookup.add(path, mapping);
      }

      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
         corsConfig.validateAllowCredentials();
         this.corsLookup.put(handlerMethod, corsConfig);
      }

      this.registry.put(mapping,
            new MappingRegistration<>(mapping, handlerMethod, directPaths, corsConfig != null));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

SpringMVC 初始化 - HttpservletBean

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

要分析 SpringMVC 的初始化过程,就必须从 HttpServletBean 的 init 方法开始。


创建 PropertyValues

  • 创建 BeanWrapper
  • 创建 ResourceLoader
  • 将 ResourceLoader 添加到 BeanWrapper
  • 将 ResourceLoader 添加到 BeanWrapper
  • 至于两个空方法:

    • initBeanWrapper :在 SpringMVC 中并未做任务实现,而是留给用户扩展
    • initServletBean:在其子类 FrameworkServlet 作具体实现。

    Tags:

    本文暂时没有评论,来添加一个吧(●'◡'●)

    欢迎 发表评论:

    最近发表
    标签列表