Spring注解开发

一、Java Web注解开发

使用Java Web 注解开发可以不用不用任何xml配置文件来开发Java Web。
大致原理是:

  • 实现ServletContainerInitializer接口
  • 在META-INF/service 建立spi文件(spi机制),指定ServletContainerInitializer的实现类
  • 在实现类上加@HandlesTypes 注解(Tomcat扫到该注解的指定的类型以及该类型的泛华类型)
  • 实现类onStartup的方法参数Set> c 就是@HandlesTypes指定类型的泛化类型全部的类,由Tomcat容器扫描到的进来
  • 项目启动Tomcat会扫描实现ServletContainerInitializer的类,并扫描实现类上@HandlesTypes指定的类型和泛化类型全部的类传到实现类的onStartup方法上
  • 在实现类的onStartup里面注册filter,listener,servlet
@HandlesTypes({Handler.class})
public class SampleServletContainerInitializer implements ServletContainerInitializer {


    @Override
    public void onStartup(Set> c, ServletContext ctx) throws ServletException {

        // c servlet 容器帮我们注入感兴趣的类型

        ServletRegistration.Dynamic homeServlet = ctx.addServlet("home", HomeServlet.class);
        homeServlet.addMapping("/home");

        // 注册过滤器
        FilterRegistration.Dynamic userFilter = ctx.addFilter("userFilter", UserFilter.class);
        userFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");

        // 注册监听
        ctx.addListener(UserListener.class);

    }
}

二、Spring 基于注解开发

Spring Web 就是基于上面的Java Web开发的方式开实现注解方式来开发项目。


Spring注解开发_第1张图片
image.png

如上图,在spring-web项目中,有个SpringServletContainerInitializer类,实现了ServletContainerInitializer。在项目启动时候,Tomcat会扫描到该类。并且扫描注解@HandlesTypes里面指定的接口WebApplicationInitializer的泛化类。


Spring注解开发_第2张图片
image.png

这些类都会被Tomcat扫描到并且注入到SpringServletContainerInitializer的onStart方法的参数webAppInitializerClasses上。
其中最重要的三个抽象类:

  • AbstractContextLoaderInitializer (createRootApplicationContext 用来创建spring父容器)
    • AbstractDispatcherServletInitializer (抽象方法createServletApplicationContext,spring web 容器的抽象方法定义)
      • AbstractAnnotationConfigDispatcherServletInitializer (spring web容器创建AnnotationConfigWebApplicationContext)

这三个抽象类依次继承关系。

这里可以实现这里面的抽象类,来配置项目。
例如建立一个自定义的WebApplicationInitializer实现类,继承AbstractAnnotationConfigDispatcherServletInitializer,来指定项目中的配置。


Spring注解开发_第3张图片
image.png

AbstractAnnotationConfigDispatcherServletInitializer 类里面已经创建AnnotationConfigWebApplicationContext的spring web容器,所以在实现类中只需要指定配置文件就好了。

  • RootConfig 类似web.xml
  • ServletConfig 类似servlet-mvc.xml
Spring注解开发_第4张图片
spring web 注解开发

Spring Mvc 基于注解开发

接着上面的思路,ServertConfig是mvc的配置。spring又简化了该配置。
直接实现WebMvcConfigurerAdapter类,来配置Spring Mvc 的配置。

@ComponentScan(basePackages = "com.smallcode", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
}, useDefaultFilters = false)
@EnableWebMvc // 开启spring mvc的配置功能,类似 annotation-driven
public class ServletConfig  extends WebMvcConfigurerAdapter {


    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/",".jsp");
        super.configureViewResolvers(registry);
    }
}

跟基于xml文件的方式一样可以添加很多配置。
更多配置见官网:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/web.html#mvc-config-content-negotiation

WebMvcConfigurerAdapter 加载原理大致如下:
  • @EnableWebMvc 上面@Import DelegatingWebMvcConfiguration;DelegatingWebMvcConfiguration 里面,@EnableWebMvc 类似spring-mvc.xml配置文件里面的,再次重声ServletConfig类似spring-mvc.xml,就是将xml的配置用JavaConfig的形式表示。
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    @Autowired(required = false)
    public void setConfigurers(List configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }

