Springboot之---强大的Servlet(一)

举报
赵KK日常技术记录 发表于 2023/06/21 15:56:31 2023/06/21
【摘要】 If I have seen further, it is by standing on the shoulders of giants 如果我比别人看得更远,那是因为我站在巨人的肩膀上 如今回头看下Servlet不仅如此强大,还具有很强烈的参考意义,能在现如今流行的大部分框架中找到它的影子。下面文章不止与探索Servlet,可能在其中穿插其他的关联知识点,旨在能从此次的学习中获取更多的知...

If I have seen further, it is by standing on the shoulders of giants

如果我比别人看得更远,那是因为我站在巨人的肩膀上

如今回头看下Servlet不仅如此强大,还具有很强烈的参考意义,能在现如今流行的大部分框架中找到它的影子。下面文章不止与探索Servlet,可能在其中穿插其他的关联知识点,旨在能从此次的学习中获取更多的知识点参考资料总结,转化为自己的理解输出,在文中我尽量以截图+复制全限定类名的方式记录,以便感兴趣的再次查找。

Springboot与Servlet

在springboot中内嵌了Tomcat容器,而Tomcat又是Servlet的容器,Springboot就与Servlet产生了紧密的联系。
在分析各个类时,注意下每个类所在的包是如何在tomcat与boot之间跨越的~

生命周期

1、初始化
2、处理请求
3、销毁
流程

应用上下文ServletContext

应用上下文即可看做:一次请求到达,到响应结束的过程中间的catlog,即阅读中结合上下文语境,是一个广义定义。
为什么说到上下文呢?来看下ServletContext的实现,第一个经典实现既是ApplicationContext我们不止在一次源码和应用中见到它,另外加载器目前有两种选择:ContextLoaderListener和ContextLoaderServlet。其功能是完全相同。会在下文进行介绍

/**
 * Standard implementation of <code>ServletContext</code> that represents
 * a web application's execution environment.  An instance of this class is
 * associated with each instance of <code>StandardContext</code>.
 * 代表web应用程序的执行环境。这个类的一个实例是
 *与StandardContext的每个实例关联。
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 */
public class ApplicationContext implements ServletContext {

    protected static final boolean STRICT_SERVLET_COMPLIANCE;///翻译为是否严格遵守

    protected static final boolean GET_RESOURCE_REQUIRE_SLASH;//获取资源是否需要斜线。

    static {
        STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE;

        String requireSlash = System.getProperty("org.apache.catalina.core.ApplicationContext.GET_RESOURCE_REQUIRE_SLASH");
        if (requireSlash == null) {
            GET_RESOURCE_REQUIRE_SLASH = STRICT_SERVLET_COMPLIANCE;
        } else {
            GET_RESOURCE_REQUIRE_SLASH = Boolean.parseBoolean(requireSlash);
        }
    }

注:特别重要上述配置为tomcat中第一个开关配置,决定多个属性的值。来自于下面的Globals.STRICT_SERVLET_COMPLIANCE;默认为false
验证:

