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

相关推荐
寒山李白1 小时前
JavaSE核心知识点01基础语法01-04(数组)
java·学习·数组
BOB-wangbaohai1 小时前
Flowable7.x学习笔记(十八)拾取我的待办
笔记·学习
Java~~1 小时前
山东大学软件学院项目实训-基于大模型的模拟面试系统-个人主页头像上传
java·vue.js·spring
叫我王富贵i1 小时前
0基础学习鸿蒙开发-HarmonyOS4
学习·华为·harmonyos·arkts
十年之少1 小时前
引用第三方自定义组件——微信小程序学习笔记
笔记·学习·微信小程序
佩奇的技术笔记2 小时前
Java学习手册:Spring 生态其他组件介绍
java·spring
极客智谷2 小时前
Spring AI系列——大模型驱动的自然语言SQL引擎:Java技术实现详解
java·人工智能·spring
zfj3213 小时前
spring-boot-maven-plugin 将spring打包成单个jar的工作原理
spring·maven·jar·springboot·classloader·打包
ll7788114 小时前
C++学习之路,从0到精通的征途:stack_queue的模拟实现及deque原理介绍
开发语言·c++·笔记·学习·职场和发展
神秘的t4 小时前
Spring Web MVC————入门(1)
java·spring·mvc