Spring5.3学习——from 官网day1-1

Spring5.3学习——from 官网day1-1

前言

本系列为系统的spring框架学习系列,源于官方文档5.3.23版本,本系列文章是我第二次对于官网内容学习,也更加细致,所有内容均为自我总结,并非复制官网内容,若有错误的地方敬请指出,本系列并不太适合新生学习,很多都是源码阅读

概述

  • Spring是当前企业级Java应用最常用的框架
  • 支持Groovy以及Kotlin作为JVM上的代替语言
  • JDK版本建议8+,请大家逐渐适应使用11
  • Spring应用可以以jar包方式长时间的运行
  • Spring是开源的
  • Spring Framework就是我们常说的Spring框架
  • Spring框架是分模块的,应用程序可以自由的选择需要的模块
  • 核心容器模块包括配置模型和依赖注入机制
  • Spring提供基础支持,有消息传递,事务数据,持久化,Web,基于Servlet的Spring MVC,反应式Web框架Spring WebFlux

虽然JDK8和9开始相差巨大,但Spring框架支持你部署于更高的版本

Spring的设计理念

  1. 配置而不是代码,意思是Spring更希望我们以配置的方式控制代码切换业务,而不是直接取修改代码(例如:我们一开始选择使用持久化策略为mysql,但是最后实际业务中使用Oracle,此时按照以前的方式,我们需要去修改代码以完成mysql到Oracle的切换,而Spring则可以以修改配置文件的方式进行修改,无需更改一行代码)
  2. 整合而不是造轮子,意思是Spring不希望自己去重复造轮子,而是去整合本来就十分完善的解决策略,使得自身更加灵活,我们甚至可以将Spring看作是容器,而不是框架!
  3. 兼容而不是变革,Spring具有强大的向后兼容性,每一个版本的退出都经过精心的设计管理,方便维护依赖于Spring的应用程序和库,并且十分注重可用性强大API,使得API可以沿用多个版本
  4. 高标准而不是屎山,Spring的设计以及代码设计结构清晰,使用标准,避免循环依赖,粗制乱造的代码

Spring核心:IOC

什么是IOC

IOC:inversion of control,翻译过来就是控制反转,我们也称为依赖注入(dependency injection)
将IOC == DI可不是我说的是官网说的,但是我们常常看视频和一些文章的时候为什么常把DI和IOC并不说是同一个东西呢?
因为我们通常的认为IOC是一种思想,DI则是IOC的体现(但我并不完全同意这种说法,具体请看下面)
我来看一下下面官网对IOC的描述:

IOC是一个过程,对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后设置的属性来定义它们的依赖关系(即与它们一起工作的其他对象) . 然后容器在创建 bean 时注入这些依赖项。 这个过程基本上是 bean 本身通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖关系的实例化或位置的逆过程(因此称为控制反转)。

看起来不太好理解,那我们直接看解释

解释
  1. IOC指的是将对象的生命周期的权限委托给Spring,最具体的体现就是创建对象的控制权,而不是我们自己去new对象出来

现在我们这样想,平时我们写程序的时候对象需要我能new出来,好比我们吃饭,吃饭的菜需要我们自己去买,然后烧好
现在有了Spring,相当于Spring是一家餐馆,我们要吃什么,只要直接去餐馆里买就行,餐馆根据我们提供的点单就会帮我们准备好饭菜,这就是IOC
同样我们理解一下DI,依赖注入,请问依赖是什么?从哪里来的依赖?我们一下就想到,不就是程序中我们需要一个对象,但是这个对象需要依赖一个外部的jar包,这不就是我们索要的依赖吗!我们将这个也想像称一个菜单,那么菜单上的菜品就是对象,组成菜品所要的原材料就是依赖,接下来注入就是指将所需要的菜品从原材料做好给我们食用(使用)咯!我们将外部依赖转化为内部依赖,这样一想,DI和IOC就是一个概念嘛!
所以实际上DI和IOC是同一个概念的不同角度上的阐述!准确来说是应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源

  1. IOC其实是Java反射机制的具体体现,根据配置文件在运行时动态的去管理对象

