SpringBoot中Utils中无法注入Bean

问题:

使用springboot的JavaMailSender 发送邮件时一直注入不了。

@Autowired
private JavaMailSender javaMailSender;

原始代码:

@Autowired
private JavaMailSender javaMailSender;

 public static void sendMail(String receiveEmail, String emailSubject, String emailText){
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("website@aamazedata.com");
        message.setTo(receiveEmail);
        message.setText(emailText);
        message.setSubject(emailSubject);
        javaMailSender.send(message);
    }

上面的这样测试时一直在报空指针异常,提示无法注入JavaMailSender。

有两种解决方法:

一、 使用applicationContext的getBean方法即可实现。

public static void sendMail(String receiveEmail, String emailSubject, String emailText){
    JavaMailSender javaMailSender = ApplicationContextUtil.get(JavaMailSender.class);
    SimpleMailMessage message = new SimpleMailMessage();
    message.setFrom("website@aamazedata.com");
    message.setTo(receiveEmail);
    message.setText(emailText);
    message.setSubject(emailSubject);
    javaMailSender.send(message);
}

ApplicationContextUtill:

@Configuration
public class ApplicationContextUtil implements ApplicationContextAware {

    // 定义静态的ApplicationContext成员对象
    private static ApplicationContext applicationContext;

    // 重写setApplicationContext方法,把参数中的ApplicationContext对象赋值给类静态成员
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextUtil.applicationContext = applicationContext;
    }

    // 定义get方法,参数为Class,调用上下文的getBean方法获取容器中的指定对象
    public static <T> T get(Class<T> clazz){
        return applicationContext.getBean(clazz);
    }

    // 同上,参数为对象名
    public static Object get(String name) {
        return applicationContext.getBean(name);
    }
}

个人感觉上面这个类其实的核心还是ApplicationContext,直接调用ApplicationContext。getBean()应该就可以。

上面是使用自己定义的ApplicationContextUtil来获取Spring配置文件中的实例方法。

当然还有许多现成的工具类: SpringUtils.getBean() 、 SpringContextUtil.getBean()都可以。

二、 使用@PostConstruct注解,专门可以解决在静态方法中调用依赖注入的Bean中的方法。

@PostConstruct注解好多人以为是Spring提供的。其实是java自己的注解。

java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。该@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且服务器只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

通常我们会是在Spring框架中使用@PostConstruct注解。该注解在整个Bean初始化中的执行顺序是: Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)。

解决方法

1、首先在MailUtis上添加@Component声明其为bean组件

2、使用Autowired注入要注入的那个类(JavaMailSender)

3、在MailUtils中声明一个静态的私有变量private static MailUtils mailUtils;

4、添加共有的init方法,并使用@PostConstruct声明在项目启动的时候进行执行该方法,也可以理解为在Spring容器初始化的时候执行该方法。

代码变为:

@Component
public class MailUtils {
    @Autowired
    private JavaMailSender javaMailSender;

    private static MailUtils mailUtils;

     @PostConstruct
     public void init(){
     mailUtils = this;
     mailUtils.javaMailSender = this.javaMailSender;
     }

      public static void sendMail(String receiveEmail, String emailSubject, String emailText){
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom("website@aamazedata.com");
            message.setTo(receiveEmail);
            message.setText(emailText);
            message.setSubject(emailSubject);
            mailUtils.javaMailSender.send(message);
        }
}

上面的办法亲测可用。

上面的办法也可以解决下面的问题:

当我们自己创建了配置类:

@Configuration
public class MailAccountConfig implements ApplicationContextAware {
    @Value("${email.host}")
    private String host;
    @Value("${email.port}")
    private Integer port;
    @Value("${email.from}")
    private String from;
    @Value("${email.user}")
    private String user;
    @Value("${email.pass}")
    private String pass;
    @Value("${email.ssLEnable}")
    private Boolean ssLEnable;
    
    
     @Bean
    public MailAccount mailAccount(){
        MailAccount mailAccount = new MailAccount();
        mailAccount.setHost(host);
        mailAccount.setPort(port);
        mailAccount.setAuth(true);
        mailAccount.setFrom(from);
        mailAccount.setUser(user);
        mailAccount.setPass(pass);
        mailAccount.setSslEnable(ssLEnable);
        return mailAccount;
    }

当想自己使用这个Bean时,你会发现在Utils中还是不能注入,这时还是借鉴上面的PostConstruct注解解决。

SpringBoot的Utils中还不能读取Application.yml中配置的属性

遇到这种情况时,首先考虑Spring接口类Environment:

  1. Spring 为运行环境提供了的高度的抽象接口,项目运行中的所有配置都是基于此接口,用来表示整个应用运行时的环境。
  2. 该接口继承自PropertyResolver, 而PropertyResolver规范了解析底层任意property资源,也就意味着application.yml是由ProprttyResolver管理。
  3. PropertyResolver提供了方法getProperty(String key) ,该方法通过传入properties文件中定义的key,返回与给定键关联的属性值。
    因此使用下面的代码即可实现在Utils中自由读取配置文件中自定义的所有属性。
public static String getProperty(){
    Environment environment = ApplicationContextUtil.get(Environment.class);
    String host = environment.getProperty("spring.mail.host");
    logger.info(host);
    String port = environment.getProperty("spring.mail.port");
    logger.info(port);
    String username = environment.getProperty("spring.mail.username");
    logger.info(username);
    String password = environment.getProperty("spring.mail.password");
    logger.info(password);
    String protocol = environment.getProperty("spring.mail.protocol");
    logger.info(protocol);
    return null;
}