@Autowired в статических классах

Это проект Spring MVC с Hibernate. Я пытаюсь создать класс Logger, который отвечает за ввод журналов в базу данных. Другие классы просто вызывают правильные методы с некоторыми атрибутами, и этот класс должен делать всю магию. По своей природе это должен быть класс со статическими методами, но это вызывает проблемы с автоподключением объекта dao.

public class StatisticLogger {
    @Autowired
    static Dao dao;
    public static void AddLoginEvent(LogStatisticBean user){
        //TODO code it god damn it
    }
    public static void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
        //TODO code it god damn it
    }
    public static void addErrorLog(Exception e, String page,  HashMap<String, Object> parameters){
        ExceptionLogBean elb=new ExceptionLogBean();
        elb.setStuntDescription(e);
        elb.setSourcePage(page);
        elb.setParameters(parameters);
        if(dao!=null){ //BUT DAO IS NULL
            dao.saveOrUpdateEntity(elb);
    }
}

Как сделать это правильно? Что мне делать, чтобы объект dao не стал нулевым? Я знаю, что могу передать его как параметр метода, но это не очень хорошо. Я предполагаю, что автосвязывание не может работать со статическими объектами, потому что они созданы слишком рано, а механизм автосвязывания еще не создан.


person T.G    schedule 09.07.2012    source источник


Ответы (4)


Вы не можете @Autowired статическое поле. Но есть хитрый навык, чтобы справиться с этим:

@Component
public class StatisticLogger {

  private static Dao dao;

  @Autowired
  private Dao dao0;

  @PostConstruct     
  private void initStaticDao () {
     dao = this.dao0;
  }

}

Одним словом, @Autowired поле экземпляра и присвойте значение статическому полю при построении вашего объекта. Кстати, объект StatisticLogger также должен управляться Spring.

person Weibo Li    schedule 18.02.2014
comment
Тип возвращаемого значения метода ДОЛЖЕН быть пустым. docs.oracle.com/javaee/5/api/javax/ аннотация/ - person Dušan Knežević; 12.06.2014
comment
Спустя долгое время после битвы я стал использовать это решение, которое по большей части работает. Но компания Sonar быстро предупредила меня об этом: Correctly updating a static field from a non-static method is tricky to get right and could easily lead to bugs if there are multiple class instances and/or multiple threads in play. Ideally, static fields are only updated from synchronized static methods. Я подумал, что стоит упомянуть об этом. - person MaxouMask; 19.01.2017
comment
В документации указано The method on which PostConstruct is applied MAY be public, protected, package private or private., поэтому я думаю, что initStaticDao() может быть private, просто чтобы никто не видел его при использовании завершения кода и т. д. - person spoko; 27.03.2017
comment
@spokoСпасибо за комментарий. - person Weibo Li; 15.08.2017
comment
@WeiboLi: - Я пытаюсь реализовать то же самое, я хотел выбрать значения из файла bootstrap.yml. Все @values настроены в классе CosmosConnection. Вот мой код static CosmosConnection cosmos= new CosmosConnection(); @Autowired private CosmosConnection tcosmos; @PostConstruct public void init() { SupplierGetResponseFeed.cosmos = tcosmos; } В том же классе у меня есть другой метод, из которого я вызываю cosmos.connectToDB(); - person Anand Deshmukh; 09.04.2018

Классическое автосвязывание, вероятно, не сработает, потому что статический класс не является компонентом Bean и, следовательно, не может управляться Spring. Есть способы обойти это, например, используя подход factory-method в XML или путем загрузки bean-компонентов из контекста Spring в статический блок инициализатора, но я бы предложил изменить ваш дизайн:

Не используйте статические методы, используйте сервисы, которые вы внедряете там, где они вам нужны. Если вы используете Spring, вы можете использовать его правильно. Внедрение зависимостей — это объектно-ориентированный метод, и он имеет смысл только в том случае, если вы действительно используете ООП.

person Sean Patrick Floyd    schedule 09.07.2012

Я знаю, что это старый вопрос, но просто хотел поделиться тем, что я сделал, решение @Weibo Li в порядке, но проблема, которую оно вызывает. Критическое предупреждение Sonar о назначении нестатической переменной статической переменной.

способ, которым я разрешил это без предупреждений сонара, следующий

  1. Я меняю StatisticLogger на класс singleton (больше не статический), как это

    public class StatisticLogger {
    private static StatisticLogger instance = null;
    private Dao dao;
    
    public static StatisticLogger getInstance() {
        if (instance == null) {
            instance = new StatisticLogger();
        }
        return instance;
    }
    
    protected StatisticLogger() {
    }
    
    public void setDao(Dao dao) {
        this.dao = dao;
    }
    public void AddLoginEvent(LogStatisticBean user){
        //TODO code it god damn it
    }
    public void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
        //TODO code it god damn it
    }
    public  void addErrorLog(Exception e, String page,  HashMap<String, Object> parameters){
        ExceptionLogBean elb=new ExceptionLogBean();
        elb.setStuntDescription(e);
        elb.setSourcePage(page);
        elb.setParameters(parameters);
        if(dao!=null){ 
            dao.saveOrUpdateEntity(elb);
    }
    }
    
  2. Я создал службу (или компонент), которая автоматически связывает нужную мне службу и устанавливает ее в классе синглтона. Это безопасно, поскольку весной он инициализирует все управляемые компоненты, прежде чем делать что-либо еще, и это означает, что метод PostConstruct ниже всегда вызывается до все может получить доступ к StatisticLogger примерно так

    @Component
    public class DaoSetterService {
    
    @Autowired
    private Dao dao0;
    
    @PostConstruct     
    private void setDaoValue () {
        StatisticLogger.getInstance().setDao(dao0);
    }
    
    }
    
  3. Вместо того, чтобы использовать StatisticLogger как статический класс, я просто использую его как StatisticLogger.getInstance(), и я могу получить доступ ко всем методам внутри него.

person Hani    schedule 01.11.2017
comment
Это отличный трюк. Но я думаю, что вопрошающий просит присвоить его статическому члену (в отличие от приватного дао дао ;) возможно для использования в статическом члене класса. Я думаю, что этим можно управлять, сделав его статическим (приватное статическое дао дао;) и вместо this.dao = dao можно просто dao = dao. Таким образом, к нему можно получить доступ внутри статического члена. Есть идеи?? - person PravyNandas; 30.04.2020
comment
Я думаю, поскольку это синглтон, все члены могут быть нестатическими, и в этом случае ваш метод отлично работает! Спасибо. - person PravyNandas; 30.04.2020

Вы можете передать DAO в StatisticLogger из того места, где вы его вызываете.

public static void AddLoginEvent(LogStatisticBean user, DAO dao){
    dao.callMethod();
}
person usertest    schedule 08.06.2020