为什么这样说呢?我们首先回忆一下,Java是一门静态语言,这就说明Java在不具备反射机制前无法具备动态特性,是反射让Java获得了动态特性吧!那么Spring如何才能根据配置文件运行时动态的管理对象呢?这样一想很明显结果一目了然,肯定是利用了反射才做到的嘛!

IOC容器的包

org.springframework.beansorg.springframework.context包是基础用于 Spring Framework 的 IoC 容器的基础

什么是Bean

在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。 bean 是由 Spring IoC 容器实例化、组装和管理的对象。 否则,bean 只是应用程序中的众多对象之一。 Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。

BeanFactory接口简述

BeanFactory接口提供了一种高级配置机制,能够管理任意类型的对象,BeanFactory 提供了配置框架和基本功能

ApplicationContext接口简述

ApplicationContext是BeanFactory的子接口
增加了更多的企业特定功能,添加了以下特性

  1. 更容易与 Spring 的 AOP 功能集成
  2. 消息资源处理(用于国际化)
  3. 活动发布
  4. 应用层特定上下文,例如用于 Web 应用程序的 WebApplicationContext。
    ApplicationContext 是 BeanFactory 的完整超集,专门用于描述 Spring 的 IoC 容器。
    负责实例化bean,配置bean,组装 bean

容器通过读取配置元数据来获取关于要实例化、配置和组装哪些对象的指令。 (配置元数据以 XML、Java 注释或 Java 代码表示。 它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系)

在程序中我们通常使用 ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext 的实例,作为ApplicationContext接口的实现。
如下是Spring容器中应用程序与配置相结合形成完全配置可执行使用的应用程序
在这里插入图片描述

BeanFactory源码

描述

用于访问 Spring bean 容器的根接口。

以下是Bean工厂创建和销毁bean的完整生命周期流程

Bean 工厂实现应尽可能支持标准的 bean 生命周期接口。全套初始化方法及其标准顺序为:

  1. BeanNameAware 的setBeanName
  2. BeanClassLoaderAware 的setBeanClassLoader
  3. BeanFactoryAware 的setBeanFactory
  4. EnvironmentAware 的setEnvironment
  5. EmbeddedValueResolverAware 的setEmbeddedValueResolver
  6. ResourceLoaderAware 的setResourceLoader (仅在应用程序上下文中运行时适用)
  7. ApplicationEventPublisherAware 的setApplicationEventPublisher (仅在应用程序上下文中运行时适用)
  8. MessageSourceAware 的setMessageSource (仅在应用程序上下文中运行时适用)
  9. ApplicationContextAware 的setApplicationContext (仅在应用程序上下文中运行时适用)
  10. ServletContextAware 的setServletContext (仅适用于在 Web 应用程序上下文中运行时)
  11. BeanPostProcessors 的postProcessBeforeInitialization方法
  12. InitializingBean 的afterPropertiesSet
  13. 自定义init-method定义
  14. BeanPostProcessors 的postProcessAfterInitialization方法

在关闭 bean 工厂时,将应用以下生命周期方法:

  1. DestructionAwareBeanPostProcessors 的postProcessBeforeDestruction方法
  2. DisposableBean 的destroy
  3. 自定义destroy-method定义

常量&

用于取消引用FactoryBean实例并将其与 FactoryBean创建的 bean 区分开来。当我们获取某个bean时获取到的是工厂而不是实例

String FACTORY_BEAN_PREFIX = "&";

获取Bean实例

通过Bean的名字获取bean
Object getBean(String name) throws BeansException;
通过Bean名字获取bean并预设返回Bean的类型

行为与getBean(String)相同,但如果 bean 不属于所需类型,则通过抛出 BeanNotOfRequiredTypeException 来提供类型安全度量。

<T> T getBean(String name, Class<T> requiredType) throws BeansException;
通过Bean名字获取bean并通过传入参数进行显示构造

允许指定显式构造函数参数/工厂方法参数,覆盖 bean 定义中指定的默认参数

Object getBean(String name, Object... args) throws BeansException;
通过Bean的类型获取Bean实例