//WebMvcConfigurerComposite.addWebMvcConfigurers
public void addWebMvcConfigurers(List configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.delegates.addAll(configurers);
        }
    }
  • DelegatingWebMvcConfiguration 继承WebMvcConfigurationSupport

  • WebMvcConfigurationSupport 里面有基于@Bean 实例化的的类,例如

@Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
        mapping.setOrder(0);
        mapping.setInterceptors(getInterceptors());
        mapping.setContentNegotiationManager(mvcContentNegotiationManager());
        mapping.setCorsConfigurations(getCorsConfigurations());

        PathMatchConfigurer configurer = getPathMatchConfigurer();
        if (configurer.isUseSuffixPatternMatch() != null) {
            mapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
        }
        if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
            mapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
        }
        if (configurer.isUseTrailingSlashMatch() != null) {
            mapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
        }
        UrlPathHelper pathHelper = configurer.getUrlPathHelper();
        if (pathHelper != null) {
            mapping.setUrlPathHelper(pathHelper);
        }
        PathMatcher pathMatcher = configurer.getPathMatcher();
        if (pathMatcher != null) {
            mapping.setPathMatcher(pathMatcher);
        }

        return mapping;
    }
  • 这样当@Bean下的方法 requestMappingHandlerMapping 随项目启动的时候执行, getInterceptors()的时候会去调用WebMvcConfigurationSupport的addInterceptor,进而调用DelegatingWebMvcConfiguration的addInterceptors 进而调用WebMvcConfigurerComposite的addInterceptors,进而调用List的addInterceptors。
区别

以前经常区分不了这两个东西,其实是不明白这个的实际作用导致,在这里,由上面的@EnableWebMvc可以得到该注解是为了向容器里面注入RequestMappingHandlerMapping等用于处理springmvc请求的类的。而 @EnableWebMvc是 Java Config的体现,因此,可以推测 也是向容器里面注入RequestMappingHandlerMapping等用于处理springmvc请求的类的。查找用于解析 的类


Spring注解开发_第5张图片
image.png

AnnotationDrivenBeanDefinitionParser 中解析代码如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);
 
        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);
 
        RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
        //第一个在这 RequestMappingHandlerMapping
        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerMappingDef.getPropertyValues().add("order", 0);
        handlerMappingDef.getPropertyValues().add("removeSemicolonContent", false);
        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
        //第二个在这 RequestMappingHandlerAdapter
        RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
        handlerAdapterDef.setSource(source);
        handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
        handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
        if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
        }
        if (argumentResolvers != null) {
            handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
        }
        if (returnValueHandlers != null) {
            handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
        }
        if (asyncTimeout != null) {
            handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
        }
        if (asyncExecutor != null) {
            handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
        }
        handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
        handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
        String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
        //异常处理解析器
        RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
        exceptionHandlerExceptionResolver.setSource(source);
        exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
        exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
        String methodExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);
        //异常处理解析器
        RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
        responseStatusExceptionResolver.setSource(source);
        responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        responseStatusExceptionResolver.getPropertyValues().add("order", 1);
        String responseStatusExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
        //异常处理解析器
        RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
        defaultExceptionResolver.setSource(source);
        defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        defaultExceptionResolver.getPropertyValues().add("order", 2);
        String defaultExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
 
        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
        parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
        parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
        //这里注册了BeanNameUrlHandlerMapping,SimpleControllerHandlerAdapter等
        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
 
        parserContext.popAndRegisterContainingComponent();
 
        return null;
    }
//在这啊。
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
        registerBeanNameUrlHandlerMapping(parserContext, source);
        registerHttpRequestHandlerAdapter(parserContext, source);
        registerSimpleControllerHandlerAdapter(parserContext, source);
    }

由此可见该方法向容器中注册了RequestMappingHandlerMapping等类。用于处理spring mvc的请求。

你可能感兴趣的:(Spring注解开发)