     public static final boolean STRICT_SERVLET_COMPLIANCE =Boolean.parseBoolean(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false"));

和官网截图
流程
问题:会因为tomcat的版本配置不同改变此值,在8.5.57当中会改变为true,当controller中配置多个映射路径会出现访问不到的问题
此处参考博文:https://blog.csdn.net/xing930408/article/details/111225064
Tomcat文档:https://tomcat.apache.org/tomcat-8.5-doc/config/systemprops.html
而GET_RESOURCE_REQUIRE_SLASH直接赋值为STRICT_SERVLET_COMPLIANCE

SpringBoot ApplicationContext

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver

都在说ApplicationContext衍生了BeanFactory那么对此都扩展了那些功能?ApplicationContext定了高级容器的基本规范,其实他也不是直接继承BeanFactory基础容器,可以看到ApplicationContext的直接父接口对BeanFactory进行很多拓展其中就包括:
1.事件的注册和发布
2.消息解析
3.资源解析
4.Bean工厂层级管理
5.监听器
6.容器环境
通过以上拓展,我们基本可以知道高级IOC容器有哪些特点,这也是学习整个ApplicationContext容器重点了解的部分

再比较ApplicationContext和ConfigurableApplicationContext定义的方法以及下图层级关系,ConfigurableApplicationContext是ApplicationContext的子接口,也就包含了ApplicationContext。通过方法可以知道,ConfigurableApplicationContext重在对各种属性的配置,而ApplicationContext接口主要各种属性的get方法。

Spring这种将get和set分开到两个接口的设计增大了属性设置和获取的灵活性,将两者分开也更加清晰。在以后的解决方案设计中,可以参考,将配置信息和获取信息分开,两者互不干扰,在保证重要的基础属性不变的情况,可以按需进行拓展。其实Spring的框架设计中应用了大量的装饰者模式,这也是高拓展点的需要

该类提供了高级IOC规范
其中有几个注意点:

从ListableBeanFactory接口继承来的:用于访问应用组件的工厂方法
从ResourceLoader接口继承来的:用通用的方式加载文件资源
从ApplicationEventPublisher接口继承来的:注册和发布事件
从MessageSource接口继承来的:处理消息,支持国际化

从父应用上下文定义的在子上下文中将始终保持优先

https://www.javaguides.net/2019/10/how-to-get-application-context-in-spring-boot.html

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    /**
     * 返回一个唯一的应用上下文id+
     * @return the unique id of the context, or {@code null} if none
     */
    @Nullable
    String getId();

    /**
     * 返回已经部署的该应用上下文的名称.
     * @return a name for the deployed application, or the empty String by default
     */
    String getApplicationName();

    /**
     * 返回此上下文友好的名称---这有什么用呢?
     * @return a display name for this context (never {@code null})
     */
    String getDisplayName();

    /**
     * 返回该上下文第一次被加载的时间戳
     * @return the timestamp (ms) when this context was first loaded
     */
    long getStartupDate();

    /**
     *返回父应用上下文, 如果没有父上下文,该上下文就是在上下文层次的根
     * @return the parent context, or {@code null} if there is no parent
     */
    @Nullable
    ApplicationContext getParent();


    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}


这其中对AutowireCapableBeanFactory getAutowireCapableBeanFactory()方法有疑惑,然后网上百度了一下。Spring提供了一种机制,能够为第三方框架赋能,让Spring去管理的Bean去装配和填充那些没有被SpringIOC管理的bean。也就是Spring提供了使用第三方框架的能力,能够做到无缝的将第三方框架整合到Spring中来进行使用,Junit与Quartz借用了这种机制为自己赋能。

————————————————
版权声明:本文为CSDN博主「还你一梦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ligel136738/article/details/113533132

AutowireCapableBeanFactory

package org.springframework.beans.factory.config;

import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.lang.Nullable;


public interface AutowireCapableBeanFactory extends BeanFactory {

  
    int AUTOWIRE_NO = 0;

  
    int AUTOWIRE_BY_NAME = 1;

   
    int AUTOWIRE_BY_TYPE = 2;

   
    int AUTOWIRE_CONSTRUCTOR = 3;

   
    @Deprecated
    int AUTOWIRE_AUTODETECT = 4;

  
    String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";


    <T> T createBean(Class<T> beanClass) throws BeansException;

    void autowireBean(Object existingBean) throws BeansException;

   
    Object configureBean(Object existingBean, String beanName) throws BeansException;



    Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

    Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

   
    void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
            throws BeansException;

    
    void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;

    
    Object initializeBean(Object existingBean, String beanName) throws BeansException;

   
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException;

    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException;

    void destroyBean(Object existingBean);


   
    <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;

  
    @Nullable
    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;

    
    @Nullable
    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

}

在ApplacationContext中并没有实现此工厂接口,如上文可见,是在初始化Spring管理Bean之外的实例时直接调用getAutowireCapableBeanFactory方法
AutowireCapableBeanFactory定义了5种装配策略:

不自动注入:AUTOWIRE_NO
使用BeanName策略注入:AUTOWIRE_BY_NAME
使用类型装配策略:AUTOWIRE_BY_TYPE
使用构造器装配策略:AUTOWIRE_CONSTRUCTOR
自动装配策略:AUTOWIRE_AUTODETECT

以AbstractAutowireCapableBeanFactory为例,其实现如下

@@Override
    public void autowireBean(Object existingBean) {
        //使用非单例bean定义,以避免将bean注册为依赖bean
        // Use non-singleton bean definition, to avoid registering bean as dependent bean.
        RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
        Bean的作用域
        bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        检查给定类在给定上下文中是否是缓存安全的,
        即它是由给定类加载器加载还是由其父级加载。类加载器
        bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), getBeanClassLoader());
        BeanWrapper bw = new BeanWrapperImpl(existingBean);
        初始化BeanRapper并创建
        initBeanWrapper(bw);
        实际上该方法的逻辑主要是在populateBean中。这个方法是Spring中一个重要的方法。用于装配Bean。主要是通过反射获取到我们new出来的对象的属性及注解,若是注解时Autowired、Value、Inject时,进行Bean组装。此方法执行完毕,我们new出来的方法就可以通过注解注入的bean进行操作了
        作者:小胖学编程
        链接:https://www.jianshu.com/p/14dd69b5c516
        来源:简书
        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
        populateBean(bd.getBeanClass().getName(), bd, bw);
    }
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。