此方法进入ListableBeanFactory按类型查找领域,但也可以转换为基于给定类型名称的常规按名称查找。要跨 bean 集进行更广泛的检索操作,请使用ListableBeanFactory和/或BeanFactoryUtils 。

<T> T getBean(Class<T> requiredType) throws BeansException;
通过Bean名字获取bean并通过类型约束传入参数进行显示构造
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

获取Bean的提供者

通过传入Bean的类型获取Bean的提供者
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
通过ResolvableType对Bean类型进行规范

ResolvableType的.forType(Class<T> classType)方法获取到传入Bean的类型进行规范

<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

判断bean工厂中是否含有指定Bean

判断这个 bean 工厂是否包含 bean 定义或外部注册的具有给定名称的单例实例?(如果给定的名称是别名,它将被翻译回相应的规范 bean 名称。)
如果这个工厂是分层的,如果在这个工厂实例中找不到 bean,将询问各个父工厂,直到查询完毕

boolean containsBean(String name);

判断Bean为单例或多例

首先我们要知道什么是Spring中的单例Bean、多例Bean(原型Bean)
单例:每次我们获取bean时并不会重新创建一个新的实例,而是永远使用一个
多例:每次我们获取bean时会创建一个独立的新的实例

判断Bean是否为单例Bean

返回false并不代表这个Bean是个独立实例,仅仅表示它是非单例实例(可能在某些特定作用域里是这样的),我们需要使用isPrototype方法显式检查独立实例

boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
判断Bean是否为多例Bean

判断这个bean是原型吗?也就是说, getBean总是会返回独立的实例吗?(true表示是原型会返回独立的实例)
返回false仅仅表示非独立实例,也可能对应于作用域 bean。使用isSingleton操作显式检查共享单例实例

boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

判断类型是否匹配

判断传入的Bean类型是否和指定类型相符合

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

获取Bean的类型

根据传入的Bean名称获取Bean的类型

Class<?> getType(String name) throws NoSuchBeanDefinitionException;

根据allowFactoryBeanInit标志,如果没有可用的早期类型信息,这可能会导致对先前未初始化的FactoryBean进行初始化

Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

返回给定 bean 名称的别名

在getBean调用中使用时,所有这些别名都指向同一个 bean。
如果给定名称是别名,则将返回相应的原始 bean 名称和其他别名(如果有),原始 bean 名称是数组中的第一个元素。遍历数组会获取除了你传入的名称以为所有的别名

String[] getAliases(String name);

简单示例

如下给出了所有的方法示例

TestBean

创建该类指明@Component注解,其中@Bean注解的name属性它用于指定别名

@Component
public class TestBean {
    @Bean
    public void test(){
        System.out.println("use function name as bean name");
    }
    @Bean(name = {"testing","myName","Name2"})
    public void test2(){
        System.out.println("use bean name");
    }
}

测试
@SpringBootApplication
public class Beanfactory1Application {

    public static void main(String[] args) {
        final ConfigurableApplicationContext run = SpringApplication.run(Beanfactory1Application.class, args);

        /**通过默认方法名作为Bean实例名称,获取bean实例
         * @Bean
         *     public void test(){
         *         System.out.println("use function name as bean name");
         *     }
         */
        run.getBean("test");
        //通过bean的类型获取Bean实例(类)
        final TestBean testBean = run.getBean("testBean", TestBean.class);
        testBean.test2();
        //通过bean名称获取bean(@Bean("testing"))
        run.getBean("testing");
        //获取Bean的提供者
        final ObjectProvider<? extends TestBean> beanProvider = run.getBeanProvider(testBean.getClass());
        System.out.println(beanProvider);
        final ObjectProvider<Object> beanProvider1 = run.getBeanProvider(ResolvableType.forType(testBean.getClass()));
        System.out.println(beanProvider1);
        //判断是否含有Bean
        System.out.println(run.containsBean("testing"));
        //判断是否单例
        System.out.println(run.isSingleton("testing"));
        //判断是否多例
        System.out.println(run.isPrototype("testBean"));
        //判断传入的Bean类型是否和指定类型相符合
        System.out.println(run.isTypeMatch("testBean", TestBean.class));
        final Class<?> testing = run.getType("testing");
        System.out.println(testing);
        final Class<?> testBean1 = run.getType("testBean", false);
        System.out.println(testBean1);
        //这里并没有有别名所有会引发ArrayIndexOutOfBoundsException数组越界
        final String[] beanAliasesName = run.getAliases("myName");
        System.out.println(beanAliasesName[0]);
        for (String s : beanAliasesName) {
            System.out.println("-----------------");
            System.out.println(s);
        }
    }

}

