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

相关推荐
我想我不够好。6 小时前
监控学习 7.23 1.5hour
学习
suaizai_7 小时前
Spring7新宠:RestClient全面解析
spring
大模型码小白7 小时前
Spring AI 框架实战:Java 后端集成大模型的架构设计与工程落地
java·人工智能·python·spring
泰勒朗斯8 小时前
Helicopter FBI-405尾电机版本设置
学习
minglie19 小时前
香橙派zero3配个iic屏显示ip
linux·学习
微露清风10 小时前
模拟算法学习记录
学习·算法
不爱记笔记10 小时前
上下班路上怎么消化技术视频?我把B站内容转成播客,通勤时间也能系统学习
学习·ai·音视频·知识库·知识管理
霸道流氓气质11 小时前
基于 Spring 事务同步机制的事务后置动作收集器 Starter 实践
java·后端·spring
筱谙11 小时前
BES BLE CTKD 完整学习笔记
笔记·学习
minglie111 小时前
zynq开发板的xvcServer作为烧录器
学习