Spring IoC资源管理之ResourceLoader

简介: Spring IoC资源管理之ResourceLoader

概述


在上一章节Spring IoC资源管理之Resource提到 Spring 将资源的定义和资源的加载区分开了,Resource 定义了统一的资源,那资源的加载则由  ResourceLoader 来统一定义。


ResourceLoader


org.springframework.core.io.ResourceLoader 为 Spring 资源加载的统一抽象,具体的资源加载则由相应的实现类来完成,所以我们可以将 ResourceLoader 称作为统一资源定位器。其定义如下:


public interface ResourceLoader {
    String CLASSPATH_URL_PREFIX = "classpath:";
    Resource getResource(String var1);
    @Nullable
    ClassLoader getClassLoader();
}
复制代码


作为 Spring 统一的资源加载器,它提供了统一的抽象,具体的实现则由相应的子类来负责实现,其类的类结构图如下:


image.png


ResourceLoader 接口提供两个方法:getResource()getClassLoader()

getResource() 根据所提供的路径 location 返回 Resource 实例,但是它不确保该 Resource 一定存在,需要调用 Resource.exist() 方法判断。该方法支持以下模式的资源加载:

  • URL位置资源,如“file:D:/conf.xml”
  • ClassPath位置资源,如“classpath:conf.xml”
  • 相对路径资源,如"conf/conf.xml",此时返回的 Resource 实例根据实现不同而不同

该方法的主要实现是在其子类 DefaultResourceLoader 中实现,具体过程我们在分析 DefaultResourceLoader 时做详细说明。

getClassLoader() 返回 ClassLoader 实例,对于想要获取 ResourceLoader 使用的 ClassLoader 用户来说,可以直接调用该方法来获取。


DefaultResourceLoader


DefaultResourceLoader 是 ResourceLoader 的默认实现,它接收 ClassLoader 作为构造函数的参数或者使用不带参数的构造函数,在使用不带参数的构造函数时,使用的 ClassLoader 为默认的 ClassLoader(一般为Thread.currentThread().getContextClassLoader()),可以 通过 ClassUtils.getDefaultClassLoader()获取。当然也可以调用 setClassLoader()方法进行后续设置。如下:


public DefaultResourceLoader() {
    this.classLoader = ClassUtils.getDefaultClassLoader();
}
public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
    this.classLoader = classLoader;
}
public void setClassLoader(@Nullable ClassLoader classLoader) {
    this.classLoader = classLoader;
}
@Nullable
public ClassLoader getClassLoader() {
    return this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader();
}
复制代码


ResourceLoader 中最核心的方法为 getResource(),它根据提供的 location 返回相应的 Resource,而 DefaultResourceLoader 对该方法提供了核心实现(它的两个子类都没有提供覆盖该方法,所以可以断定ResourceLoader 的资源加载策略就封装 DefaultResourceLoader中),如下:


public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");
    Iterator var2 = this.getProtocolResolvers().iterator();
    Resource resource;
    do {
        if (!var2.hasNext()) {
            if (location.startsWith("/")) {
                return this.getResourceByPath(location);
            }
            if (location.startsWith("classpath:")) {
                return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
            }
            try {
                URL url = new URL(location);
                return (Resource)(ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
            } catch (MalformedURLException var5) {
                return this.getResourceByPath(location);
            }
        }
        ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();
        resource = protocolResolver.resolve(location, this);
    } while(resource == null);
    return resource;
}
复制代码


在分析该方法,我们需要认识一下 ProtocolResolver,该类与 DefaultResourceLoader 密不可分。ProtocolResolver 翻译过来叫做"协议解析器", 允许用户自定义协议资源解决策略,作为 DefaultResourceLoader 的 SPI,它允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类。 在这个接口类中只有一个方法:


public interface ProtocolResolver {
    @Nullable
    Resource resolve(String var1, ResourceLoader var2);
}
复制代码


这个方法就是根据传入的 location 字符串,解析出对应的 Resource 资源。

在介绍 Resource 时,提到如果要实现自定义 Resource,我们只需要继承  AbstractResource 即可,但是有了 ProtocolResolver 后,我们不需要直接继承 DefaultResourceLoader,改为实现 ProtocolResolver 接口也可以实现自定义的 ResourceLoader。  


然后我们再来查看 getResource() 方法,首先通过 getProtocolResolvers()获取 ProtocolResolver 的集合,如果存在 ProtocolResolver,则直接用它来加载资源,并返回 Resource,否则调用如下逻辑:


  • 若 location 以/开头,则调用 getResourceByPath()构造 ClassPathContextResource  类型资源并返回;
  • 若 location 以 classpath:开头, 则构造 ClassPathResource 类型资源并返回,在构造该资源时,通过 getClassLoader()获取当前的 ClassLoader;
  • 构造 URL,尝试通过它进行资源定位,若没有抛出 MalformedURLException 异常,然后判断是否为 FileURL,如果是则构造 FileUrlResource 类型资源,否则构造 UrlResource。若在加载过程中抛出 MalformedURLException 异常,则 委派 getResourceByPath() 实现资源定位加载。


