Spring framework IOC resource

We often need to read external resources into applications, such as text files, properties files, picture files, etc. These resources may be located in different locations, such as file systems, resources under classpath, or resources on remote servers. Generally, we need to load resources of different types of paths through different API s, which has many inconveniences  

  Spring provides a series of Resource and ResourceLoader interfaces to solve the above problems. The API is very friendly and powerful

Resource is an abstraction of various resources such as file system resources, classpath resources, URL resources, etc

ResourceLoader provides a method to load resources. It automatically selects the corresponding Resource type through the Resource path prefix, shielding developers from the differences of using different Resource implementations

Resource related interfaces can be used independently of Spring. Different types of resources can be loaded in the following ways:

//Load different types of resources by specifying resource paths with different prefixes
//Load resources through file system absolute path
Resource resource = resourceLoader.getResource("file:D:/code/spring/src/main/resources/demo.xml");
//Load resources through a relative path relative to the root directory of the current project
Resource resource = resourceLoader.getResource("file:src/main/resources/demo.xml");
//Load resources under classpath
Resource resource = resourceLoader.getResource("classpath:demo.xml");
Resource resource = resourceLoader.getResource("classpath:com/example/spring/ResourceLoaderDemo.class");
//Load a blog resource on CSDN through https url  
Resource resource = resourceLoader.getResource("https://blog.csdn.net/wb_snail/article/details/108134550");
(tips: spring It not only provides the loading method of a single resource, but also provides the method of loading a group of resources by wildcards, which will be described below)

After you get the Resource, you can call Resource#getInputStream to get the Resource input stream and read its contents:

InputStream inputStream=resource.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while (true) {
    String line = reader.readLine();
    if (line == null)
        break; 
    System.out.println(line);
} 
reader.close();

The ResourceLoader instance can be obtained as follows:

1. Implement the ResourceLoaderAware interface, which is injected when the Spring container starts

2. During the startup of @ Autowired ResourceLoader resourceLoader(Spring container), some special objects (including ResourceLoader object) will be injected into BeanFactory  ), In particular, because they are not defined as beans, they are internal components of spring, which allows us to use them through @ Autowire)

3. Using ApplicationContext, ApplicationContext inherits from ResourceLoader. In fact, the ResourceLoader object obtained through the first two methods is ApplicationContext

4. Directly new DefaultResourceLoader (the default implementation class of resourceloader interface). By default, when ApplicationContext#getResource is used, the underlying implementation is DefaultResourceLoader

As mentioned above, the Resource interface abstracts various types of resources. The core methods are:

getInputStream(): inherits from the InputStreamSource interface and returns the input stream corresponding to the resource, which is used to read the resource

exists(): returns the identification of whether the resource exists

getFile(): if the resource exists in the file system, return the corresponding file object, otherwise throw FileNotFoundException (such as ByteArrayResource, which exists only in memory)

getURL(): returns the URL corresponding to the resource (Java. Net. URL). The URL is the resource locator. In the above example, "file:D:/demo.xml", "classpath:demo.xml"“ https://blog.csdn.net/wb_snail/article/details/108134550 "Are all String representations of URLs

Specific implementation classes include (the inheritance system of Resource is relatively large, and only some common Resource implementations are selected here):

InputStreamSource: the parent interface of the Resource. There is only one getInputStream() method

WritableResource: a writable resource whose method getOutputStream() can return the output stream of the resource

