【问题标题】:Can you use @Autowired with static fields?您可以将@Autowired 与静态字段一起使用吗?
【发布时间】:2010-11-04 08:44:33
【问题描述】:

有没有办法将@Autowired 与静态字段一起使用。如果没有,还有其他方法可以做到这一点吗?

【问题讨论】:

标签: java spring autowired


【解决方案1】:

简而言之,没有。您不能在 Spring 中自动连接或手动连接静态字段。您必须编写自己的逻辑来执行此操作。

【讨论】:

  • 当你发现旧代码这样做时,这是一种反模式。眯着眼睛,歪着头,找到解决问题的更好方法。你会很高兴你做到了。
  • this answer 对 Spring 的 @AutoWired 也有帮助
【解决方案2】:

创建一个可以自动装配的 bean,它将初始化静态变量作为副作用。

【讨论】:

    【解决方案3】:

    @Autowired 可以与 setter 一起使用,因此您可以让 setter 修改静态字段。

    最后一个建议...不要

    【讨论】:

    • 你为什么不建议这样做?
    • 嗯..我对为什么不推荐它的感觉是,因为那时类中的静态实例超出了spring的控制范围。注入后,静态字段是相应(周围)类的所有对象实例的 引用。但是,这种行为可能正是预期会发生的,因此可能被视为错误或功能......
    • 是的@matthaeus,这正是我在需要访问 org.springframework.core.env.Environment 时所期望的功能:@Component public class SpringAppEnv{ public static Environment _env; @Autowired public void setEnv(Environment env) {_env = env;} }
    • @JonLorusso and all 因为当类加载器加载静态值时,还没有必要加载 Spring 上下文。所以类加载器不会正确地在 bean 中注入静态类并且会失败。 Andrea T 提供的答案
    【解决方案4】:

    您可以使用 XML 表示法MethodInvokingFactoryBean 来实现这一点。例如看here

    private static StaticBean staticBean;
    
    public void setStaticBean(StaticBean staticBean) {
       StaticBean.staticBean = staticBean;
    }
    

    您应该尽可能使用弹簧注射,因为这是推荐的方法,但这并不总是可行的,因为我相信您可以想象,并非所有东西都可以从弹簧容器中拉出或您可能会处理遗留系统。

    使用这种方法进行笔记测试也可能更加困难。

    【讨论】:

      【解决方案5】:
      @Component("NewClass")
      public class NewClass{
          private static SomeThing someThing;
      
          @Autowired
          public void setSomeThing(SomeThing someThing){
              NewClass.someThing = someThing;
          }
      }
      

      【讨论】:

      • 知道如何在初始化存储库时使用这种方法吗?
      • 缺点:不能保证 someThing 如果静态访问则已初始化:NewClass.staticMethodWhichUsesSomething(); 如果在应用程序初始化之前使用,可能会抛出 NPE
      • 能避免Instance methods should not write to "static" fields (squid:S2696)的警告吗?
      • @user7294900:仅针对这种非常特殊的情况禁用此警告。
      • @izogfif 仍然是一个问题,如果我在广泛的案例和类中选择此解决方案
      【解决方案6】:

      在@PostConstruct 方法中初始化你的自动装配组件

      @Component
      public class TestClass {
         private static AutowiredTypeComponent component;
      
         @Autowired
         private AutowiredTypeComponent autowiredComponent;
      
         @PostConstruct
         private void init() {
            component = this.autowiredComponent;
         }
      
         public static void testMethod() {
            component.callTestMethod();
         }
      }
      

      【讨论】:

      • 能避免Instance methods should not write to "static" fields (squid:S2696)的警告吗?
      • 也可以直接通过构造函数来实现。
      • @user7294900 我想这个警告告诉我们为什么这被认为是一种反模式。当实例方法写入静态变量时,静态变量成为类的所有对象共享的关键资源,从而在多线程环境中产生潜在问题。
      • 它拯救了我的一天
      【解决方案7】:
      private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);
      

      【讨论】:

      • 虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
      • 我认为这个答案可能根本不需要任何解释。
      【解决方案8】:

      你可以使用 ApplicationContextAware

      @Component
      public class AppContext implements ApplicationContextAware{
          public static ApplicationContext applicationContext;
      
          public AppBeans(){
          }
      
          @Override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
              this.applicationContext = applicationContext;
          }
      }
      

      然后

      static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);
      

      【讨论】:

        【解决方案9】:

        免责声明这绝不是标准的,很可能有更好的弹簧方式来做到这一点。以上答案都没有解决连接公共静态字段的问题。

        我想完成三件事。

        1. 使用弹簧“自动装配”(我使用@Value)
        2. 公开一个公共静态值
        3. 防止修改

        我的对象看起来像这样

        private static String BRANCH = "testBranch";
        
        @Value("${content.client.branch}")
        public void finalSetBranch(String branch) {
            BRANCH = branch;
        }
        
        public static String BRANCH() {
            return BRANCH;
        }
        

        我们现在已经检查了 1 和 2,我们如何防止调用 setter,因为我们无法隐藏它。

        @Component
        @Aspect
        public class FinalAutowiredHelper {
        
        @Before("finalMethods()")
        public void beforeFinal(JoinPoint joinPoint) {
            throw new FinalAutowiredHelper().new ModifySudoFinalError("");
        }
        
        @Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
        public void finalMethods() {}
        
        
        public class ModifySudoFinalError extends Error {
            private String msg;
        
            public ModifySudoFinalError(String msg) {
                this.msg = msg;
            }
        
            @Override
            public String getMessage() {
                return "Attempted modification of a final property: " + msg;
            }
        }
        

        这个切面会包装所有以final开头的方法,如果被调用就会抛出错误。

        我认为这不是特别有用,但如果你是强迫症并且喜欢将豌豆和胡萝卜分开,这是一种安全的方法。

        重要 Spring 在调用函数时不会调用你的切面。让这变得更容易了,糟糕的是我在弄清楚之前就弄清楚了逻辑。

        【讨论】:

          【解决方案10】:

          想要添加自动连接静态字段(或常量)将被忽略,但也不会产生任何错误的答案:

          @Autowired
          private static String staticField = "staticValue";
          

          【讨论】:

            【解决方案11】:

            通常,通过对象实例设置静态字段是一种不好的做法。

            为避免可选问题,您可以添加 synchronized 定义,并且仅在私有静态 Logger 记录器时设置它;

            @Autowired
            public synchronized void setLogger(Logger logger)
            {
                if (MyClass.logger == null)
                {
                    MyClass.logger = logger;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-12-10
              • 1970-01-01
              • 1970-01-01
              • 2022-06-15
              相关资源
              最近更新 更多