精华内容
下载资源
问答
  • resource
    千次阅读
    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:59
    Android中解析编译之后的
  • vue-resource.js

    千次下载 热门讨论 2016-04-20 12:47:03
    vue-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装配未生效的情况:

    1. @Autowired所在类未加@Controller、@Service、@Component、@Repository等注解,或者或者一些其它情况(如直接new对象的到实例)。这些情况会导致该类的bean并没有交给spring容器去管理,spring就无法完成自动装配的功能。

      public class UserService {
      
          @Autowired
          private IUser user;
      
          public void test() {
              user.say();
          }
      }
      
    2. 注解未被@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:47
    org.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){...}
    
    

            在类继承关系中,每个类定义的方法如下:

    ResourceAbstractResourceAbstractFileResolvingResourceClassPathResource
    对底层资源的抽象描述
    比如文件或类路径资源
    对资源接口描述的基础实现
    预先实现特定的行为
    将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协议则返回true
    protected boolean isFile(URI uri)
    重载isFile方法,根据指定的URI判断该资源是否是一个文件引用,如果URI的scheme以vfs开头则交给VfsResourceDelegate类判断,否则判断如果URI的scheme等于file则返回true
    URL getURL()
    返回该资源对应的URL
    public URL getURL()
    实现父接口方法,这里假设资源不能解析为URL,直接返回了FileNotFoundException异常
    public URL getURL()
    重写父类方法,根据类路径参数获取该资源的URL对象
    URI getURI()
    返回该资源对应的URI
    public URI getURI()
    实现父接口方法,基于getURL返回的URL构建一个URI
    File 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.txt
    public 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:22
    maven 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) 问题: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:16
    getResource是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:15
    createRelative:用于创建相对于当前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:14
    Unity Resource文件的基本概念 本篇文章主要讲解Resource文件在Unity场景中的使用以及他的基本概念。 文章目录 Unity Resource文件的基本概念 前言 一、Resource文件是什么鬼? 二、Resource常用方式 1.Resource....
  • 只不过Class.getResource是先调用Class 的 getResource 方法,在这个getResource 方法中,再去调用ClassLoader 类的getResource方法 那么Class类中的getResource方法做了什么呢,主要的一句是 name = resolve...
  • @Autowired 和 @Resource 这两个注解大家想必都有在项目里面出现过,但是真的清楚这俩玩意的用法或者说是区别么? 一直用的都是@Autowired ? 别人代码用什么就copy用什么,反正他没错,俺也不会错? 它们都是...
  • 关于资源,在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/*
  • @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) 问题:Error: Rule can only have one resource source (provided resource and test + include + exclude) ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,609,346
精华内容 643,738
关键字:

resource

友情链接: tdhotel.rar