接下来看一个测试案例,代码如下:


@Test
public void useResourceLoader(){
    ResourceLoader resourceLoader = new DefaultResourceLoader();
    Resource resource = null;
    String location = "";
    //location以/开头
    location = "/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //location以classpath开头
    location = "classpath:application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //抛出MalformedURLException异常,进而执行getResourceByPath方法
    location = "target/classes/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //同上
    location = "F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //ResourceUtils.isFileURL(url)为true,返回FileUrlResource
    location = "file:/target/classes/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //ResourceUtils.isFileURL(url)为false,返回UrlResource
    location = "https://cn.bing.com/";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
}
复制代码


执行结果为:


class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource
class org.springframework.core.io.ClassPathResource
class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource
class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource
class org.springframework.core.io.FileUrlResource
class org.springframework.core.io.UrlResource
复制代码


FileSystemResourceLoader



在上一章节中,我们还提到了 FileSystemResource 类型,但是 DefaultResourceLoader 中并没有对此类型的处理,包括 getResourceByPath(String) 方法只是构建 ClassPathResource 类型。所以我们可以使用 FileSystemResourceLoader, 它继承 DefaultResourceLoader 且覆写了 getResourceByPath(String),使之从文件系统加载资源并以 FileSystemResource 类型返回,这样我们就可以得到想要的资源类型,如下:

protected Resource getResourceByPath(String path) {
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    return new FileSystemResourceLoader.FileSystemContextResource(path);
}
复制代码


FileSystemContextResource 为 FileSystemResourceLoader 的内部类,它继承 FileSystemResource。


private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
    public FileSystemContextResource(String path) {
        super(path);
    }
    public String getPathWithinContext() {
        return this.getPath();
    }
}
复制代码


在构造器中也是调用 FileSystemResource 的构造方法来构造 FileSystemContextResource 的。


除了对getResourceByPath(String)方法的重写之外,其他方法都是复用 DefaultResourceLoader 中的方法。此时将上面的示例稍作修改:


@Test
public void useResourceLoader(){
    ResourceLoader resourceLoader = new DefaultResourceLoader();
    Resource resource = null;
    String location = "";
    resourceLoader = new FileSystemResourceLoader();
    //抛出MalformedURLException异常,进而执行getResourceByPath方法
    location = "target/classes/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
    //同上
    location = "F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/application_context.xml";
    resource = resourceLoader.getResource(location);
    System.out.println(resource.getClass());
}
复制代码


执行结果为:


class org.springframework.core.io.FileSystemResourceLoader$FileSystemContextResource
class org.springframework.core.io.FileSystemResourceLoader$FileSystemContextResource
复制代码


ResourcePatternResolver


ResourceLoader 的 Resource getResource(String location) 每次根据 location 返回一个 Resource,当需要加载多个资源时,必须要多次调用 getResource()方法,这样很不合理,所以引申而来了 ResourcePatternResolver。该类是 ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例,其定义如下:


public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    Resource[] getResources(String var1) throws IOException;
}
复制代码


ResourcePatternResolver 在 ResourceLoader 的基础上增加了 getResources(String locationPattern),以支持根据路径匹配模式返回多个 Resource 实例,同时也新增了一种新的协议前缀 classpath*:,该协议前缀由其子类负责实现。


PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类(在后续构建应用上下文调试的过程中,会发现该类的使用),它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml)。


PathMatchingResourcePatternResolver 提供了三个构造方法,如下:


public PathMatchingResourcePatternResolver() {
    this.resourceLoader = new DefaultResourceLoader();
}
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
    Assert.notNull(resourceLoader, "ResourceLoader must not be null");
    this.resourceLoader = resourceLoader;
}
public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {
    this.resourceLoader = new DefaultResourceLoader(classLoader);
}
复制代码


PathMatchingResourcePatternResolver 在实例化的时候,可以指定一个 ResourceLoader,如果不指定的话,它会在内部构造一个 DefaultResourceLoader。


getResource()


该方法在当前类中的定义如下:


public Resource getResource(String location) {
    return this.getResourceLoader().getResource(location);
}
复制代码


getResource() 方法直接委托给相应的 ResourceLoader 来实现,所以如果我们在实例化 PathMatchingResourcePatternResolver 的时候,如果不知道 ResourceLoader ,那么在加载资源时,其实就是 DefaultResourceLoader 的过程。


