【问题标题】:Using an autowired singleton bean in a non spring managed java class在非 Spring 管理的 Java 类中使用自动装配的单例 bean
【发布时间】:2020-07-09 16:52:02
【问题描述】:

好吧,这对于所有退伍军人来说可能看起来很愚蠢,但请耐心等待,因为我只是在 Spring 和 Spring Boot 中找到自己的方法。

我这里有一个控制器类,

@RestController
public class Controller {

    private static final Logger logger = LogManager.getLogger(Controller.class);

    private static Controller controller = null;

    @Autowired
    private ApplicationParameters applicationParameters;

    public static Controller getInstance() {
        if (controller == null) {
            synchronized (Controller.class) {
                if (controller == null) {
                    controller = new Controller();
                }
            }
        }
        return controller;
    }

    public Controller() {}

    public ApplicationParameters getApplicationParameters() {
        return applicationParameters;
    }

    @RequestMapping("/")
    public void init() {
        try {
            for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
                logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());

                Utils.concatenate("key1", "key2");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
} 

它使用属性文件中的属性自动装配ApplicationParameters bean。

实用类

public class Utils {

protected static final Logger logger = LogManager.getLogger(Utils.class);

    //Need to get the value of the property keys propKey1 & propKey2 and concat them.
    public static String concatenate(String propKey1, String propKey2) throws Exception {
        if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
            return Controller.getInstance().getApplicationParameters().getProperties().get(propKey1) + Controller.getInstance().getApplicationParameters().getProperties().get(propKey2)
        } else {
            logger.error("System Property is undefined." );
            return null;
        }
    }

所以,我想在我的项目的整个生命周期中使用这个自动连接的ApplicationParameters bean 作为singleton 实例。

例如,我想在Utils 类中使用它。显然Utils 类不是弹簧管理的,它只是一个普通的旧 java 类。

所以我想知道如何在我的 Utils 类中使用完全初始化的 applicationParameters。

这是我迄今为止尝试过的:

  1. Utils 类中再次自动装配ApplicationParameters,就像这样,

    public class Utils {
    
    @Autowired
    private ApplicationParameters applicationParameters;
    
    protected static final Logger logger = LogManager.getLogger(Utils.class);
    

applicationParameters 在这里将是null,我猜这是因为Utils 不是弹簧管理的bean。

  1. 使Controller 类单例。 (不确定如何执行此操作,因为需要在 Web 服务器启动时调用 init(),然后在哪里调用 getInstance()?)

所以,有没有好心人来帮助这里的新手。

附: Utils 类仅作为示例显示,以说明 Spring 管理的自动装配 bean 必须在常规 java 类中使用。

【问题讨论】:

  • 您的 Util 类是否使用 Service/Controller/Component 注释?否则,您在类中使用的 Autowired 注释不起作用。您也可以使用 PostConstruct 而不是 RequestMapping 来注释您的 init 方法。
  • 您的单例实现可能是错误的。当您将公共构造函数放入类中时,仍然有人可以创建该类的新实例。您应该将构造函数设为私有,以便它们只能通过 getInstance 方法访问类实例。最好再读一遍spring bean scope,singleton,prototype文档。

标签: java spring spring-boot


【解决方案1】:

您可以使用像这样的辅助类从外部访问 spring 上下文:

public class SpringContextUtil implements ApplicationContextAware {
   static ApplicationContext applicationContext;     

   public void setApplicationContext(ApplicationContext context) throws BeansException {
     applicationContext = context;
   }

   public static ApplicationContext getApplicationContext() {
     return applicationContext;
   }
}

然后,你可以这样做:SpringContextUtil.getApplicationContext.getBean("applicationParameters")

【讨论】:

  • 嗨,有什么办法可以将我的 Controller 类本身用作单例实例?
  • 您可以在控制器的构造函数中初始化静态控制器字段。但是你最终会在 util 类中得到一个控制器依赖项。
  • You could probably initialize the static controller field in the constructor of the controller。你能帮我解决这个问题吗?我不确定在哪里调用 getInstance() 来初始化静态控制器字段
  • public Controller() {controller = this;}
【解决方案2】:

作为第一条规则,不要。一秒钟也不要。仅当您确实必须这样做时,因为没有保证这将可靠地工作,因为在调用此方法时无法正确初始化所有内容。相反,尝试将您的 util 重新设计为 Spring 托管类。

如果你真的想要,放弃你的大部分代码,因为你试图在你的代码中过于聪明。使用这个黑客(是的,这是一个黑客恕我直言,如有必要应避免使用!)。

public class SpringUtil {

  private static final ApplicationContext ctx;

  SpringUtil(ApplicationContext ctx) {
    SpringUtil.ctx=ctx;
  }

  public static Controller getController() {
    return this.ctx.getBean(Controller.class);
  }

  public static ApplicationParameters getApplicationParameters() {
    return ctx.getBean(ApplicationParameters.class);
  }
}

然后清理你的控制器

@RestController
public class Controller {

    private static final Logger logger = LogManager.getLogger(Controller.class);

    @Autowired
    private ApplicationParameters applicationParameters;

    @GetMapping("/")
    public void init() {
        try {
            for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
                logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());

                Utils.concatenate("key1", "key2");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
} 

然后使用SpringUtil获取ApplicationParameters而不是控制器

public class Utils {

protected static final Logger logger = LogManager.getLogger(Utils.class);

    //Need to get the value of the property keys propKey1 & propKey2 and concat them.
    public static String concatenate(String propKey1, String propKey2) throws Exception {
        if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
            return SpringUtils.getApplicationParameters().getProperties().get(propKey1) + SpringUtils.getApplicationParameters().getProperties().get(propKey2)
        } else {
            logger.error("System Property is undefined." );
            return null;
        }
    }

但是,这是一个非常巧妙的方法,可能在 90% 的情况下都有效。还有一个设计缺陷/气味,因为你在课堂上做了很多吸气剂链接。因此,总而言之,您最好重构Utils 以利用常规方法调用和适当的设计技术。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多