Spring使用@Autowired注解静态实例对象方式

 更新时间:2021年08月26日 15:06:33   作者:jessehuangzx  
这篇文章主要介绍了Spring使用@Autowired注解静态实例对象方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Spring @Autowired注解静态实例对象

问题

最近项目小组在重新规划工程的业务缓存,其中涉及到部分代码重构,过程中发现有些工具类中的静态方法需要依赖别的对象实例(该实例已配置在xml成Spring bean,非静态可以用@Autowired加载正常使用),而我们知道,类加载后静态成员是在内存的共享区,静态方法里面的变量必然要使用静态成员变量,这就有了如下代码:

@Component
public class TestClass {
    @Autowired
    private static AutowiredTypeComponent component;
    // 调用静态组件的方法
    public static void testMethod() {
        component.callTestMethod();
    }
}

编译正常,但运行时报java.lang.NullPointerException: null异常,显然在调用testMethod()方法时,component变量还没被初始化,报NPE。

原因

所以,在Springframework里,我们是不能@Autowired一个静态变量,使之成为一个Spring bean的。为什么?其实很简单,因为当类加载器加载静态变量时,Spring上下文尚未加载。所以类加载器不会在bean中正确注入静态类,并且会失败。

解决方案

方式一

将@Autowired 注解到类的构造函数上。很好理解,Spring扫描到AutowiredTypeComponent的bean,然后赋给静态变量component。示例如下:

@Component
public class TestClass {
    private static AutowiredTypeComponent component;
    @Autowired
    public TestClass(AutowiredTypeComponent component) {
        TestClass.component = component;
    }
    // 调用静态组件的方法
    public static void testMethod() {
        component.callTestMethod();
    }
}

方式二

给静态组件加setter方法,并在这个方法上加上@Autowired。Spring能扫描到AutowiredTypeComponent的bean,然后通过setter方法注入。示例如下:

@Component
public class TestClass {
    private static AutowiredTypeComponent component;
    @Autowired
    public void setComponent(AutowiredTypeComponent component){
        TestClass.component = component;
    }
    // 调用静态组件的方法
    public static void testMethod() {
        component.callTestMethod();
    }
}

方式三

定义一个静态组件,定义一个非静态组件并加上@Autowired注解,再定义一个初始化组件的方法并加上@PostConstruct注解。这个注解是JavaEE引入的,作用于servlet生命周期的注解,你只需要知道,用它注解的方法在构造函数之后就会被调用。示例如下:

@Component
public class TestClass {
   private static AutowiredTypeComponent component;
   @Autowired
   private AutowiredTypeComponent autowiredComponent;
   @PostConstruct
   private void beforeInit() {
      component = this.autowiredComponent;
   }
   // 调用静态组件的方法
   public static void testMethod() {
      component.callTestMethod();
   }
}

方式四

直接用Spring框架工具类获取bean,定义成局部变量使用。但有弊端:如果该类中有多个静态方法多次用到这个组件则每次都要这样获取,个人不推荐这种方式。示例如下:

public class TestClass {
    // 调用静态组件的方法
   public static void testMethod() {
      AutowiredTypeComponent component = SpringApplicationContextUtil.getBean("component");
      component.callTestMethod();
   }
}

总结

在上面的代码示例中,我每个类都加了@Component注解,其实可以根据需要进行变更,比如这个类是处理业务逻辑,可以换成@Service;这个类是处理请求进行转发或重定向的,可以换成@Controller(是Spring-mvc的注解);这个类是专门用来操作Dao的就@Repository。

Spring的注解帮你做了一件很有意义的事:就是它们对应用进行了分层,这样就能将请求处理、业务逻辑处理、数据库操作处理分离出来,为代码解耦,也方便了项目的开发和维护。

Spring容器bean加载机制用到了Java的反射,这里先不作赘述,以后会专门写一篇文章来总结Java反射在Spring的IoC和AoP中的应用。

@Autowired注解和静态方法

一、业务场景

spring框架应用中有些静态方法需要依赖被容器管理的类,就像这样:

@Component
public class Test {    
    @Autowired
    private static UserService userService;    
    public static void test() {
        userService.test();
    }
}

这样一定会报java.lang.NullPointerException: null异常。

