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

  • Post author:
  • Post category:其他




问题

之前写了一个配置类,读取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 一样的。



版权声明:本文为qq_36256590原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。