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