二、原理剖析

静态变量、类变量不是对象的属性,而是一个类的属性,所以静态方法是属于类(class)的,普通方法才是属于实体对象(也就是New出来的对象)的,spring注入是在容器中实例化对象,所以不能使用静态方法。

而使用静态变量、类变量扩大了静态方法的使用范围。静态方法在spring是不推荐使用的,依赖注入的主要目的,是让容器去产生一个对象的实例,然后在整个生命周期中使用他们,同时也让testing工作更加容易。

一旦你使用静态方法,就不再需要去产生这个类的实例,这会让testing变得更加困难,同时你也不能为一个给定的类,依靠注入方式去产生多个具有不同的依赖环境的实例,这种static field是隐含共享的,并且是一种global全局状态,spring同样不推荐这样去做。

三、解决方法

1、将@Autowire加到构造方法上

@Component
public class Test {    
    private static UserService userService;    
    @Autowired
    public Test(UserService userService) {
        Test.userService = userService;
    }
}

2、用@PostConstruct注解

@Component
public class Test {    
    private static UserService userService;    
    @Autowired
    private UserService userService2;    
    @PostConstruct
    public void beforeInit() {
        userService = userService2;
    }    
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot与SpringMVC第一讲

    SpringBoot与SpringMVC第一讲

    SpringMVC全名应该叫做SpringWebMVC,它其实是基于servlet来构建的一个原始web框架从一开始就包含在了spring框架中,下面通过实例代码给大家介绍SpringBoot与SpringMVC的相关知识,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • Java操作redis实现增删查改功能的方法示例

    Java操作redis实现增删查改功能的方法示例

    这篇文章主要介绍了Java操作redis实现增删查改功能的方法,涉及java操作redis数据库的连接、设置、增删改查、释放资源等相关操作技巧,需要的朋友可以参考下
    2017-08-08
  • 详解Java中String,StringBuffer和StringBuilder的使用

    详解Java中String,StringBuffer和StringBuilder的使用

    这篇文章主要为大家详细介绍了Java中String,StringBuffer和StringBuilder三者的区别以及使用,文中的少了讲解详细,感兴趣的可以了解一下
    2022-07-07
  • 详解基于Spring Data的领域事件发布

    详解基于Spring Data的领域事件发布

    这篇文章主要介绍了详解基于Spring Data的领域事件发布,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • java ssm框架的controller实现向页面传递参数

    java ssm框架的controller实现向页面传递参数

    这篇文章主要介绍了java ssm框架的controller实现向页面传递参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Spring Boot 使用 SSE 方式向前端推送数据详解

    Spring Boot 使用 SSE 方式向前端推送数据详解

    这篇文章主要介绍了Spring Boot 使用SSE方式向前端推送数据详解,SSE简单的来说就是服务器主动向前端推送数据的一种技术,它是单向的,也就是说前端是不能向服务器发送数据的
    2022-08-08
  • Geotools实现shape文件的写入功能

    Geotools实现shape文件的写入功能

    Geotools作为开源的Java GIS三方库,已经成为GIS服务器端的主流开源库,其功能非常强大,涉及到GIS业务的方方面面,其中就包括GIS数据的读写,今天小编就借助Geotools来实现shape数据的写入,需要的朋友可以参考下
    2023-08-08
  • Java中的StackOverflowError错误问题及解决方法

    Java中的StackOverflowError错误问题及解决方法

    这篇文章主要介绍了Java中的StackOverflowError错误,在本文中,我们仔细研究了StackOverflower错误,包括Java代码如何导致它,以及我们如何诊断和修复它,需要的朋友可以参考下
    2022-07-07
  • Java编程实现向文本文件中读取数据之Scanner用法示例

    Java编程实现向文本文件中读取数据之Scanner用法示例

    这篇文章主要介绍了Java编程实现向文本文件中读取数据之Scanner用法,结合实例形式分析了java使用Scanner类读取文本文件相关操作技巧与注意事项,需要的朋友可以参考下
    2018-03-03
  • IDEA调试技巧条件断点实现步骤详解

    IDEA调试技巧条件断点实现步骤详解

    这篇文章主要介绍了IDEA调试技巧条件断点实现步骤详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09

最新评论