getResources()

查看该方法的定义:


public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith("classpath*:")) {
        return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
    } else {
        int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;
        return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
    }
}
复制代码


处理逻辑作图如下:


image.png

下面就 findAllClassPathResources()做详细分析。


findAllClassPathResources

当 locationPattern 以 classpath*: 开头但是不包含通配符,则调用findAllClassPathResources() 方法加载资源。 该方法返回 classes 路径下和所有 jar 包中的相匹配的资源。


protected Resource[] findAllClassPathResources(String location) throws IOException {
    String path = location;
    if (location.startsWith("/")) {
        path = location.substring(1);
    }
    Set<Resource> result = this.doFindAllClassPathResources(path);
    if (logger.isTraceEnabled()) {
        logger.trace("Resolved classpath location [" + location + "] to resources " + result);
    }
    return (Resource[])result.toArray(new Resource[0]);
}
复制代码


真正执行加载的是在 doFindAllClassPathResources()方法,如下:


protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
    Set<Resource> result = new LinkedHashSet(16);
    ClassLoader cl = this.getClassLoader();
    Enumeration resourceUrls = cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path);
    while(resourceUrls.hasMoreElements()) {
        URL url = (URL)resourceUrls.nextElement();
        result.add(this.convertClassLoaderURL(url));
    }
    if ("".equals(path)) {
        this.addAllClassLoaderJarRoots(cl, result);
    }
    return result;
}
复制代码


doFindAllClassPathResources() 根据 ClassLoader 加载路径下的所有资源。 如果在构造 PathMatchingResourcePatternResolver 实例时未传入 resourceLoader 或 classLoader 参数,则默认调用 DefaultResourceLoader 的 classLoader(默认为Thread.currentThread().getContextClassLoader())。然后调用 classLoader 的 getResources()方法,只有当 getClassLoader()返回为 null 时才会 调用ClassLoader.getSystemResources(path)ClassLoader.getResources()如下:


public Enumeration<URL> getResources(String var1) throws IOException {
    Enumeration[] var2 = (Enumeration[])(new Enumeration[2]);
    if (this.parent != null) {
        var2[0] = this.parent.getResources(var1);
    } else {
        var2[0] = getBootstrapResources(var1);
    }
    var2[1] = this.findResources(var1);
    return new CompoundEnumeration(var2);
}
复制代码


如果当前父类加载器不为 null,则通过父类向上迭代获取资源,否则调用 getBootstrapResources()。 这一部分得到的返回结果倒是可以看懂,但是在执行 resourceUrls.nextElement()语句时,对于如何得到的 url 的值不明白,如图所示:


image.png


如果有大神知悉,可否指点一下。


若 path 为 空(“”)时,则调用 addAllClassLoaderJarRoots()方法。该方法主要是加载路径下得所有 jar 包,方法较长也没有什么实际意义就不贴出来了。

通过上面的分析,我们知道 findAllClassPathResources() 其实就是利用 ClassLoader 来加载指定路径下的资源,不管它是在 class 路径下还是在 jar 包中。如果我们传入的路径为空或者 /,则会调用 addAllClassLoaderJarRoots() 方法加载所有的 jar 包。


findPathMatchingResources

当 locationPattern 以 classpath*: 开头且当中包含了通配符,则调用该方法进行资源加载。如下:


protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    //获取根路径
    String rootDirPath = this.determineRootDir(locationPattern);
    String subPattern = locationPattern.substring(rootDirPath.length());
    //在根路径下扫描到的jar包或class路径
    Resource[] rootDirResources = this.getResources(rootDirPath);
    Set<Resource> result = new LinkedHashSet(16);
    Resource[] var6 = rootDirResources;
    int var7 = rootDirResources.length;
    for(int var8 = 0; var8 < var7; ++var8) {
        Resource rootDirResource = var6[var8];
        Resource rootDirResource = this.resolveRootDirResource(rootDirResource);
        URL rootDirUrl = ((Resource)rootDirResource).getURL();
        // bundle 资源类型
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL)ReflectionUtils.invokeMethod(equinoxResolveMethod, (Object)null, new Object[]{rootDirUrl});
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        // VFS 资源
        if (rootDirUrl.getProtocol().startsWith("vfs")) {
            result.addAll(PathMatchingResourcePatternResolver.VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, this.getPathMatcher()));
        } else if (!ResourceUtils.isJarURL(rootDirUrl) && !this.isJarResource((Resource)rootDirResource)) {
            result.addAll(this.doFindPathMatchingFileResources((Resource)rootDirResource, subPattern));
        } else {
            result.addAll(this.doFindPathMatchingJarResources((Resource)rootDirResource, rootDirUrl, subPattern));
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
    }
    return (Resource[])result.toArray(new Resource[0]);
}
复制代码


