关于springboot读取配置类,使用@Autowired自动注入为null的问题

问题

之前写了一个配置类,读取config.yml配置文件里写好的阿里云oss的一些参数配置。配置类读取并无问题,但是在另一个普通类里自动注入该配置类时,引用配置类属性却报NullPointer异常。然后终于发现问题原因了。

代码

1. 配置文件

application-oss.properties

aliyun.oss.endpoint=oss-ap-southeast-1.aliyuncs.com
aliyun.oss.bucket-name=tms-oss-singapore
aliyun.oss.key-id=LTAI5tN999G8YVxnJ8ss8ouh
aliyun.oss.key-secret=dSk4kGrtHFT2quUBwO4KNJFcQLwFZc

2. 配置类

package com.yunsystem.common.utils;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;


@Component
@Data
@PropertySource(value = {"classpath:application-oss.properties"})
@SpringBootApplication
public class OssProperties {

    @Value(value = "${aliyun.oss.endpoint}")
    private String endpoint; // bucket所在地域对应的endpoint
    @Value(value = "${aliyun.oss.key-id}")
    private String keyid; // accesskey的id
    @Value(value = "${aliyun.oss.key-secret}")
    private String keysecret; // accesskey的密钥
    @Value(value = "${aliyun.oss.bucket-name}")
    private String bucketname; // 存储桶名称

}

3. 调用配置类的普通类

以下类为OSS上传文件类

package com.yunsystem.common.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.model.ObjectMetadata;
import com.yunsystem.common.error.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class OssUpload {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private OssProperties ossProperties;    // 这里即注解自动装配

    /**
     * 使用aliyun-sdk实现上传
     * @param inputStream 上传的文件流
     * @param module   oss目录
     * @param originalFileName 文件名
     * @return
     * @throws BusinessException
     */
    public Map upload(InputStream inputStream, String module, String originalFileName) throws BusinessException {

        /**
         * 获取oss的属性
         */

        String endpoint = ossProperties.getEndpoint();
        String accessKeyId = ossProperties.getKeyid();
        String accessKeySecret = ossProperties.getKeysecret();
        String bucketName = ossProperties.getBucketname();
        String objectName = "";
        Map<String, String> map = new HashMap<>();

        // 创建上传文件的元信息,可以通过文件元信息设置HTTP header。
        ObjectMetadata meta = new ObjectMetadata();
        // 创建OSSClient实例。
        OSS ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String folder = simpleDateFormat.format(new Date());
            // 文件名  sn+开始结束时间戳
            String fileName = originalFileName.substring(0, originalFileName.lastIndexOf(".")); //UUID.randomUUID().toString();

            //文件扩展名
            String fileExtention = originalFileName.substring(originalFileName.lastIndexOf("."));
            if (StringUtils.isNotEmpty(module)) {
                //最终的路径 类似actionRec/abnormal/2023-01-05-09:52:41/xxxxxxxxx.jpg
                objectName = module + "/" + folder + "/" + fileName + fileExtention;
            } else {
                objectName = folder + "/" + fileName + fileExtention;
            }
            meta.setContentType("text/plain");
            meta.setContentDisposition("inline");

            // 上传文件
            ossClient.putObject(bucketName, objectName, inputStream, meta); // bucket名称,上传文件路径和名称,上传文件输入流
            //https://rxt.oss-cn-beijing.aliyuncs.com/avatar/01.jpeg
            map.put("fileUrl", "https://" + bucketName + "." + endpoint + "/" + objectName);
            return map;

        } catch (OSSException oe) {
            logger.error("Error Message: " + oe.getErrorMessage());
            throw new ServiceException(oe.getMessage());
        } catch (ClientException ce) {
            logger.error("Error Message: " + ce.getMessage());
            throw new ServiceException(ce.getMessage());
        } finally {
            if (ossClient != null) {
                // 关闭OSSClient。
                ossClient.shutdown();
            }
        }
    }
}

4. 业务类ServiceImpl调用oss上传工具类实现业务文件上传

@Transactional
@Service
public class DeviceActiveInfoImpl implements DeviceActiveInfoService {
	
