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 都值得看看。

相关推荐
lang2015092835 分钟前
Spring Boot集成Spring Integration全解析
spring boot·后端·spring
AhriProGramming38 分钟前
Python学习快速上手文章推荐(持续更新)
开发语言·python·学习·1024程序员节
泡泡鱼(敲代码中)1 小时前
数据结构(顺序表和链表)
笔记·学习·算法
大G的笔记本1 小时前
Spring IOC和AOP
java·后端·spring
无妄无望1 小时前
在没有网络的环境下安装包pymysql
学习·docker
酌量1 小时前
基于3D激光点云的障碍物检测与跟踪---(3)基于匈牙利算法的障碍物跟踪
学习·算法·机器人·匈牙利算法·障碍物跟踪
电子云与长程纠缠2 小时前
Blender入门学习04 - 材质
学习·blender
文火冰糖的硅基工坊2 小时前
[人工智能-大模型-21]:“AI 编程工作流”模板(含 prompt 示例)
人工智能·科技·学习·大模型·prompt
hello 早上好2 小时前
解析 Prompt 工程中的四大核心角色
spring
讽刺人生Yan3 小时前
RFSOC学习记录(六)混频模式分析
学习·fpga·rfsoc