-
2022-04-11 23:00:25
我在读取邮件模板的时候,本地测试使用ClassPathResource都可以正常读取,但打包成jar包传到服务器上就无法获取了,报错信息是:class path resource [xxxx] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:xxxx.jar!/BOOT-INF/classes!xxxx,话不多说,先看正确的获取方法:使用PathMatchingResourcePatternResolver。
String txt = ""; ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources("templates/layout/email.html"); Resource resource = resources[0]; //获得文件流,因为在jar文件中,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流 InputStream stream = resource.getInputStream(); StringBuilder buffer = new StringBuilder(); byte[] bytes = new byte[1024]; try { for (int n; (n = stream.read(bytes)) != -1; ) { buffer.append(new String(bytes, 0, n)); } } catch (IOException e) { e.printStackTrace(); } txt = buffer.toString();
然后,想知道更多的咱们就继续看看是怎么回事,如果只是为了解决问题,那就可以忽略下面的内容了。
为了老夫好奇的心,我们继续探索下去,到底是怎么回事?我们先看看之前的代码:
String txt = ""; Resource resource = new ClassPathResource("templates/layout/email.html"); txt = fileUtil.readfile(resource.getFile().getPath());
其实这是一个jar包发布的大坑,相信很多小伙伴遇到了读取文件的问题,其实使用getFile()的时候的坑,为了弄明白到底是咋回事,我进行了跟踪,结果返回的是一个Jar协议地址:jar:file:/xxx/xx.jar!/xxxx。
然后继续跟踪到org.springframework.util.ResourceUtils#getFile(java.net.URL, java.lang.String)中,有如下的判断:
public static File getFile(URL resourceUrl, String description) throws FileNotFoundException { Assert.notNull(resourceUrl, "Resource URL must not be null"); if (!"file".equals(resourceUrl.getProtocol())) { throw new FileNotFoundException(description + " cannot be resolved to absolute file path because it does not reside in the file system: " + resourceUrl); } else { try { return new File(toURI(resourceUrl).getSchemeSpecificPart()); } catch (URISyntaxException var3) { return new File(resourceUrl.getFile()); } } }
因为resourceUrl.getProtocol()不是file,而是 jar,这样就抛出了一个FileNotFoundException异常。
ResouceUtils.getFile()是专门用来加载非压缩和Jar包文件类型的资源,所以它根本不会去尝试加载Jar中的文件,要想加载Jar中的文件,只要用可以读取jar中文件的方式加载即可,比如 xx.class.getClassLoader().getResouceAsStream()这种以流的形式读取文件的方式,所以使用读取文件流就可以拿到了。
解决方案一:在jar包中使用文件流读取。
ExcelWriter excelWriter = EasyExcel.write(httpResponse.getOutputStream()) .withTemplate(new ClassPathResource("templates/excel/b2cSaleOrder/OrderListExportTemplate.xlsx").getInputStream()) .build(); WriteSheet writeSheetOne = EasyExcel.writerSheet("Sheet1").build(); excelWriter.fill(listOne, writeSheetOne); excelWriter.finish();
解决方案二:通过指定类所在的目录来指定模板所在根路径
String fontPath = new ClassPathResource("/fonts/", FontUtil.class.getClassLoader()).getFile().getPath(); 或 String templatePath = new ClassPathResource("/templates/excel/b2cSaleOrder/OrderListExportTemplate.xlsx", FontUtil.class.getClassLoader()).getFile().getPath();
参考文章1:SpringBoot项目打包成jar后读取文件的大坑,使用ClassPathResource获取classpath下文件失败 - Posts - 任霏的博客
参考文章2:打包成jar后读取文件的大坑:使用ClassPathResource获取classpath下文件失败_赶路人儿的博客-CSDN博客
参考文章3:freemaker模板位置ClassTemplateLoader的绝对路径相对路径设置方法_机械手学Java的博客-CSDN博客_freemarker 模板路径
更多相关内容 -
解析Resource格式
2016-02-03 15:25:59Android中解析编译之后的 -
vue-resource.js
2016-04-20 12:47:03vue-resource.js,其他地方还没有找到可以下载的 -
@Resource详解
2021-03-31 16:10:15@Resource详解 阅读本文之前希望读者最好已经对整个Bean的大体Spring执行顺序已经有了一定的了解。 示例 定义一个接口,表示水果类,只包含一个方法代表售卖。 public interface Fruit { void sell(); } 有两个...@Resource详解
阅读本文之前希望读者最好已经对整个Bean的大体Spring执行顺序已经有了一定的了解。
示例
定义一个接口,表示水果类,只包含一个方法代表售卖。
public interface Fruit { void sell(); }
有两个具体实现类,Apple🍎和Banana🍌。
@Service public class Apple implements Fruit { public Apple() { System.out.println("Apple......"); } @Override public void sell() { System.out.println("苹果2元一斤"); } }
@Service public class Banana implements Fruit { public Banana() { System.out.println("Banana..."); } @Override public void sell() { System.out.println("香蕉3元一串"); } }
具体的商店类来注入Fruit接口。
@Component public class Store { @Resource private Fruit fruit; public void getFruit() { fruit.sell(); } }
测试方法:
public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); context.getBean(Store.class).getFruit(); // 优雅关闭,SpringWEB中已有相关实现 context.registerShutdownHook(); } }
实际执行结果:
这里可以清楚的看到报了
NoUniqueBeanDefinitionException
异常,说是希望单个Bean的匹配,却找到了多个。下面就来具体的讲下为什么。
首先,@Resource中没有设置任何属性值,统统采用的是默认的值。
按照Spring Bean的加载顺序,Store Bean创建的时候,BeanFactory中已经创建了Apple和Banana Bean。
1. newInstance
第一步就是先创建出Store对象。
2. 解析类中的字段
Spring在实例化对象后,会调用
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
方法对BeanDefinition进行完善,主要为了后续的BeanPostProcessor处理器在注入对应的字段时能获取到需要注入的类的相关信息。而对应需要注入的一个个类而言,就是使用ResourceElement对象来进行保存,相关重要的构造函数如下:
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) { super(member, pd); Resource resource = ae.getAnnotation(Resource.class); // 获取@Resource的name属性 String resourceName = resource.name(); // 获取@Resource的type属性 Class<?> resourceType = resource.type(); this.isDefaultName = !StringUtils.hasLength(resourceName); if (this.isDefaultName) { // 如果没有设置@Resource name属性就用字段名称作为bean name resourceName = this.member.getName(); // 如果member是setter方法,则取setXXX的XXX部分为bean name if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { resourceName = Introspector.decapitalize(resourceName.substring(3)); } } else if (embeddedValueResolver != null) { // 如果设置了@Resource name的属性,则使用EmbeddedValueResolver对象先做一次SpringEL解析得到真正的bean name resourceName = embeddedValueResolver.resolveStringValue(resourceName); } if (Object.class != resourceType) { // 确保字段或setter方法类型与resourceType一致 checkResourceType(resourceType); } else { // No resource type specified... check field/method. resourceType = getResourceType(); } this.name = (resourceName != null ? resourceName : ""); this.lookupType = resourceType; String lookupValue = resource.lookup(); // 如果使用jndi查找名字 this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName()); Lazy lazy = ae.getAnnotation(Lazy.class); // 是否延迟注入 this.lazyLookup = (lazy != null && lazy.value()); }
当name属性没有被设置时,就会执行下面的分支,根据是方法注入还是属性注入,分别设置为方法名称set后面的字符串或字段名称。
if (this.isDefaultName) { // 如果没有设置@Resource name属性就用字段名称作为bean name resourceName = this.member.getName(); // 如果member是setter方法,则取setXXX的XXX部分为bean name if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { resourceName = Introspector.decapitalize(resourceName.substring(3)); } }
下面是具体的ResourceElement类对象中的各属性。
然后可以测试下在@Resource注解中加入name属性;
@Resource(name = “apple”)
得到的是下面的对象。
这里可以看出name属性会有明显的不同。
这里name属性和lookupType属性其实可以对应于@Resource中的name和type属性。
3. populateBean
第三步就是类属性的注入。
执行到对应的处理器(@Resource是通过
CommonAnnotationBeanPostProcessor
处理器进行处理的)进行属性注入的时候,autowireResource
就会用来获取对应属性需要注入的对象。protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { // 自动装配的对象 Object resource; // 自动装配的名字 Set<String> autowiredBeanNames; // 依赖的属性名 String name = element.name; if (factory instanceof AutowireCapableBeanFactory) { AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory; DependencyDescriptor descriptor = element.getDependencyDescriptor(); // 判断是否设置了name属性的值 if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet<>(); resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } else { resource = beanFactory.resolveBeanByName(name, descriptor); autowiredBeanNames = Collections.singleton(name); } } else { resource = factory.getBean(name, element.lookupType); autowiredBeanNames = Collections.singleton(name); } if (factory instanceof ConfigurableBeanFactory) { ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory; for (String autowiredBeanName : autowiredBeanNames) { if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) { //注册依赖关系 beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName); } } } return resource; }
这里的element对象就是我们之前解析保存的ResourceElement。
然后最重要的,上面强调过的name属性在不同Resource注解中解析出来的ResourceElement是会不同的,这里就会有不同的处理方式。
当
isDefaultName
为false时,说明name属性被设置了值,此时执行的如下逻辑;resource = beanFactory.resolveBeanByName(name, descriptor); autowiredBeanNames = Collections.singleton(name);
resolveBeanByName
方法就是通过设置的名称来进行解析对应的Bean对象。接着就能看到经常使用到的getBean方法,从BeanFactory中拿到Bean对象。public Object resolveBeanByName(String name, DependencyDescriptor descriptor) { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { // 获取bean return getBean(name, descriptor.getDependencyType()); } finally { // 为目标工厂方法提供依赖描述符 ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
当Resource注解中name没有设置值,即使用的是字段名称作为beanName,执行的就是
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
resolveDependency方法中实际工作的方法就是doResolveDependency:
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { //设置新得当前切入点对象,得到旧的当前切入点对象 InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); // ...省略 // 查找相关所有类型匹配的bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); //如果没有候选bean对象 if (matchingBeans.isEmpty()) { //如果descriptor需要注入 if (isRequired(descriptor)) { //抛出NoSuchBeanDefinitionException或BeanNotOfRequiredTypeException以解决不可 解决的依赖关系 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } //返回null,表示么有找到候选Bean对象 return null; } //定义用于存储唯一的候选Bean名变量 String autowiredBeanName; //定义用于存储唯一的候选Bean对象变量 Object instanceCandidate; //如果候选Bean对象Map不止有一个 if (matchingBeans.size() > 1) { //确定candidates中可以自动注入的最佳候选Bean名称 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { // 如果有多个匹配结果,抛出异常 if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { //让descriptor尝试选择其中一个实例,默认实现是抛出NoUniqueBeanDefinitionException. return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { //获取machingBeans唯一的元素 Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } // ...省略 //返回最佳候选Bean对象【result】 return result; } finally { //设置上一个切入点对象 ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
doResolveDependency方法中会获取所有和指定type相同的所有bean集合,当发现bean集合中的元素个数超过1时就抛出了上面出现的错误了,如果只有一个那自然而然就是拿那个bean来返回。
这里抛出的异常其实不是直接抛出的,而是调用的DependencyDescriptor中的resolveNotUnique方法,而该方法默认实现就是会直接抛出一个NoUniqueBeanDefinitionException异常,这也是Spring留的一个可扩展的地方。
我们可以通过继承DependencyDescriptor,然后重写该方法来自定义选择一个最优的bean(比如说选择第一个),然后在BeanPostProcessor中选择我们的子类作为实现,这样在@Resource不指定任何属性的情况下,有多个实现类的bean也不会抛出异常。
拓展
- 当只指定type属性时;
@Resource(type = Apple.class)
设置type属性后,isDefaultName的值还是为true,所以执行的还是resolveDependency方法。但是由于添加了类型的限制,所以也就不会匹配到多个Bean,而产生异常。
- 既指定了name属性,又指定了type类型,但是是不同的类;
@Resource(name = “banana”, type = Apple.class)
name属性被设置为banana,isDefaultName变为false,执行resolveBeanByName方法。
但是由于找不到对应beanName为banana,但是类型又为Apple.class的bean,还是会抛出异常。
总结
多种@Resource不同使用情况所执行方法如下所示;
-
当@Resource不设置任何值时,isDefaultName会为true,当对应字段名称的bean或者BeanDefinition已存在时会走byName的形式,否则走byType的形式;
-
只指定了type属性时,只有当对应的名称不存在对应的bean或BeanDefinition,才会通过byType找到唯一的一个类型匹配的bean;
-
只指定了name属性,会执行getBean方法,根据指定的name来获取bean;
-
既指定了name属性,又指定了type属性,会先根据那么查找对应的bean,然后进行type类型比较。
如何解决本文最上面出现的问题?
- @Resource中指定name或着type;
- @Qualifier指定bean名称;
- 将字段名称修改为指定的bean名称;
- 直接修改对象类型。
只推荐第一种方法。
-
@Autowired与@Resource区别
2022-04-05 16:06:15一、前言 Spring Bean覆盖配置 二、@Autowired 注解处理器 装配方式 注解属性 作用范围 1. 成员变量 2. 构造器 3. 方法 ...三、@Resource ...四、@Autowired与@Resource对比 二者对比 @Autowired装配流程 @Resou文章目录
一、前言
@Autowired和@Resource都是用来自动装配bean的。
- @Resource是JSR-250提供的,它是Java标准,绝大部分框架都支持。
- @Autowired功能非常强大,但只适用于Spring框架,如果换成了JFinal等其他框架,功能就会失效。
Spring Bean覆盖配置
spring: main: allow-bean-definition-overriding: true
allow-bean-definition-overriding属性用于配置出现相同名称bean的情况如何处理:
- 值为false时(默认值为false),如果出现相同名称的bean,直接抛异常。
- 值为true时,表示支持相同名称的bean覆盖,后定义的bean会覆盖之前定义的相同名称的bean。
下文会提到按类型装配,那么什么是同一类型呢?
- 父类及其子类,都属于父类这一类型。
- 接口及其实现类,都属于接口这一类型。
二、@Autowired
@Autowired是Spring提供的注解,用于自动装配。
注解处理器
AutowiredAnnotationBeanPostProcessor类是Autowired注解的注解处理器。
关于注解处理器可参考文章:https://blog.csdn.net/JokerLJG/article/details/123548694
装配方式
- 按类型装配(默认使用的装配方式)。
- 按名称装配(结合@Qualifier注解使用)。
注解属性
- required:默认值true。值为true时,表示必须注入,如bean不存在则会报错;值为false时,表示bean存在就注入,不存在则不注入。
作用范围
@Autowired的作用范围:成员变量、构造器、方法、参数、注解。
1. 成员变量
@Service public class UserService { @Autowired private IUser user; }
使用最多的方式。
2. 构造器
@Service public class UserService { private IUser user; @Autowired public UserService(IUser user) { this.user = user; } }
构造器上使用Autowired注解,实际上还是使用了成员变量装配的方式,并非构造器装配。
3. 方法
@Service public class UserService { @Autowired public void test(IUser user) { user.test(); } }
Spring会在项目启动的过程中,自动调用一次加了@Autowired注解的方法,我们可以在该方法做一些初始化的工作。
4. 参数
在构造器的入参上加Autowired注解
@Service public class UserService { private IUser user; public UserService(@Autowired IUser user) { this.user = user; System.out.println("user:" + user); } }
在非静态方法的入参上加Autowired注解
@Service public class UserService { public void test(@Autowired IUser user) { user.test(); } }
5. 注解
略。
使用技巧
同一类型多个bean
当按类型装配时,如果该类型的bean不止一个时,会直接报错。举例说明:
接口:
public interface IUser { void test(); }
实现类1:
@Service public class User1 implements IUser{ @Override public void test() { } }
实现类2:
@Service public class User2 implements IUser{ @Override public void test() { } }
自动装配:
@Service public class UserService { @Autowired private IUser user; }
启动时的错误信息:
Field userService in com.joker.controller.UserController required a single bean, but 2 were found: - userServiceImpl1: defined in file [D:\work\my\springboot\target\classes\com\joker\controller\UserServiceImpl1.class] - userServiceImpl2: defined in file [D:\work\my\springboot\target\classes\com\joker\controller\UserServiceImpl2.class]
@Primary的使用
@Primary注解可以解决上述问题(按类型装配时,如果该类型的bean不止一个时,会报错)。
当我们使用自动配置的方式装配Bean时,如果这个Bean有多个候选者,假如其中一个候选者具有@Primary注解修饰,该候选者会被选中,作为自动装配的bean。
在上面代码不变的情况下,只需在User1或User2上加@Primary注解,此时@Autowired自动装配会成功,并且自动装配的是加了@Primary注解的这个类对应的bean。
User1类加@Primary注解
@Service @Primary public class User1 implements IUser{ @Override public void test() { } }
@Qualifier的使用
通过@Autowired和@Qualifier的结合使用可以按名称装配。
@Service public class UserService { @Autowired @Qualifier("user1") private IUser user; }
自动装配名称为user1的bean(注意:bean的类型也必须要满足为IUser类型)。
装配多个实例
我们一般使用的都是用@Autowired自动装配单个实例,但其实它也可以用来装配多个实例。可以通过List、Set、Map来装配多个实例,如下:
@Service public class UserService { @Autowired private List<IUser> userList; @Autowired private Set<IUser> userSet; @Autowired private Map<String, IUser> userMap; }
上面的装配方式会吧IUser类型的多个实例bean都装配的List、Set、Map中。
@Autowired装配未生效
下面列举常见@Autowired装配未生效的情况:
-
@Autowired所在类未加@Controller、@Service、@Component、@Repository等注解,或者或者一些其它情况(如直接new对象的到实例)。这些情况会导致该类的bean并没有交给spring容器去管理,spring就无法完成自动装配的功能。
public class UserService { @Autowired private IUser user; public void test() { user.say(); } }
-
注解未被@ComponentScan扫描到。
三、@Resource
@Resource是JDK自带的注解,用于自动装配。
注解处理器
CommonAnnotationBeanPostProcessor类是Resource的注解处理器。
装配方式
@Resource默认按照名称自动注入。
-
既没指定name,也没指定type,自动按照名称装配(当注解写在字段上时,默认取字段名,当注解写在setter方法上时,默认取属性名进行装配。);如果没有匹配,则退而按照类型装配,找不到则抛出异常。
如果没有指定 name 属性,
-
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
-
如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
-
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
注解属性
Resource注解的主要属性:
- name:指定需注入的bean的名称
- type: 指定需注入的bean的类型
作用范围
@Resource的作用范围:类、成员变量、方法。
1. 成员变量
@Service public class UserService { @Resource private IUser user; }
2. 方法
@Service public class UserService { @Resource public void test(IUser user) { user.test(); } }
3. 类
略。
四、@Autowired与@Resource对比
二者对比
@Autowired @Resource Spring定义的注解 JSR-250定义的注解 默认按类型自动装配 默认按名称自动装配 一个参数:required(默认true),表示是否必须注入 七个参数:最重要的两个参数是name、type 默认按类型自动装配
如果要按名称自动装配,需要使用@Qualifier一起配合默认按名称自动装配
如果指定了name,则按名称自动装配;如果指定了type,则按类型自动装配作用范围:构造器、方法、参数、成员变量、注解 作用范围:类、成员变量、方法 @Autowired装配流程
@Resource装配流程
-
Spring ClassPathResource详解
2019-08-07 17:43:47org.springframework.core.io.ClassPathResource位于Spring核心core下,用以表达类路径下的资源。org.springframework.core.io.ClassPathResource位于Spring核心core下,用以表达类路径下的资源。
首先简要说明一下什么是classpath,顾名思义,就是存放*.class类文件的路径,或者说ClassLoader加载类时为找到 *.class文件的路径。我们以一个WEB项目为例,发布后的目录结构大致如下:然后以Tomcat为例,看一下WEB项目类加载时候的目录,参考 Tomcat Class Loader How-To 中的说明:
WebappX — A class loader is created for each web application that is deployed in a single Tomcat instance. All unpacked classes and resources in the /WEB-INF/classes directory of your web application, plus classes and resources in JAR files under the /WEB-INF/lib directory of your web application, are made visible to this web application, but not to other ones.
因此,对于部署在Tomcat上的WEB应用来说,/WEB-INF/classes和/WEB-INF/lib目录就是我们所指的classpath。
ClassPathResource是org.springframework.core.io.Resource接口的实现类。可以使用ClassLoader或Class类加载资源。支持转换为java.io.File对象(在Jar文件中的资源除外)。其继承实现关系图如下:
ClasspathResource类的属性变量和构造方法如下:
private final String path; @Nullable private ClassLoader classLoader;// 通过ClassLoader加载资源文件 @Nullable private Class<?> clazz; // 通过Class类加载资源文件 // 通过类路径创建resource public ClassPathResource(String path){...} // 通过类路径和给定的ClassLoader创建resource public ClassPathResource(String path, @Nullable ClassLoader classLoader){...} // 通过类路径和给定的Class类创建resource public ClassPathResource(String path, @Nullable Class<?> clazz){...} // 通过类路径和给定的ClassLoader或Class创建resource protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz){...}
在类继承关系中,每个类定义的方法如下:
Resource AbstractResource AbstractFileResolvingResource ClassPathResource 对底层资源的抽象描述
比如文件或类路径资源对资源接口描述的基础实现
预先实现特定的行为将URL解析为文件资源引用的抽象基类,尤其对JBOOS的vfs文件协议的支持 类路径资源的Resource实现 boolean exists()
判断该资源是否存在public boolean exists()
实现父接口方法,检查资源(文件或目录)File对象或资源(文件)InputStream对象是否打开public boolean exists()
重写父类方法,getURL后如果URL的文件协议是file:
、vfsfile:
、vfs
则返回getFile().exists()
直接判断该文件是否存在,如果非上述文件协议则尝试判断是否网络资源,通过HTTP请求看是否返回HTTP Status-Code=200,如果扔非上述,则尝试getInputStream().close()
看文件流是否可打开public boolean exists()
重写父类方法,判断是否能获取到该资源的URL对象boolean isReadable()
是否可通过InputStreamSource.getInputStream()读取,这里注意返回true仍然可能读取失败,但返回false一定是不能读取默认返回exists()
public boolean isReadable()
实现父接口方法,该方法在当资源存在的情况下始终返回true,返回值与父接口方法中定义的默认返回值一致,即返回exists()
public boolean isReadable()
重写父类方法,getURL后如果URL的文件协议是file:
、vfsfile:
、vfs
则getFile得到该资源File对象,判断该File资源是否非目录且canRead可读,如果非上述文件协议则尝试调用网络资源,判断是否可成功调用且返回内容长度大于0,如果扔非上述,则尝试getInputStream().close()
看文件流是否可打开boolean isOpen()
表明该资源是否有打开的stream流,如果返回true则InputStream无法多次读取,且读完之后关闭流以防止内存泄露默认返回false
public boolean isOpen()
实现父接口方法,与接口方法中定义的默认返回值一致,即始终返回false
boolean isFile()
判断该文件是否是系统文件中的文件,true值表示(但不保证)可以成功调用getFile()方法默认返回false
public boolean isFile()
实现父接口方法,与接口方法中定义的默认返回值一致,即始终返回false
public boolean isFile()
重写父类方法,getURL后如果url为vfs开头协议(vfs/vfsfile)则交给VfsResourceDelegate
类判断,如果url为file协议则返回trueprotected boolean isFile(URI uri)
重载isFile方法,根据指定的URI判断该资源是否是一个文件引用,如果URI的scheme以vfs开头则交给VfsResourceDelegate
类判断,否则判断如果URI的scheme等于file
则返回trueURL getURL()
返回该资源对应的URLpublic URL getURL()
实现父接口方法,这里假设资源不能解析为URL,直接返回了FileNotFoundException
异常public URL getURL()
重写父类方法,根据类路径参数获取该资源的URL对象URI getURI()
返回该资源对应的URIpublic URI getURI()
实现父接口方法,基于getURL返回的URL构建一个URIFile getFile()
返回该资源的File对象public File getFile()
实现父接口方法,这里假设资源无法解析为文件绝对路径,直接返回了FileNotFoundException
异常public File getFile()
重写父类方法,父类直接返回FileNotFoundException
异常,在这里通过getURL获取到URL对象(具体URL由其子类确定,比如ClasspathResource重写了getURL通过类加载器获取到了类路径资源的URL),如果url为vfs开头协议(vfs/vfsfile)则交给VfsResourceDelegate
类获取File对象,否则通过得到url获取File对象protected File getFile(URI uri)
重载getFile方法,根据指定的URI获取资源File对象,如果URI的scheme以vfs开头则交给VfsResourceDelegate
类获取ReadableByteChannel readableChannel() 默认返回Channels.newChannel(getInputStream())
public ReadableByteChannel readableChannel()
实现父接口方法,与父接口的默认返回值相同public ReadableByteChannel readableChannel()
根据指定的URI调用FileChannel.open
返回一个ReadableByteChannel
对象long contentLength()
返回该资源内容的长度public long contentLength()
根据getInputStream()
返回的InputStream,读取并计算资源的内容长度public long contentLength()
根据getURL获得URL对象,如果是文件(file/vfsfile/vfs)URL返回文件长度,否则尝试网络连接获取资源并返回长度long lastModified()
返回该资源最后一次修改的时间戳public long lastModified()
获取并返回该资源文件的时间戳public long lastModified()
根据getURL获取URL对象,如果是文件(file/vfsfile/vfs)或归档文件(jar/war/zip/vfszip/wsjar)则获取文件的最后修改时间戳,否则获取网络连接并获取最后修改时间戳Resource createRelative(String relativePath)
根据相对于该资源的相对路径,创建一个Resource资源,比如classpath资源目录conf下有A.xml和B.xml,Resource a = new ClassPathResource("conf/A.xml");
那么在创建b资源的时候就可以以a为参照Resource b = a.createRelative("B.xml");
public Resource createRelative(String relativePath)
实现父接口方法,这里假设改相对资源未被创建,直接返回了FileNotFoundException
异常public Resource createRelative(String relativePath)
重写父类方法,根据参照资源的类路径得到relativePath参数的真实classpath路径,并创建Resource资源对象String getFilename()
返回该资源的文件名,通常是路径的最后一部分,比如:myfile.txtpublic String getFilename()
实现父接口方法,这里假设该资源无文件名,直接返回了null
public String getFilename()
重写父类方法,根据classpath截取后面的文件名并返回String getDescription()
返回该资源的描述,用于该资源在处理时的错误输出public String getDescription()
重写父类方法,返回格式如class path resource [...]
的内容protected File getFileForLastModifiedCheck()
获取用于时间戳检查的文件,这里默认返回了getFile()protected File getFileForLastModifiedCheck()
重写父类方法,扩展了父类方法,在getFile之前先判断url协议是否为jar/war/zip/vfszip/wsjar
,如果是则获取最外层的文件URL对应的File对象,比如嵌套在war中的jar文件,则返回war文件File对象。这里判断vfs开头协议扔交给VfsResourceDelegate
类来处理public boolean equals(Object other)
重写Object的equals方法,用于比较两个Resource的Description是否相同public boolean equals(Object other)
重写Object方法,比较类路径的值是否相同public int hashCode()
重写Object的hashCode方法,用于获取Resource的Description值的hashCode值public int hashCode()
重写Object方法,获取类路径classpath的hashCode值public String toString()
重写Object的toString方法,返回Resource的Description信息public final String getPath()
返回该资源的classpath,构造函数的path参数经过规范化处理的结果public final ClassLoader getClassLoader()
如果指定了Class,则通过该Class获取ClassLoader,否则返回属性变量的ClassLoader参数public InputStream getInputStream()
Resource继承了InputStreamSource接口,在ClassPathResource中得到了具体的实现,根据资源路径得到文件流
在AbstractFileResolvingResource类中由于增加了对JBOOS的vfs文件协议的支持,因此包含了一个`VfsResourceDelegate`内部类用于获取`VfsResource`类型资源对象,该资源对象同样继承自`AbstractResource`抽象类,并针对vfs文件的特点对方法进行了重写。如下:/** * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. */ private static class VfsResourceDelegate { public static Resource getResource(URL url) throws IOException { return new VfsResource(VfsUtils.getRoot(url)); } public static Resource getResource(URI uri) throws IOException { return new VfsResource(VfsUtils.getRoot(uri)); } }
ClassPathResource的使用:
Resource resource = new ClassPathResource("conf/custom-beans.xml");
参数path应在类路径下能够被ClassLoader所加载。
获取到了Resource对象也就等于获取到了该资源文件,后面可以根据方法的定义对文件进行相关操作。
System.out.println(resource.getURL()); System.out.println(resource.getFilename()); System.out.println(resource.getFile().getPath()); // ... ....
-
maven中resource配置详解
2021-11-28 01:15:22maven maven中resource配置 maven中resource资源配置 -
1、spring之Resource加载
2019-04-10 20:37:38也就是说,不管什么格式的文件,也不管文件在哪里,到Spring 底层,都只有一个访问接口,Resource。 1.1 类结构图 1.2 类和接口分析 1、可以看到有四个比较重要的接口 InputStreamSource、Resource、... -
Java读取jar包中的resource资源文件
2022-03-16 22:24:07在Java项目中,需要读取resource资源目录下的文件,以及遍历指定资源目录下的所有文件,并且在读取文件时保留文件相对路径。 问题 在IDEA中运行时,可以获取并遍历指定资源,但是将Java项目打成jar包运行后,就... -
Java之Resource接口
2021-03-21 08:48:47接口简介JDK中提供了java.net.URL这个类来用于获取不同种类的资源(根据不同前缀的url可以获取不同种类的资源)。...Resource接口spring中的org.springframework.core.io.Resource接口代表着物理存在... -
Error Rule can only have one resource source (provided resource and test + include + exclude)
2022-02-26 23:23:21Error: Rule can only have one resource source (provided resource and test + include + exclude) 问题:Error: Rule can only have one resource source (provided resource and test + include + exclude) ... -
Autowired注解与Resource注解的区别
2021-09-09 19:35:25两者的用法 其实这两个注解的作用都一样,都是在做bean的注入,在使用过程中,两个注解有时候可以替换... @Resource注解有两个重要的属性,分别是name和type,如果name属性有值,则使用byName的自动注入策略,将值作为需要 -
Spring中@Autowired和@Resource的区别
2021-10-19 20:13:48@Resource 在语义上被定义为通过其唯一的名称来标识特定的目标组件,其中声明的类型与匹配过程无关。 如果没有明确指定名称,则默认名称是从字段名称或设置方法(get、set方法)派生的。 如果用在字段上,则采用... -
@Autowired 与@Resource的区别(详细)
2018-06-10 19:44:00这个时候@AutoWire没有@Resource好用,因为@Resource可以根据名字来搜索,是这样写的@Resource("userService")。这个@Autowired @Qualifie("userService") 也可以用名字啊,为什么不用呢,原因很简单,这个有点长,... -
java的getResource方法
2020-10-07 12:12:16getResource是Class类中的一个方法 作用呢就是配置文件的读取 首先我们得知道,在new一个对象的时候会在java堆区中生成一个代表这个类的java.lang.Class对象,(不能主动创建,只能获取)作为这个类的各种数据的访问... -
@Autowired和@Resource区别
2022-05-12 20:45:40@Autowired和@Resource区别 1.提供方不同 @Autowired 是Spring提供的,@Resource 是J2EE提供的。 2.装配时默认类型不同 @Autowired只按type装配,@Resource默认是按name装配。 3、使用区别 (1)@Autowired与@... -
Spring中Resource接口详解
2018-02-22 10:49:15createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源。 getFilename... -
一文弄懂Spring源码之@Resource注解
2021-12-29 13:32:49一.@Resource注解简单介绍 @Resource注解标注的属性默认按照ByName进行注入,由J2EE提供 如果我们想按照ByType注入,代码要这样写: public class LaController { //按类型注入 @Resource(type=LaService.... -
resource failed to call close
2021-05-26 11:27:30在运行Android项目时,日志中会出现A resource failed to call close告警信息: 2021-05-26 11:26:52.564 11386-11401/com.xxx.example W/System: A resource failed to call close. 这是由于文件输入和输出流在... -
@Resource注解用法
2021-08-20 15:58:03@Resource注解和@Autowired注解一样,都是为了装配Bean,但是两者在使用上又有少许区别 @Autowired是默认按照类型装配Bean,当需要用名称装配时,可以在@Autowired后面使用@Qualifier注解指定name属性,来告知... -
Android resource compilation failed
2022-04-11 22:43:20> A failure occurred while executing com.android.build.gradle.internal.res.ResourceCompilerRunnable > Resource compilation failed. Check logs for details. * Try: > Run with --info or --debug option... -
Unity Resource文件基础操作(一)
2022-04-09 23:09:14Unity Resource文件的基本概念 本篇文章主要讲解Resource文件在Unity场景中的使用以及他的基本概念。 文章目录 Unity Resource文件的基本概念 前言 一、Resource文件是什么鬼? 二、Resource常用方式 1.Resource.... -
彻底搞懂Class.getResource和ClassLoader.getResource的区别和底层原理
2018-09-14 18:18:19只不过Class.getResource是先调用Class 的 getResource 方法,在这个getResource 方法中,再去调用ClassLoader 类的getResource方法 那么Class类中的getResource方法做了什么呢,主要的一句是 name = resolve... -
Springboot @Autowired 和 @Resource 我的剖析,你看完就不会忘
2020-10-23 11:01:17@Autowired 和 @Resource 这两个注解大家想必都有在项目里面出现过,但是真的清楚这俩玩意的用法或者说是区别么? 一直用的都是@Autowired ? 别人代码用什么就copy用什么,反正他没错,俺也不会错? 它们都是... -
从零开始之驱动发开、linux驱动(二十四、关于资源resource)
2018-10-09 23:57:14关于资源,在linux中有如下定义 /* * IO resources have these defined flags. */ #define IORESOURCE_BITS 0x000000ff /* Bus-...#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define I... -
Android resource linking failed AAPT: error: resource attr/*
2022-02-26 18:27:28Android resource linking failed AAPT: error: resource attr/* -
@Resource和@Autowired的区别
2022-02-10 11:57:15@Resource和@Autowired的区别 -
Java基础之try-with-resource语法糖
2020-12-22 11:41:20} } } } 运行之后我们发现: basic.exception.MyException: close at basic.exception.Connection.close(Connection.java:10) at basic.exception.TryWithResource.test(TryWithResource.java:82) at basic.... -
@Autowired注解与@Resource注解的区别
2022-04-12 11:42:38@Resource是由J2EE提供的注解,需要导入包javax.annotation.Resource 也就是说@Autowired是外部包导入的,而@Resource是J2EE自己的 二、自动注入规则 @Autowired默认按照byType自动注入 @Autowired采取的是按照... -
Error: Rule can only have one resource source (provided resource and test + include + exclude)
2020-11-02 11:01:37Error: Rule can only have one resource source (provided resource and test + include + exclude) 问题:Error: Rule can only have one resource source (provided resource and test + include + exclude) ...