spring mvc源码学习笔记之九

在前面的文章中,我们简单讲了可以用 WebApplicationInitializer 接口去替换 web.xml

本文对这一块再做个详细讲解。

WebApplicationInitializer 这个接口的 javadoc 中有提到可以用继承 AbstractAnnotationConfigDispatcherServletInitializer 的方式替换实现 WebApplicationInitializer 接口。

先看代码,然后再具体解释。

  • pom.xml 的内容如下
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.qsm</groupId>
        <artifactId>learn</artifactId>
        <version>1.0.0</version>
    </parent>

    <groupId>com.qs</groupId>
    <artifactId>demo-47</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.28</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
  • com.qs.demo.MyWebApplicationInitializer 的内容如下
java 复制代码
package com.qs.demo;

import com.qs.demo.config.RootApplicationContextConfig;
import com.qs.demo.config.WebApplicationContextConfig;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @author qs
 * @date 2023/07/20
 */
public class MyWebApplicationInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {RootApplicationContextConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] {WebApplicationContextConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }
}
  • com.qs.demo.config.RootApplicationContextConfig 的内容如下
java 复制代码
package com.qs.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author qs
 * @date 2023/07/20
 */
@Configuration
@ComponentScan(basePackages = {"com.qs.demo.service", "com.qs.demo.dao"})
public class RootApplicationContextConfig {}
  • com.qs.demo.config.WebApplicationContextConfig 的内容如下
java 复制代码
package com.qs.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author qs
 * @date 2023/07/20
 */
@Configuration
@ComponentScan(basePackages = {"com.qs.demo.controller"})
public class WebApplicationContextConfig {}
  • com.qs.demo.controller.PeopleController 的内容如下
java 复制代码
package com.qs.demo.controller;

import com.qs.demo.service.PeopleService;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qs
 * @date 2023/07/20
 */
@RequestMapping("/people")
@RestController
public class PeopleController {

    private final PeopleService peopleService;

    public PeopleController(PeopleService peopleService) {
        this.peopleService = peopleService;
    }

    @GetMapping("/01")
    public String a() {
        return peopleService.a();
    }
}
  • com.qs.demo.service.PeopleService 的内容如下
java 复制代码
package com.qs.demo.service;

import com.qs.demo.dao.PeopleDao;

import org.springframework.stereotype.Service;

/**
 * @author qs
 * @date 2023/07/20
 */
@Service
public class PeopleService {

    private final PeopleDao peopleDao;

    public PeopleService(PeopleDao peopleDao) {
        this.peopleDao = peopleDao;
    }

    public String a() {
        return peopleDao.a();
    }
}
  • com.qs.demo.dao.PeopleDao 的内容如下
java 复制代码
package com.qs.demo.dao;

import org.springframework.stereotype.Repository;

import java.util.UUID;

/**
 * @author qs
 * @date 2023/07/20
 */
@Repository
public class PeopleDao {

    public String a() {
        return UUID.randomUUID().toString();
    }

}

以上就是全部代码

接下来重点分析下 AbstractAnnotationConfigDispatcherServletInitializer 这个类,源代码不多,如下

java 复制代码
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.support;

import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

/**
 * 下面这句话意思是:这个类是一个 WebApplicationInitializer(这个抽象类实现了 WebApplicationInitializer 接口)。
 * 它用于注册 DispatcherServlet 。
 * 并且,它使用 Java-based 的 spring 配置,也就是基于注解的配置方式。
 *
 * {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializer}
 * to register a {@code DispatcherServlet} and use Java-based Spring configuration.
 *
 * 这个类的实现者需要重写 getRootConfigClasses 这个抽象方法。用于指定 root 应用上下文的配置类。
 * 并且这个括号里边也备注了,所谓的 root 应用上下文,应该是非 web 相关的。啥意思呢,粗暴理解,就是 service | dao 这些东西放到 root 应用上下文。
 *
 * 这个类的实现者需要重写 getServletConfigClasses 这个抽象方法。用于指定 DispatcherServlet 的应用上下文的配置类。
 * 在这个配置类中配置的是跟 spring mvc 相关的配置信息。
 *
 * 对比这2个方法。我们可以看出。父子容器设定的一个出发点就是将web相关的东西和非web相关的东西隔离开。
 *
 * <p>Implementations are required to implement:
 * <ul>
 * <li>{@link #getRootConfigClasses()} -- for "root" application context (non-web
 * infrastructure) configuration.
 * <li>{@link #getServletConfigClasses()} -- for {@code DispatcherServlet}
 * application context (Spring MVC infrastructure) configuration.
 * </ul>
 *
 * 下面这段话意思是,如果不需要父子容器的话,可以将所有配置写到 getRootConfigClasses 指定的配置类中,
 * 让 getServletConfigClasses 返回 null。
 *
 * <p>If an application context hierarchy is not required, applications may
 * return all configuration via {@link #getRootConfigClasses()} and return
 * {@code null} from {@link #getServletConfigClasses()}.
 *
 * @author Arjen Poutsma
 * @author Chris Beams
 * @since 3.2
 */
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {

	/**
	 * {@inheritDoc}
	 * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
	 * providing it the annotated classes returned by {@link #getRootConfigClasses()}.
	 * Returns {@code null} if {@link #getRootConfigClasses()} returns {@code null}.
	 */
	@Override
	@Nullable
	protected WebApplicationContext createRootApplicationContext() {
	
        System.out.println("所谓创建根web应用上下文就是 new AnnotationConfigWebApplicationContext() ");
        
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
	 * providing it the annotated classes returned by {@link #getServletConfigClasses()}.
	 */
	@Override
	protected WebApplicationContext createServletApplicationContext() {
	
        System.out.println("createServletApplicationContext ----> new AnnotationConfigWebApplicationContext() ");
        
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

	/**
	 * Specify {@code @Configuration} and/or {@code @Component} classes for the
	 * {@linkplain #createRootApplicationContext() root application context}.
	 * @return the configuration for the root application context, or {@code null}
	 * if creation and registration of a root context is not desired
	 */
	@Nullable
	protected abstract Class<?>[] getRootConfigClasses();

	/**
	 * Specify {@code @Configuration} and/or {@code @Component} classes for the
	 * {@linkplain #createServletApplicationContext() Servlet application context}.
	 * @return the configuration for the Servlet application context, or
	 * {@code null} if all configuration is specified through root config classes.
	 */
	@Nullable
	protected abstract Class<?>[] getServletConfigClasses();

}

最后,AbstractAnnotationConfigDispatcherServletInitializer 这个抽象类的父类 AbstractDispatcherServletInitializer以及祖父 AbstractContextLoaderInitializer 都值得看看。

相关推荐
金色天际线-29 分钟前
Nginx 优化与防盗链配置指南
java·后端·spring
知识分享小能手4 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao7 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾7 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT8 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa8 小时前
HTML和CSS学习
前端·css·学习·html
看海天一色听风起雨落9 小时前
Python学习之装饰器
开发语言·python·学习
cyforkk9 小时前
Spring 异常处理器:从混乱到有序,优雅处理所有异常
java·后端·spring·mvc
Z_z在努力9 小时前
【杂类】Spring 自动装配原理
java·spring·mybatis
speop10 小时前
llm的一点学习笔记
笔记·学习