简体   繁体   中英

How to autowire beans inside the implementation of a NoRepositoryBean

Using Spring Boot / Spring Data, I added a custom functionnality to all my repositories. This is a snipped of what I did :

So I have my repository Interface :

@NoRepositoryBean
public interface RepositoryBase<T, ID extends Serializable> extends JpaRepository<T, ID> {

And its implementation

public class RepositoryBaseImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements RepositoryBase<T, ID> {

    @Autowired 
    MessageLocale messageLocale; // <- this is a classic spring bean which is not injected in this spot (always null)

    private EntityManager entityManager;

    public RepositoryBaseImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    //... My custom methods here

and my config :

@Configuration
@EnableJpaRepositories(basePackages = "my.base.pkg", repositoryBaseClass = RepositoryBaseImpl.class)
public class RepositoryConfig {
}

My custom methods works correctly but I need to inject messageLocal

Autowired don't work inside RepositoryBaseImpl (I think that is because it is not a bean)

I cannot add @Repository on RepositoryBaseImpl because I use @NoRepositoryBean on its parent interface

So is there a way to inject messageLocale ?

Based on @Prabhakar D comment, I will post my answer based on my need (using @EnableJpaRepositories besides @EnableMongoRepositories and some other little modifications)

In the @EnableJpaRepositories annotation, add the repositoryFactoryBeanClass :

@EnableJpaRepositories(basePackages = "my.base.pkg", repositoryBaseClass = RepositoryBaseImpl.class, repositoryFactoryBeanClass = MyRepositoryFactoryBean.class)

The key thing that is you can use @Autowire inside a repositoryFactoryBeanClass

Create a repositoryFactoryBeanClass and autowire your beans in it. This is a spring bean that create a custom JpaRepositoryFactory in the overrided method createRepositoryFactory :

public class MyRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {

    @Autowired
    private MessageLocale messageLocale;

    public MyRepositoryFactoryBean(Class repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new MyJpaRepositoryFactory(entityManager, messageLocale);
    }
}

Now, create the custom factory (MyJpaRepositoryFactory) and use the overrided method getTargetRepository to create an instance of your base repository (RepositoryBaseImpl). Here you can inject your bean in its constructor :

public class MyJpaRepositoryFactory extends JpaRepositoryFactory {

    private EntityManager entityManager;
    private MessageLocale messageLocale;

    public MyJpaRepositoryFactory(EntityManager entityManager, MessageLocale messageLocale) {
        super(entityManager);
        this.entityManager = entityManager;
        this.messageLocale = messageLocale;
    }

    //****************** Edit ********************
    //starting from spring boot 2.1.0, getTargetRepository(RepositoryInformation information) was made final. So you can't override it anymore. You will need to override getTargetRepository(RepositoryInformation information, EntityManager entityManager)
    //@Override
    //protected Object getTargetRepository(RepositoryInformation information) {
    //    return new RepositoryBaseImpl(getEntityInformation(information.getDomainType()), entityManager, messageLocale);
    //}
    @Override
    protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
        return new RepositoryBaseImpl(getEntityInformation(information.getDomainType()), entityManager, messageLocale);
    }
    //****************** End Edit ******************** 
}

Now, just modify the constructer of your RepositoryBaseImpl so it can accept the required Bean :

public class RepositoryBaseImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements RepositoryBase<T, ID> {

    private MessageLocale messageLocale;
    private EntityManager entityManager;

    //if you are using IntelliJ, it can show you an error saying "Could not autowire. No beans of JpaEntityInformation". It is just a bug in IntelliJ
    public RepositoryBaseImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager, MessageLocale messageLocale) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.messageLocale = messageLocale;
    }

    //... My custom methods here

Now that your messageLocal is injected in your BaseRepositoryImpl, you can use it in your custom methods without having to pass it in parameters

Hope that this will help someone

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM