@Autowired in static classes
Asked Answered
A

5

37

This is an Spring MVC project with Hibernate. I'm, trying to make a Logger class that, is responsible for inputting logs into database. Other classes just call proper methods with some attributes and this class should do all magic. By nature it should be a class with static methods, but that causes problems with autowiring dao object.

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);
    }
}

How to make it right? What should I do not to make dao object null? I know that I could pass it as a method parameter, but that isn't very good. I'm guessing that autowired can't work on static objects, because they are created to early to autowiring mechanism isn't created yet.

Aristides answered 9/7, 2012 at 9:51 Comment(0)
E
79

You can't @Autowired a static field. But there is a tricky skill to deal with this:

@Component
public class StatisticLogger {

  private static Dao dao;

  @Autowired
  private Dao dao0;

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

}

In one word, @Autowired a instance field, and assign the value to the static filed when your object is constructed. BTW, the StatisticLogger object must be managed by Spring as well.

Expire answered 18/2, 2014 at 11:14 Comment(5)
The return type of the method MUST be void. docs.oracle.com/javaee/5/api/javax/annotation/…Sjambok
Long after the battle, I've come to use this solution which works for the most part. But the company Sonar quickly gave me a warning about it : 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. I thought it'd worthy to mention it.Capapie
Documentation says The method on which PostConstruct is applied MAY be public, protected, package private or private. so I think initStaticDao() could be private just so nobody has to see it when using code completion etc.Clementinaclementine
@spokoThanks for the comment.Expire
@WeiboLi :- I try to Implement the same thing,I wanted to pick the values from bootstrap.yml file. All the @values are configured in CosmosConnection class. Here is my code static CosmosConnection cosmos= new CosmosConnection(); @Autowired private CosmosConnection tcosmos; @PostConstruct public void init() { SupplierGetResponseFeed.cosmos = tcosmos; } In the same class I have another method from where I am calling cosmos.connectToDB();Style
H
22

Classical autowiring probably won't work, because a static class is not a Bean and hence can't be managed by Spring. There are ways around this, for example by using the factory-method aproach in XML, or by loading the beans from a Spring context in a static initializer block, but what I'd suggest is to change your design:

Don't use static methods, use services that you inject where you need them. If you use Spring, you might as well use it correctly. Dependency Injection is an Object Oriented technique, and it only makes sense if you actually embrace OOP.

Halftimbered answered 9/7, 2012 at 9:57 Comment(0)
P
4

I know this is an old question but just wanted to share what I did, the solution by @Weibo Li is ok but the problem it raises Sonar Critical alert about assigning non static variable to a static variable

the way i resolved it with no sonar alerts is the following

  1. I change the StatisticLogger to singlton class (no longer static) like this

    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. I created a service(or Component) that autowire the service that i want and set it in the singlton class This is safe since in spring it will initialize all the managed beans before doing anything else and that mean the PostConstruct method below is always called before anything can access the StatisticLogger something like this

    @Component
    public class DaoSetterService {
    
    @Autowired
    private Dao dao0;
    
    @PostConstruct     
    private void setDaoValue () {
        StatisticLogger.getInstance().setDao(dao0);
    }
    
    }
    
  3. Instead of using StatisticLogger as static class I just use it as StatisticLogger.getInstance() and i can access all the methods inside it

Peoples answered 1/11, 2017 at 20:59 Comment(2)
It's a great trick. But I think the questioner asking to assign it to a static member (unlike private Dao dao;) perhaps to use in a static member of the class. I think it can be managed by making it statick (private static Dao dao;) and instaed of this.dao = dao, it can simply be dao = dao. Such that it can be accessed inside a static member. Any thoughts??Phrasal
I guess since it's a singleton all members can be non-static in which case your method works great !! Thanks.Phrasal
R
0

You can pass the DAO to StatisticLogger from where you call it.

public static void AddLoginEvent(LogStatisticBean user, DAO dao){
    dao.callMethod();
}
Reber answered 8/6, 2020 at 14:22 Comment(0)
C
0

It might be too late to put an answer to this question, especially when a question is already having an accepted answer. But it might help others in case they face the same issue.

inside the StatisticLogger class create an instance of the Dao service.

public static Dao daoService = new Dao();

then, auto-wire the service instance through the constructor of the StatisticLogger class.

@Autowired
public functionName(Dao daoService0) {
    this.daoService = daoService0;
}


//use this service as usual in static class
daoService.fun();

I think this is the simplest solution for the problem.

Coagulase answered 26/1, 2023 at 7:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.