FileSystemResource: File system resource, which can be built through File object, File system absolute Path and Path object (such as Paths.get("D:/demo.xml") (tips: Spring will load them as FileSystemResource when processing class files under the package Path defined by @ ComponentScan)

ClassPathResource: resources under classpath (tips: when Spring processes @ PropertyResource and @ PropertyResources, the relevant configuration files will be loaded as ClassPathResource)

UrlResource: it refers to a java.net.URL object and can access any resource (file, https, ftp and other resources) that can be represented by URL

Servletcontextresource: Web application resource. The resource path is represented by the path relative to the web application root directory, such as new   ServletContextResource("/WEB-INF/demo.xml")

ByteArrayResource: a resource created from a binary array, such as new ByteArrayResource(new String("hello").getBytes(StandardCharsets.UTF_8))

Resources can represent almost any type of underlying resources. In addition to the various Resource types implemented by Spring, you can also implement your own resources, such as the resources in DB. You can register a ProtocolResolver with ResourceLoader, and then use ResourceLoader to load your resources in a way that is no different from other types of resources. The relevant source code is as follows

public class CustomResource {
	public static void main(String[] args) {
		DefaultResourceLoader resourceLoader=new DefaultResourceLoader();
		//Register custom ProtocolResolver
		resourceLoader.addProtocolResolver(new DbProtocolResolver());
		//Load the resources in db through the path prefixed with "db:"
		Resource dbResource=resourceLoader.getResource("db:dataSource_fileDB/table_file/column_content");
	}
}

//Custom Resource
public class DbResource extends AbstractResource {
	private final String path;
    ...
}

/**
 * Implement ProtocolResolver and return DBResource for the url prefixed with "db:"
 */
public class DbProtocolResolver implements ProtocolResolver {
	
	private static final String DB_URL_PREFIX="db:";
	
	@Nullable
	public Resource resolve(String location, ResourceLoader resourceLoader){
		if(location.startsWith(DB_URL_PREFIX)){
			return new DbResource(location.substring(DB_URL_PREFIX.length()));
		}
		return null;
	}
}

//The following is the source code of relevant parts in DefaultResourceLoader
public class DefaultResourceLoader implements ResourceLoader {

    //A set of ProtocolResolver instances
	private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);

	public DefaultResourceLoader() {
	}

    //Call this method to register your custom ProtocolResolver
	public void addProtocolResolver(ProtocolResolver resolver) {
		Assert.notNull(resolver, "ProtocolResolver must not be null");
		this.protocolResolvers.add(resolver);
	}

	@Override
	public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");

        //When loading resources, priority should be given to loading resources through your registered ProtocolResolver
		for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
			Resource resource = protocolResolver.resolve(location, this);
			if (resource != null) {
				return resource;
			}
		}

		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
		else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			try {
				// Try to parse the location as a URL...
				URL url = new URL(location);
				return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
			}
			catch (MalformedURLException ex) {
				// No URL -> resolve as resource path.
				return getResourceByPath(location);
			}
		}
	}
}

ResourceLoader only supports matching a single resource. Its extension interface ResourcePatternResolver supports loading all qualified resources in the form of wildcards, such as "classpath*:META-INF/spring.handlers", "classpath:META-INF/*.properties", "WEB-INF/*-context.xml", "file:D:/resources/*.properties"

Note the difference between "classpath:" and "classpath *:". The latter will also take all jar packages under the classpath as the search target, @ ComponentScan can scan @ components under the jar package. It is just by using this feature that Spring realizes SPI by searching the spring.handlers file

ResourcePatternResolver supports three wildcard characters:

*: matches any character in the resource path

?: matches a single character in the resource path

For example, "*. XML" can be matched to a.xml and ab.xml, while "?. XML" can only be matched to a.xml

**: match any level. For example, "mapper/**/*Mapper.xml" can match mapper/RoleMapper.xml, mapper/order/OrderMapper.xml, mapper/order/goods/GoodsMapper.xml

In the Spring Framework, the only effective implementation of ResourcePatternResolver is PathMatchingResourcePatternResolver. ApplicationContext also inherits from ResourcePatternResolver, but by default, its getResources method will be delegated to PathMatchingResourcePatternResolver for execution

ResourcePatternResolver can be used in two ways  :

//Load using PathMatchingResourcePatternResolver
ResourcePatternResolver resourcePatternResolver=new PathMatchingResourcePatternResolver();
Resource[] resources=resourcePatternResolver.getResources("classpath*:META-INF/spring.handlers");

//Load using ApplicationContext
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
applicationContext.refresh();
Resource[] resources=applicationContext.getResources("classpath*:META-INF/spring.handlers");

Posted on Mon, 29 Nov 2021 08:12:09 -0500 by Fabrizzio PHP