在这里插入图片描述

ApplicationContext扩展说明

由类图可以看出,ApplicationContext接口扩展了MessageSource, ResourcePatternResolver, ApplicationEventPublisher,EnvironmentCapable四个接口,接下来我们详细看一下源码
在这里插入图片描述

MessageSource源码及说明

用于解析消息的策略接口,支持此类消息的参数化和国际化
我们要知道的是我们必须要由相关的映射文件才可以进行匹配解析,需要以Locale类进行标准映射如下:

public static final Locale ENGLISH = createConstant("en", "");

    /** Useful constant for language.
     */
    public static final Locale FRENCH = createConstant("fr", "");

    /** Useful constant for language.
     */
    public static final Locale GERMAN = createConstant("de", "");

    /** Useful constant for language.
     */
    public static final Locale ITALIAN = createConstant("it", "");

    /** Useful constant for language.
     */
    public static final Locale JAPANESE = createConstant("ja", "");

    /** Useful constant for language.
     */
    public static final Locale KOREAN = createConstant("ko", "");

    /** Useful constant for language.
     */
    public static final Locale CHINESE = createConstant("zh", "");

    /** Useful constant for language.
     */
    public static final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");

    /** Useful constant for language.
     */
    public static final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");

    /** Useful constant for country.
     */
    public static final Locale FRANCE = createConstant("fr", "FR");

    /** Useful constant for country.
     */
    public static final Locale GERMANY = createConstant("de", "DE");

    /** Useful constant for country.
     */
    public static final Locale ITALY = createConstant("it", "IT");

    /** Useful constant for country.
     */
    public static final Locale JAPAN = createConstant("ja", "JP");

    /** Useful constant for country.
     */
    public static final Locale KOREA = createConstant("ko", "KR");
获取消息并进行国际化

尝试解决该消息。如果未找到消息,则返回默认消息。

String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

尝试解决该消息。如果找不到消息,则视为错误

String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

尝试使用传入的MessageSourceResolvable参数中包含的所有属性来解析消息。
注意:我们必须在此方法上抛出NoSuchMessageException ,因为在调用此方法时,我们无法确定可解析的defaultMessage属性是否为null

String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

示例

1.编写properties设置映射关系

在这里插入图片描述

messages.properties什么都不写

messages_en.properties
hi=say hi
messages_zh.properties
hi=你好
2.编写国际化信息转换
System.out.println(run.getMessage("hi", null, Locale.CHINA));

System.out.println(run.getMessage("hi",null,Locale.ENGLISH));

我们可以看到输出就是和我们映射相同的内容
在这里插入图片描述

ResourceLoader源码及说明

用于加载资源(例如,类路径或文件系统资源)的策略接口
DefaultResourceLoader是一个独立的实现,可以在 ApplicationContext 之外使用,也被ResourceEditor使用。
当在 ApplicationContext 中运行时,可以使用特定上下文的资源加载策略从字符串中填充Resource和Resource[]类型的 Bean 属性。

常量

从类路径加载的伪 URL 前缀:“classpath:”

String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

我们可以看到这个不是直接写的classpath:这样的字符串而是使用ResourceUtils这个抽象类中的常量,所以我们下面看一下ResourceUtils

ResourceUtils源码及说明

用于将资源位置解析为文件系统中的文件的实用方法。主要供框架内部使用,在package org.springframework.util;包下,主要提供与ResourceLoader的getResource()方法将任意位置的Resource对象进行加载

