Использование @Autowired вне компонента - PullRequest
0 голосов
/ 29 марта 2020

У меня есть служба, которую я хотел бы использовать в разных местах своего приложения:

@Service
public class ActionListMain  { /* .... */ }

Сначала я хочу использовать в контексте сущности:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "myTable")
public class myTable {
    @Autowired
    @Transient
    private static ActionListMain allActions;
    /* .... */
}

И я хочу также использовать его в других неаннотированных классах, например:

public class Action {
    @Autowired
    private ActionListMain actionListMain;
}

С другой стороны у меня есть StartupComponent, где он подключен, как и ожидалось:

@Component
public class StartupComponent {
    @Autowired
    private ActionListMain actionListMain;
}

Почему это равен NULL во всех других классах?

Ответы [ 2 ]

2 голосов
/ 29 марта 2020

Spring может автоматически связывать бины только с классами, управляемыми Spring. Так как классы Action и MyTable не управляются Spring, ActionListMain не может быть автоматически подключен туда.

Существует обходной путь (hacki sh), который включает создание bean-компонента, управляемого Spring, и автоподсоединение applicationContext. и затем получение бобов из stati c applicationContext.

@Component
public class SpringContext implements ApplicationContextAware {

    //Has to be static to have access from non-Spring-managed beans
    private static ApplicationContext context;

    public static <T extends Object> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }

    @Override
    // Not static
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringContext.context = context;
    }
}
1 голос
/ 29 марта 2020

Как упоминалось @ Janar , Spring может автоматически связывать бины только в классы, управляемые Spring.

Вы можете добавить эту хитрую реализацию, которая позволяет получить ActionListMain вне @Component .

Мы собираемся централизовать все инъекции бобов (Spring + Custom) в BeansManager.

PRO / CONT

  • Все новые сервисы будут автоматически созданы
  • Централизованные решения инициации Circular Dependencies in Spring
  • Вам нужно позвонить .getBean, чтобы инициировать вручную

Зависимости

<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.12</version>
</dependency>

Классы, которые реализуют Injectable, должны иметь конструктор без аргументов

Injectable интерфейсов для получения @Service / @Component экземпляров

public interface Injectable {

    /**
    * Initiate all beans dependencies.
    * @param manager Beans manager allows to get singleton instances
    */
    void init(BeansManager manager);
}

BeansManager class

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.reflections.Reflections;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class BeansManager {

    @Autowired
    private List<Injectable> injectables;

    /**
     * This method will make sure all the injectable classes will get the
     * BeansManager in its steady state, where it's class members are ready to be
     * set.
     */
    @PostConstruct
    protected void inject() throws Exception {

        // Init all Spring @Component classes
        for (Injectable injectableItem : injectables) {
            injectableItem.init(this);
        }

        //Setup your package to scan
        Reflections reflections = new Reflections("com.example.demo");
        Set<Class<? extends Injectable>> classes = reflections.getSubTypesOf(Injectable.class);

        for (Class<? extends Injectable> clazz : classes) {

            // Avoid calling twicely if clazz already initialized by Spring
            if (getBean(clazz, Boolean.FALSE) == null) {
                Method init = clazz.getDeclaredMethod("init", BeansManager.class);
                init.invoke(clazz.newInstance(), this);
            }

        }
    }

    /**
     * Get singleton from BeansManager.
     * 
     * @param <T>   Spring service / component class
     * @param clazz singleton class
     * @return Singleton / throws exception
     * @throws NoSuchBeanDefinitionException If bean not found (required=true)
     */
    public <T> T getBean(Class<T> clazz) {
        return getBean(clazz, Boolean.TRUE);
    }

    /**
     * Get singleton from BeansManager.
     * 
     * @param <T>      Component service / component class
     * @param clazz    singleton class
     * @param required If bean not found, it throw exception (true) or returns null
     *                 (false)
     * @return Singleton / null / throws exception
     * @throws NoSuchBeanDefinitionException If bean not found (required=true) 
     */
    public <T> T getBean(Class<T> clazz, boolean required) {
        Object bean = null;
        for (Injectable injectableItem : injectables) {
            if (clazz.isInstance(injectableItem)) {
                bean = clazz.cast(injectableItem);
                return clazz.cast(bean);
            }
        }

        if (required) {
            throw new NoSuchBeanDefinitionException(clazz);

        } else {
            return null;
        }
    }
}

Реализация Injectable для ваших классов

@Service
public class ActionListMain implements Injectable
...

public class MyTable implements Injectable
...

    @Override
    public void init(BeansManager manager) {
        allActions = manager.getBean(ActionListMain.class);
    }
...

public class Action implements Injectable

    private static ActionListMain actionListMain;

    @Override
    public void init(BeansManager manager) {
        actionListMain = manager.getBean(ActionListMain.class);
    }

@Component
public class StartupComponent implements Injectable
...
    @Override
    public void init(BeansManager manager) {
        actionListMain = manager.getBean(ActionListMain.class);     
    }  

Инициализация вашего приложения

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    ActionListMain actionListMain;

    @Autowired
    StartupComponent startupComponent;

    Action action   = new Action();
    MyTable myTable = new MyTable();

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        actionListMain.sayHello();
        startupComponent.sayHello();
        action.sayHello();
        myTable.sayHello();
    }
}

com.example.demo.ActionListMain: ready класс com.example.demo.StartupComponent com.example.demo. ActionListMain@d28c214 класс com.example.demo.Action com.example.demo. ActionListMain@d28c214 класс com.example.demo.MyTable com.example.demo. ActionListMain@d28c214

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...