一、Java Web注解开发
使用Java Web 注解开发可以不用不用任何xml配置文件来开发Java Web。
大致原理是:
- 实现ServletContainerInitializer接口
- 在META-INF/service 建立spi文件(spi机制),指定ServletContainerInitializer的实现类
- 在实现类上加@HandlesTypes 注解(Tomcat扫到该注解的指定的类型以及该类型的泛华类型)
- 实现类onStartup的方法参数Set<Class<?>> c 就是@HandlesTypes指定类型的泛化类型全部的类,由Tomcat容器扫描到的进来
- 项目启动Tomcat会扫描实现ServletContainerInitializer的类,并扫描实现类上@HandlesTypes指定的类型和泛化类型全部的类传到实现类的onStartup方法上
- 在实现类的onStartup里面注册filter,listener,servlet
@HandlesTypes({Handler.class})
public class SampleServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> 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开发的方式开实现注解方式来开发项目。
![](https://img.haomeiwen.com/i301624/289426bd87299a60.png)
如上图,在spring-web项目中,有个SpringServletContainerInitializer类,实现了ServletContainerInitializer。在项目启动时候,Tomcat会扫描到该类。并且扫描注解@HandlesTypes里面指定的接口WebApplicationInitializer的泛化类。
![](https://img.haomeiwen.com/i301624/628a669aed3d7680.png)
这些类都会被Tomcat扫描到并且注入到SpringServletContainerInitializer的onStart方法的参数webAppInitializerClasses上。
其中最重要的三个抽象类:
- AbstractContextLoaderInitializer (createRootApplicationContext 用来创建spring父容器)
- AbstractDispatcherServletInitializer (抽象方法createServletApplicationContext,spring web 容器的抽象方法定义)
- AbstractAnnotationConfigDispatcherServletInitializer (spring web容器创建AnnotationConfigWebApplicationContext)
- AbstractDispatcherServletInitializer (抽象方法createServletApplicationContext,spring web 容器的抽象方法定义)
这三个抽象类依次继承关系。
这里可以实现这里面的抽象类,来配置项目。
例如建立一个自定义的WebApplicationInitializer实现类,继承AbstractAnnotationConfigDispatcherServletInitializer,来指定项目中的配置。
![](https://img.haomeiwen.com/i301624/1f9dc867604ef691.png)
AbstractAnnotationConfigDispatcherServletInitializer 类里面已经创建AnnotationConfigWebApplicationContext的spring web容器,所以在实现类中只需要指定配置文件就好了。
- RootConfig 类似web.xml
- ServletConfig 类似servlet-mvc.xml
![](https://img.haomeiwen.com/i301624/be19584b82513359.png)
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配置文件里面的<mvc:annotation-driven/>,再次重声ServletConfig类似spring-mvc.xml,就是将xml的配置用JavaConfig的形式表示。
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//WebMvcConfigurerComposite.addWebMvcConfigurers
public void addWebMvcConfigurers(List<WebMvcConfigurer> 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<WebMvcConfigurer>的addInterceptors。
<context:component-scan/>和<mvc:annotation-driven/>区别
以前经常区分不了这两个东西,其实是不明白这个的实际作用导致,在这里,由上面的@EnableWebMvc可以得到该注解是为了向容器里面注入RequestMappingHandlerMapping等用于处理springmvc请求的类的。而 @EnableWebMvc是<mvc:annotation-driven/> Java Config的体现,因此,可以推测<mvc:annotation-driven/> 也是向容器里面注入RequestMappingHandlerMapping等用于处理springmvc请求的类的。查找用于解析<mvc:annotation-driven/> 的类
![](https://img.haomeiwen.com/i301624/8dac0241158010c4.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的请求。
网友评论