常量
	/** Pseudo URL prefix for loading from the class path: "classpath:". */
	public static final String CLASSPATH_URL_PREFIX = "classpath:";

	/** URL prefix for loading from the file system: "file:". */
	public static final String FILE_URL_PREFIX = "file:";

	/** URL prefix for loading from a jar file: "jar:". */
	public static final String JAR_URL_PREFIX = "jar:";

	/** URL prefix for loading from a war file on Tomcat: "war:". */
	public static final String WAR_URL_PREFIX = "war:";

	/** URL protocol for a file in the file system: "file". */
	public static final String URL_PROTOCOL_FILE = "file";

	/** URL protocol for an entry from a jar file: "jar". */
	public static final String URL_PROTOCOL_JAR = "jar";

	/** URL protocol for an entry from a war file: "war". */
	public static final String URL_PROTOCOL_WAR = "war";

	/** URL protocol for an entry from a zip file: "zip". */
	public static final String URL_PROTOCOL_ZIP = "zip";

	/** URL protocol for an entry from a WebSphere jar file: "wsjar". */
	public static final String URL_PROTOCOL_WSJAR = "wsjar";

	/** URL protocol for an entry from a JBoss jar file: "vfszip". */
	public static final String URL_PROTOCOL_VFSZIP = "vfszip";

	/** URL protocol for a JBoss file system resource: "vfsfile". */
	public static final String URL_PROTOCOL_VFSFILE = "vfsfile";

	/** URL protocol for a general JBoss VFS resource: "vfs". */
	public static final String URL_PROTOCOL_VFS = "vfs";

	/** File extension for a regular jar file: ".jar". */
	public static final String JAR_FILE_EXTENSION = ".jar";

	/** Separator between JAR URL and file path within the JAR: "!/". */
	public static final String JAR_URL_SEPARATOR = "!/";

	/** Special separator between WAR URL and jar part on Tomcat. */
	public static final String WAR_URL_SEPARATOR = "*/";
判断给定的资源是否为URL

需要传入一个目标资源路径进行判断

  1. 判断传入路径不为空
  2. 判断传入路径的起始为classpath:
  3. 若起始不为classpath:则尝试new URL并传入目标资源路径作为构造函数的参数,并返回true
public static boolean isUrl(@Nullable String resourceLocation) {
//1. 
		if (resourceLocation == null) {
			return false;
		}
		//2.
		if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
			return true;
		}
		try {
			new URL(resourceLocation);
			return true;
		}
		catch (MalformedURLException ex) {
			return false;
		}
	}

//new URL(String resourceLocation)
//从String表示创建一个URL对象
public URL(String spec) throws MalformedURLException {
        this(null, spec);
    }
public URL(URL context, String spec) throws MalformedURLException {
        this(context, spec, null);
    }