	...
	public void uploadFile(MultipartFile file) {
		...
		// 1. 解析文件数据
        String fileName = file.getOriginalFilename();
        File newFile = new File(fileName);
        try {
            FileUtils.copyInputStreamToFile(file.getInputStream(), newFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        newFile.deleteOnExit();  // JVM停止后删除临时文件
        // newFile.delete(); 调用完立即删除临时文件

        // 2. 调用上传文件接口
        OssUpload ossUpload = new OssUpload();    //  !!! 注意这里,问题在这!!!
        // 2.1. 上传文件
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();  // 获取文件流
        } catch (IOException e) {
            e.printStackTrace();
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String extName = fileName.substring(fileName.lastIndexOf(".")); // 后缀名
        //fileName = sn + sdf.format(startTimeStamp) + extName;
        String module = "ScanKMS/error";  // 存储空间目录文件夹

        Map map = ossUpload.upload(inputStream, module, fileName);
        // 2. 保存记录到数据库
        String filePath = (String) map.get("fileUrl");

        for (DeviceActiveInfo deviceActiveInfo : deviceActiveInfos) {
            deviceActiveInfo.setFile_url(filePath);
            deviceActiveInfoMapper.updateByPrimaryKeySelective(deviceActiveInfo);
        }
	}
}

原因

读取配置没问题,但是在普通类@Autowired注入失败,原因是 我的普通类是oss上传工具类,然后在业务类方法中调用时使用了new 的方式!!!

// 2. 调用上传文件接口
        OssUpload ossUpload = new OssUpload(); 

解决

业务类业务方法调用oss上传工具类时, 同样使用 @Autowired 自动注入的方式注入而不是new的方式!!!

	@Autowired
    private OssUpload ossUpload;

—— 以下引用自:风兮雨露——:

1、描述

有时候我们在某个类用@Autowired 进行注入时,会发现注入参数为null,这时候会有疑惑。

可能存在的原因:

(1)该类没有托管给spring 管理,一般在类的上面添加@Component

(2)你的这个类有被new出来的实例的,new 过的对象不会交给Spring容器管理 所以里面的 service或者dao注入不进来。一般是指引用某些框架,你是继承某个接口,但是这些框架默认new过这个方法,比如MVC拦截的HandlerInterceptor类。

2、解决方式

对于(1)其实就加入@Component 就可以了。接下来讲怎么解决第(2)种情况:

有时候你确实需要在这个new 的类去注入某些类,但是用@Autowired 又注入为null,这时候我们需要手动去弄Spring容器中的Bean实现ApplicationContextAware接口。

(1)比如我想在某个类实现RedisUtils 类的注入,但是用@autowired 会报null

(2)这时候我们就要手动实现这个功能,写个BeanUtils 类 实现ApplicationContextAware接口

 
 
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class BeanUtils implements ApplicationContextAware  {
    protected static ApplicationContext applicationContext ;
 
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        if (applicationContext == null) {
            applicationContext = arg0;
        }
 
    }
    public static Object getBean(String name) {
        //name表示其他要注入的注解name名
        return applicationContext.getBean(name);
    }
 
    /**
     * 拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象
     */
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

注意,该类必须用@Component 注解

(3)使用时用以下方式

    /**
     * 由于不能用@Autowired进行注入,则使用这种手动注入方式
     */
    private RedisUtils redisUtils = BeanUtils.getBean(RedisUtils.class);

这样就要正常引用redisUtils ,这样手动出来的跟@Autowire 一样的。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 提供了多种方式来读取配置文件。其中一种方式是使用 @PropertySource 注解。通过在应用程序的主上添加 @PropertySource 注解,并指定要读取配置文件路径,可以实现读取配置文件的功能。具体代码如下: ```java @SpringBootApplication @PropertySource("classpath:application.properties") public class DemoApplication implements InitializingBean { @Value("${profile.name}") private String name; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Name:" + name); } } ``` 另一种方式是使用 Properties 对象来读取配置文件。可以通过创建一个 Properties 对象,使用其 load 方法来加载配置文件,然后通过 get 方法获取配置项的值。具体代码如下: ```java @SpringBootApplication public class DemoApplication implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { Properties props = new Properties(); try { InputStreamReader inputStreamReader = new InputStreamReader( this.getClass().getClassLoader().getResourceAsStream("application.properties"), StandardCharsets.UTF_8); props.load(inputStreamReader); } catch (IOException e1) { System.out.println(e1); } System.out.println("Properties Name:" + props.getProperty("profile.name")); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 还可以使用 Environment 读取配置文件。可以通过在注入 Environment 对象,然后使用其 getProperty 方法来获取配置项的值。具体代码如下: ```java @SpringBootApplication public class DemoApplication implements InitializingBean { @Autowired private Environment environment; @Override public void afterPropertiesSet() throws Exception { System.out.println("Profile Name:" + environment.getProperty("profile.name")); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 以上是 Spring Boot 读取配置文件的几种方式。可以根据实际情况选择适合的方式来读取配置文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [SpringBoot 读取配置文件的 5 种方法!](https://blog.csdn.net/m0_71777195/article/details/126419460)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值