方法有点儿长,但是思路还是很清晰的,主要分两步:

  1. 确定目录,获取该目录下得所有资源
  2. 在所获得的所有资源中进行迭代匹配获取我们想要的资源。

在这个方法里面我们要关注两个方法,一个是 determineRootDir(),一个是 doFindPathMatchingFileResources()


determineRootDir()主要是用于确定根路径,如下:


protected String determineRootDir(String location) {
    int prefixEnd = location.indexOf(58) + 1;
    int rootDirEnd;
    for(rootDirEnd = location.length(); rootDirEnd > prefixEnd && this.getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd)); rootDirEnd = location.lastIndexOf(47, rootDirEnd - 2) + 1) {
    }
    if (rootDirEnd == 0) {
        rootDirEnd = prefixEnd;
    }
    return location.substring(0, rootDirEnd);
}
复制代码


该方法一定要给出一个确定的根目录。该根目录用于确定文件的匹配的起始点,将根目录位置的资源解析为 java.io.File 并将其传递到 retrieveMatchingFiles(),其余为知用于模式匹配,找出我们所需要的资源。


确定根路径如下:


原路径 根路径
classpath:test/cc/spring-*.xml classpath*:test/
classpath:test/aa/spring-.xml classpath*:test/aa/
file:F:/workspace/test/target/classes/*/beans.xml file:F:/workspace/test/target/classes/


确定根路径后,则调用 getResources() 方法获取该路径下得所有资源,然后迭代资源获取符合条件的资源。


下面给出一个案例,代码如下:


@Test
public void usePatternResolver() throws IOException {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = null;
    String location = "";
    //使用DefaultResourceLoader的情况下,改变location的内容
    location = "application_context.xml";
    resources = resolver.getResources(location);//调用DefaultResourceLoader.getResource()方法
    sout(resources);
    location = "classpath*:config/beans.xml";
    resources = resolver.getResources(location);//findAllClassPathResources
    sout(resources);
    location = "classpath*:*/beans.xml";
    resources = resolver.getResources(location);//findPathMatchingResources
    sout(resources);
    location = "file:F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/*/beans.xml";
    resources = resolver.getResources(location);//findPathMatchingResources
    sout(resources);
    location = "F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/application_context.xml";
    resources = resolver.getResources(location);//调用resourceLoader.getResource()方法
    sout(resources);
    //更新ClassLoader
    location = "F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/application_context.xml";
    resolver = new PathMatchingResourcePatternResolver(new FileSystemResourceLoader());
    resources = resolver.getResources(location);//调用FileSystemResourceLoader.getResource()方法
    sout(resources);
}
public void sout(Resource[] resources){
    for(Resource resource:resources){
        System.out.println(resource);
    }
}
复制代码


执行结果为:


class path resource [application_context.xml]
URL [file:/F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/config/beans.xml]
file [F:\workspace\Spmvc_Learn\spring_study\spring-chap1\target\classes\config\beans.xml]
file [F:\workspace\Spmvc_Learn\spring_study\spring-chap1\target\classes\config\beans.xml]
class path resource [F:/workspace/Spmvc_Learn/spring_study/spring-chap1/target/classes/application_context.xml]
file [F:\workspace\Spmvc_Learn\spring_study\spring-chap1\target\classes\application_context.xml]
复制代码


总结


经过两章节的讲述,关于 Spring 资源管理的过程已经分析完毕。下面简要总结下:

  • Spring 提供了 Resource 和 ResourceLoader 来统一抽象整个资源及其定位。使得资源与资源的定位有了一个更加清晰的界限,并且提供了合适的 Default 类,使得自定义实现更加方便和清晰。


  • AbstractResource 为 Resource 的默认实现类,它对 Resource 接口做了统一的实现,子类继承该类后只需要覆盖相应的方法即可,同时对于自定义的 Resource 我们也是继承该类。


  • DefaultResourceLoader 同样也是 ResourceLoader 的默认实现,在自定义 ResourceLoader 的时候我们除了可以继承该类外还可以实现 ProtocolResolver 接口来实现自定义资源加载协议。


  • DefaultResourceLoader 每次只能返回单一的资源,所以 Spring 针对该情况提供了另一个接口 ResourcePatternResolver,该接口提供了根据指定的 locationPattern 返回多个资源的策略。其子类 PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader,因为它既实现 了 Resource getResource(String location) 也实现了 Resource[] getResources(String locationPattern)



目录
相关文章
|
18天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
3月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
39 0
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
144 9
|
2月前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
38 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
3月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
46 4
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
79 0
|
3月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
151 3
|
2月前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
53 0
下一篇
DataWorks