public URL(URL context, String spec, URLStreamHandler handler)
        throws MalformedURLException
    {
        String original = spec;
        int i, limit, c;
        int start = 0;
        String newProtocol = null;
        boolean aRef=false;
        boolean isRelative = false;

        // Check for permission to specify a handler
        if (handler != null) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkSpecifyHandler(sm);
            }
        }

        try {
            limit = spec.length();
            while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
                limit--;        //eliminate trailing whitespace
            }
            while ((start < limit) && (spec.charAt(start) <= ' ')) {
                start++;        // eliminate leading whitespace
            }

            if (spec.regionMatches(true, start, "url:", 0, 4)) {
                start += 4;
            }
            if (start < spec.length() && spec.charAt(start) == '#') {
                /* we're assuming this is a ref relative to the context URL.
                 * This means protocols cannot start w/ '#', but we must parse
                 * ref URL's like: "hello:there" w/ a ':' in them.
                 */
                aRef=true;
            }
            for (i = start ; !aRef && (i < limit) &&
                     ((c = spec.charAt(i)) != '/') ; i++) {
                if (c == ':') {
                    String s = toLowerCase(spec.substring(start, i));
                    if (isValidProtocol(s)) {
                        newProtocol = s;
                        start = i + 1;
                    }
                    break;
                }
            }

            // Only use our context if the protocols match.
            protocol = newProtocol;
            if ((context != null) && ((newProtocol == null) ||
                            newProtocol.equalsIgnoreCase(context.protocol))) {
                // inherit the protocol handler from the context
                // if not specified to the constructor
                if (handler == null) {
                    handler = context.handler;
                }

                // If the context is a hierarchical URL scheme and the spec
                // contains a matching scheme then maintain backwards
                // compatibility and treat it as if the spec didn't contain
                // the scheme; see 5.2.3 of RFC2396
                if (context.path != null && context.path.startsWith("/"))
                    newProtocol = null;

                if (newProtocol == null) {
                    protocol = context.protocol;
                    authority = context.authority;
                    userInfo = context.userInfo;
                    host = context.host;
                    port = context.port;
                    file = context.file;
                    path = context.path;
                    isRelative = true;
                }
            }

            if (protocol == null) {
                throw new MalformedURLException("no protocol: "+original);
            }

            // Get the protocol handler if not specified or the protocol
            // of the context could not be used
            if (handler == null &&
                (handler = getURLStreamHandler(protocol)) == null) {
                throw new MalformedURLException("unknown protocol: "+protocol);
            }

            this.handler = handler;

            i = spec.indexOf('#', start);
            if (i >= 0) {
                ref = spec.substring(i + 1, limit);
                limit = i;
            }

            /*
             * Handle special case inheritance of query and fragment
             * implied by RFC2396 section 5.2.2.
             */
            if (isRelative && start == limit) {
                query = context.query;
                if (ref == null) {
                    ref = context.ref;
                }
            }

            handler.parseURL(this, spec, start, limit);

        } catch(MalformedURLException e) {
            throw e;
        } catch(Exception e) {
            MalformedURLException exception = new MalformedURLException(e.getMessage());
            exception.initCause(e);
            throw exception;
        }
    }

说明:

  • 进入new URL后其中public URL(URL context, String spec, URLStreamHandler handler)中仅spec有传值,其他两个参数为空
  • 进入到try子块中,首先limit获取为spec的字符串长度,接下来通过 while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) { limit--; //eliminate trailing whitespace }这个循环消除路径中尾部的空格,这说明如果我们不小心打了空格也没有关系
  • 然后是去除首部的空格,这里可以借鉴一下
  • 使用regionMatches方法将spec变量和url:两个字符串相比较看看是不是相同,若是则跳过4个字符长度start += 4(忽略大小写,使用start作为偏移量【这里的目的就是不受到空格的影响,只比较非空格部分】,于url:进行比较,比较4个字符长度)
  • 判断start当前是否小于spec的字符串长度,并且其起始位置为#,若是则aRef为true
  • 进行for循环目的是判断传入的资源地址是否为协议格式
    • 当匹配是协议时URL类的属性protocol附上我们传入的目标地址
  • 最后对URL进行解析
获取文件

将给定的资源 URL 解析为java.io.File ,即文件系统中的文件

//1.
public static File getFile(URL resourceUrl)
//2.第二个参数表示描述信息
public static File getFile(URL resourceUrl, String description)
//3.
public static File getFile(URI resourceUri)
//4.
public static File getFile(URI resourceUri, String description)
判断给定的路径是否为文件路径
public static boolean isFileURL(URL url)
判断是否为jar包路径

确定给定的 URL 是否指向 jar 文件中的资源。即具有协议“jar”、“war”、“zip”、“vfszip”或“wsjar”。

public static boolean isJarURL(URL url) 
判断是否指定jar包本身的路径
public static boolean isJarFileURL(URL url) 
从给定的 URL 中提取实际 jar 文件的 URL
public static URL extractJarFileURL(URL jarUrl) 
为给定的位置字符串创建一个 URI 实例,首先用“%20”URI 编码替换空格
public static URI toURI(String location)

public static URI toURI(URL url)
设定使用缓存标志

对于基于 JNLP 的资源,首选false ,但将标志保留为true

public static void useCachesIfNecessary
返回指定资源位置的Resource句柄

句柄应始终是可重用的资源描述符,允许多个Resource.getInputStream()调用。

必须支持完全限定的 URL,例如“file:C:/test.dat”。
必须支持类路径伪 URL,例如“classpath:test.dat”。
应该支持相对文件路径,例如“WEB-INF/test.dat”。 (这将是特定于实现的,通常由 ApplicationContext 实现提供。)

请注意, Resource句柄并不意味着现有资源;您需要调用Resource.exists来检查是否存在

Resource getResource(String location);
获取类加载器

获取的是ResourceLoader使用的ClassLoader

ClassLoader getClassLoader();

ResourcePatternResolver源码及说明

这是ResourceLoader接口的扩展。可以检查传入的ResourceLoader

常量

表示resource路径下所有的资源路径

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
根据传入目标资源地址获取资源

将给定的位置模式解析为Resource对象

Resource[] getResources(String locationPattern) throws IOException;
示例:获取资源
final ConfigurableApplicationContext run = SpringApplication.run(Beanfactory1Application.class, args);
//当前项目下的资源
    final Resource[] resources = run.getResources("classpath:application.properties");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
//所有包括项目外部库中的资源
        final Resource[] resources1 = run.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources1) {
            System.out.println(resource);
        }

