Spring中静态方法中使用@Resource注解的变量

Spring中静态方法中使用@Resource注解的变量

问题描述

开发中,有些时候可能会工具类的静态方法,而这个静态方法中又使用到了@Resource注解后的变量。如果要直接使用 Utils.staticMethod(),项目会报异常;如果不直接使用,还要先 new Utils().staticMethod() 吧啦吧啦一大堆!

例如:

@Component
public class SendJMailUtil {

    @Resource
    private static JavaMailSender mailSender;

    public static boolean sendMail(String email, String emailMsg, String title) {
        try {
            //一个普通的邮件
            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setSubject(title);   //邮件标题
            mailMessage.setText(emailMsg);  //邮件内容
            mailMessage.setTo(email);  //接收人
            mailMessage.setFrom("123456@qq.com");  //发送人
            mailSender.send(mailMessage);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

测试:

@SpringBootTest
class SpringbootWebApplicationTests {
    @Test
    void test(){
        SendJMailUtil.sendMail("654321@qq.com","这就是个测试","测试");
    }
}

上面的代码启动报如下异常:
java.lang.IllegalStateException: @Resource annotation is not supported on static fields
注意:这里并不是spring未注入依赖,而是被static方法初始化时给清空了。

原理剖析

静态变量、类变量不是对象的属性,而是一个类的属性,所以静态方法是属于类(class)的,普通方法才是属于实体对象(也就是New出来的对象)的,spring注入是在容器中实例化对象,所以不能使用静态方法。
而使用静态变量、类变量扩大了静态方法的使用范围。静态方法在spring是不推荐使用的,依赖注入的主要目的,是让容器去产生一个对象的实例,然后在整个生命周期中使用他们,同时也让testing工作更加容易。
一旦你使用静态方法,就不需要去产生这个类的实例,这会让testing变得更加困难,同时你也不能为一个给定的类,依靠注入方式去产生多个取优不同的依赖环境的实例,这种static field是隐含共享的,并且是一种global全局状态,spring同样不推荐这样去做。

解决:

方式一:
在工具类中新建一个本类的静态变量,初始化是对本类静态变量进行赋值。通过本类静态变量进行调用@Resource注解的变量

@Component
public class SendJMailUtil {

    @Resource
    private JavaMailSender mailSender;

    // 维护一个本类的静态变量
    private static SendJMailUtil sendJMailUtil;

    // 初始化的时候,将本类中的mailSender赋值给静态的本类变量
    @PostConstruct
    public void init() {
        sendJMailUtil = this;
    }

    public static boolean sendMail(String email, String emailMsg, String title) {
        try {
            //一个普通的邮件
            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setSubject(title);   //邮件标题
            mailMessage.setText(emailMsg);  //邮件内容
            mailMessage.setTo(email);  //接收人
            mailMessage.setFrom("123456@qq.com");  //发送人
            sendJMailUtil.mailSender.send(mailMessage);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

方式二:
定义静态变量,将@Autowired注解加到构造方法上,对变量赋值;

@Component
public class SendJMailUtil {

    private static JavaMailSender mailSender;
    
    //将@Autowired加到构造方法上
    @Autowired
    public SendJMailUtil(JavaMailSender mailSender){
        SendJMailUtil.mailSender = mailSender;
    }

    public static boolean sendMail(String email, String emailMsg, String title) {
        try {
            //一个普通的邮件
            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setSubject(title);   //邮件标题
            mailMessage.setText(emailMsg);  //邮件内容
            mailMessage.setTo(email);  //接收人
            mailMessage.setFrom("123456@qq.com");  //发送人
            mailSender.send(mailMessage);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

方式三
新建一个静态变量,通过@PostContruct注解,在类初始化时将@Resource注解变量赋值给静态变量;

@Component
public class SendJMailUtil {

    @Resource
    private JavaMailSender mailSender;

    // 新建一个静态的变量
    private static JavaMailSender staticMailSender;

    // 初始化的时候,将@Resource变量赋值给静态变量
    @PostConstruct
    public void init() {
        staticMailSender = mailSender;
    }

    public static boolean sendMail(String email, String emailMsg, String title) {
        try {
            //一个普通的邮件
            SimpleMailMessage mailMessage = new SimpleMailMessage();
            mailMessage.setSubject(title);   //邮件标题
            mailMessage.setText(emailMsg);  //邮件内容
            mailMessage.setTo(email);  //接收人
            mailMessage.setFrom("1737165855@qq.com");  //发送人
            staticMailSender.send(mailMessage);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

总结:自我感觉这三种方式其实是一样的,就是另外又新建了一个静态变量,通过注解对非静态变量进行bean注入,然后再将非静态变量赋值给静态变量。以达到在静态方法中调用静态变量的效果。

相关注解介绍:
@Component

泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

@Resource

Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入。
@Resoutce有两个属性比较重要,分别是 name 和 type Spring将@Resoure注解的 name 属性解析为bean的名字,而type属性则解析为bean的类型。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用byName自动注入策略。

@Resource装配顺序:

  • 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
  • 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
  • 如果指定了 type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  • 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配则回退为一个原始类型(UserDao)进行匹配,如果匹配则自动装配;

@PostConstruct

  • @PostConstruct 该注解是 javax.annotation 包下的,被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载 Servlet 的时候运行,并且只会被服务器执行一次。PostConstruct 在构造函数之后执行,init() 方法之前执行。
  • @PostConstruct注释规则:除了拦截器这个特殊情况以外,其他情况都不允许有参数,否则spring框架会报IllegalStateException; 而且返回值要通常是 void,但实际也可以有返回值,至少不会报错,只会忽略

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

参考链接:
https://www.cnblogs.com/jiliunyongjin/p/10860684.html
https://www.cnblogs.com/raoyulu/p/13331698.html
https://segmentfault.com/a/1190000003858538

  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值