Accessing spring beans in static method

前端 未结 8 586
清酒与你
清酒与你 2020-12-23 16:17

I have a Util class with static methods. Inside my Util class, I want to use spring beans so I included them in my util class. As far as I know it\'s not a good practice to

相关标签:
8条回答
  • 2020-12-23 16:41

    The approach you have outlined is what I have seen being used to inject a Spring bean into a utility class.

    <bean id="testUtils" class="com.test.TestUtils">
     <property name="testBean" ref="testBean" />
    </bean>
    

    Another option is:

    <bean name="methodInvokingFactoryBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="staticMethod" value="TestUtils.setInstance"/>
            <property name="arguments">
                <list>
                    <ref bean="testBean"/>
                </list>
           </property>
    </bean>
    

    with:

    public class TestUtils {
    
       private static testBean;
    
       public static void setInstance(TestBean anInstance) {
         testBean = anInstance;
       }
    
      public static String getBeanDetails() {
        return testBean.getDetails();
      }
    }
    

    More details are here and here

    0 讨论(0)
  • 2020-12-23 16:50

    My approach is for the bean one wishes to access to implement InitializingBean or use @PostConstruct, and containing a static reference to itself.

    For example:

    @Service
    public class MyBean implements InitializingBean {
        private static MyBean instance;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            instance = this;
        }
    
        public static MyBean get() {
            return instance;
        }
    }
    

    Usage in your static class would therefore just be:

    MyBean myBean = MyBean.get();
    

    This way, no XML configuration is required, you don't need to pass the bean in as a constructor argument, and the caller doesn't need to know or care that the bean is wired up using Spring (i.e., no need for messy ApplicationContext variables).

    0 讨论(0)
  • 2020-12-23 16:51

    you may also implement ApplicationContextAware interface, like this:

    @Component
    public class TestUtils implements ApplicationContextAware {
    
      private static ApplicationContext ac;
    
      public static String getBeanDetails() {
        return beanName = ((TestBean) ac.getBean("testBean")).getDetails();
      }
    
      @Override
      public void setApplicationContext(ApplicationContext ac) {
        TestUtils.ac = ac;
      }
    
    }
    
    0 讨论(0)
  • 2020-12-23 16:59

    The result of static methods should depend ONLY on the parameters passed into the method, therefore there is no need to call any bean.

    If you need to call another bean then your method should be a member method of a standalone bean.

    Other answers give you working solutions, but the fact it can be done doesn't mean that it should be done.

    0 讨论(0)
  • 2020-12-23 17:00

    Similar to @nullPainter's response, but we did the following. No post-construct logic required. It just sets the static member directly during injection (in the @Autowired method).

    @Service
    public class MyUtil {
    
        private static MyManager myManager;
    
        @Autowired(required = true)
        public void setMyManager(MyManager manager) {
            myManager = manager;
        }
    
        public static MyManager getMyManager() {
            return myManager;
        }
    }
    
    0 讨论(0)
  • 2020-12-23 17:03

    Generic Solution

    You can create a class that will allow access to any Bean from a static context. Most other answers here only show how to access a single class statically.

    The Proxy in the code below was added in case someone calls the getBean() method before the ApplicationContext is autowired (as this would result in a nullpointer). None of the other solutions posted here handle that nullpointer.

    Details on my blog: https://tomcools.be/post/apr-2020-static-spring-bean/

    Usage

    UserRepository userRepo = StaticContextAccessor.getBean(UserRespository.class)
    

    Full code of the StaticContextAccessor:

    @Component
    public class StaticContextAccessor {
    
        private static final Map<Class, DynamicInvocationhandler> classHandlers = new HashMap<>();
        private static ApplicationContext context;
    
        @Autowired
        public StaticContextAccessor(ApplicationContext applicationContext) {
            context = applicationContext;
        }
    
        public static <T> T getBean(Class<T> clazz) {
            if (context == null) {
                return getProxy(clazz);
            }
            return context.getBean(clazz);
        }
    
        private static <T> T getProxy(Class<T> clazz) {
            DynamicInvocationhandler<T> invocationhandler = new DynamicInvocationhandler<>();
            classHandlers.put(clazz, invocationhandler);
            return (T) Proxy.newProxyInstance(
                    clazz.getClassLoader(),
                    new Class[]{clazz},
                    invocationhandler
            );
        }
    
        //Use the context to get the actual beans and feed them to the invocationhandlers
        @PostConstruct
        private void init() {
            classHandlers.forEach((clazz, invocationHandler) -> {
                Object bean = context.getBean(clazz);
                invocationHandler.setActualBean(bean);
            });
        }
    
        static class DynamicInvocationhandler<T> implements InvocationHandler {
    
            private T actualBean;
    
            public void setActualBean(T actualBean) {
                this.actualBean = actualBean;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (actualBean == null) {
                    throw new RuntimeException("Not initialized yet! :(");
                }
                return method.invoke(actualBean, args);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题