在这里插入图片描述

EnvironmentCapable源码及说明

指示包含和公开Environment引用的组件的接口,意思是整个Spring的环境

所有 Spring 应用程序上下文都是 EnvironmentCapable,并且该接口主要用于在框架方法中执行instanceof检查,这些方法接受 BeanFactory 实例,这些实例可能实际上是也可能不是 ApplicationContext 实例,以便与环境交互(如果确实可用)。
如前所述, ApplicationContext扩展了 EnvironmentCapable,因此公开了一个getEnvironment()方法;但是, ConfigurableApplicationContext重新定义getEnvironment()并缩小了签名以返回ConfigurableEnvironment 。效果是 Environment 对象在从 ConfigurableApplicationContext 访问之前是“只读的”,此时它也可以被配置。

获取当前环境

返回与此组件关联的Environment

Environment getEnvironment();
示例:获取当前环境参数
final ConfigurableApplicationContext run = SpringApplication.run(Beanfactory1Application.class, args);
   System.out.println(run.getEnvironment().getProperty("java_home"));
        System.out.println(run.getEnvironment().getProperty("server.port"));
        System.out.println(run.getEnvironment().getProperty("define.testName"));

在这里插入图片描述

以下是application.properties中的内容

server.port=8557

define.testName=hello spring

当然我们可以直接获取系统所有参数

System.out.println(run.getEnvironment().getSystemEnvironment());
{USERDOMAIN_ROAMINGPROFILE=SYFS-COMPUTER, PROCESSOR_LEVEL=6, Maven_HOME=D:\maven\apache-...}

ApplicationEventPublisher源码及说明

封装事件发布功能的接口。
作为ApplicationContext的超级接口

发布事件

通知在此应用程序中注册的所有匹配的侦听器一个事件。
如果指定的event不是ApplicationEvent ,则将其包装在PayloadApplicationEvent中

//这是默认实现
default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
	
void publishEvent(Object event);

大家请关注这句话如果指定的event不是ApplicationEvent,则将其包装在PayloadApplicationEvent中,这句话说明了如果我们要自己定义一个事件,则需要继承ApplicationEvent

事件示例1
1.自定义事件
public class EasyEvent extends ApplicationEvent {
    public EasyEvent(Object source) {
        super(source);
    }
}
2.自定义事件监听器

我们要确保使用了@Component注册为组件,如下

@Component
public class DefineEventListener {

    @EventListener
    public void defineListener(EasyEvent event){
        System.out.println("========Listen======");
        System.out.println(event);
    }
}
3.测试
final ConfigurableApplicationContext run = SpringApplication.run(Beanfactory1Application.class, args);
//发布事件
run.publishEvent(new EasyEvent(run));

在这里插入图片描述

事件示例2:正常业务使用

以下场景:我们需要对某个事件进行相关处理,当我们接收到用户请求之后,我们需要发送一个短信给用户,我们就可以使用事件

事件发布对象
@Component
public class DefineEventPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void registerEvent(){
        publisher.publishEvent(new EasyEvent(this));
    }

}
测试
final ConfigurableApplicationContext run = SpringApplication.run(Beanfactory1Application.class, args);

run.getBean(DefineEventPublisher.class).registerEvent();

在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