I created a custom Spring Data repository like this
BaseRepositoryCustomImpl<T extends BaseEntity>
extends SimpleJpaRepository<T, Long>
implements BaseEntityRepository<T, Long> {
#Autowired
private MyCustomClass myCustomClass;
}
Note that MyCustomClass is defined in my config class as #Bean. Then use my custom repository by adding it to my config class like this
#Configuration
#Profile("jpa")
#EnableJpaRepositories(basePackages = {
"com.package.repository"
}, repositoryImplementationPostfix = "CustomImpl",
repositoryBaseClass = BaseRepositoryCustomImpl.class
)
Everything is working okay except for the myCustomClass, which is always null. How should I autowire MyCustomClass? It is auto-wiring properly if used in other classes like Controllers.
More generic problem is how to autowire instances for not spring managed types.
you can create BeanUtil class
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
and then use it the following way to get bean:
UserRepository userRepository = BeanUtil.getBean(UserRepository.class);
inspired by this
Related
Repository Class
#Repository
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
#Query("SELECT a From AllFile a where a.guid = :guid")
AllFile findByGuid(#Param("guid") String guid);
}
Service Class
#Service
#Data
public class PipelineService implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Autowired
AllFileRepository allFileRepository;
}
Creating object SimpleJpaRepository<AllFile, Long>
PipelineService pipelineService = new PipelineService();
void methodX() {
AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext)
ApplicationContextUtils.getApplicationContext();
AutowireCapableBeanFactory autowireCapableBeanFactory =
applicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(pipelineService);
JpaRepository<AllFile, Long> jpaRepository = (SimpleJpaRepository<AllFile, Long>)
AopProxyUtils.getSingletonTarget(pipelineService.getAllFileRepository());
// (AllFileRepository) jpaRepository casting causes ClassCastException
}
Exception
java.lang.ClassCastException: org.springframework.data.jpa.repository.support.SimpleJpaRepository cannot be cast to ....repository.AllFileRepository
How to get an object of AllFileRepository interface? where I can access findByGuid("");
UPDATE
I am able to get the object of SimpleJpaRepository<AllFile, Long> belonging to interface AllFileRepository which inturn extends JpaRepository
public void initialize() {
AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext) ApplicationContextUtils
.getApplicationContext();
EntityManager em = applicationContext.getBean(EntityManager.class);
AllFileRepository allFileRepository = new JpaRepositoryFactory(em).getRepository(AllFileRepository.class);
allFileRepository.findByGuid(""); // Method is accessible here but the object is proxy
SimpleJpaRepository<AllFile, Long> simpleJpaRepository = (SimpleJpaRepository<AllFile, Long>) (AopProxyUtils
.getSingletonTarget(allFileRepository)); // But proxy resolution yeilds SimpleJpaRepository cannot
// explicitly cast to AllFileRepository
((AllFileRepository) simpleJpaRepository).findByGuid(""); // class
// org.springframework.data.jpa.repository.support.SimpleJpaRepository
// cannot be cast to class
// com.example.spingaoprepository.repository.AllFileRepository
// (org.springframework.data.jpa.repository.support.SimpleJpaRepository
// and
// com.example.spingaoprepository.repository.AllFileRepository
// are in unnamed module of loader 'app'
}
Here is a link to sample program
There is no use in trying to cast incompatible types into each other. This is not a Spring problem, it is Java basics. I already told you how to solve that in Spring, too: You need to provide a class actually implementing AllFileRepository and make sure Spring Data JPA uses it as a repository instead of the interface. In order to do that, you need to
change the interface annotation from #Repository to #NoRepositoryBean,
create class #Repository AllFileRepositoryImpl and there provide an implementation of AllFile findByGuid(String guid) doing something meaningful.
Then you can easily cast the way you want because your proxy's target object will be an AllFileRepositoryImpl instance.
I sent you a pull request which you just need to accept. The classes changed in the relevant commit #5705cbb look as follows:
package com.example.spingaoprepository.repository;
import com.example.spingaoprepository.model.AllFile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.NoRepositoryBean;
#NoRepositoryBean
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
#Query("SELECT a From AllFile a where a.guid = :guid")
AllFile findByGuid(#Param("guid") String guid);
}
package com.example.spingaoprepository.repository;
import com.example.spingaoprepository.model.AllFile;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
#Repository
public class AllFileRepositoryImpl extends SimpleJpaRepository<AllFile, Long> implements AllFileRepository {
public AllFileRepositoryImpl(EntityManager em) {
super(AllFile.class, em);
}
#Override
public AllFile findByGuid(String guid) {
System.out.println("Finding AllFile by GUID " + guid);
return null;
}
}
package com.example.spingaoprepository.serializable;
import com.example.spingaoprepository.repository.AllFileRepository;
import com.example.spingaoprepository.service.PipelineService;
import com.example.spingaoprepository.utils.ApplicationContextUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
public class PipelineConfigurer {
private ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext();
private PipelineService pipelineService = applicationContext.getBean(PipelineService.class);
public void initialize() {
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(pipelineService);
AllFileRepository allFileRepository = (AllFileRepository) AopProxyUtils
.getSingletonTarget(pipelineService.getAllFileRepository());
allFileRepository.findByGuid("XY-123");
}
}
You are instantiating your PipelineService using the new keyword and then manually getting the Spring context in your methodX().
In principle, of course you 'could' do it all manually without Spring. But given both your repository and service are Spring beans, then it's better to let Spring handle the bean lifecyle. It's certainly easier anyway.
As PipelineService is a Spring bean, you should really make any class that uses it Spring aware too. So just annotate the class that with a Spring annotation like #Component, #Service, or perhaps #RestController if it's a controller.
If you insist on creating the bean manually then try -
ApplicationContext context = new AnnotationConfigApplicationContext();
PipelineService pipelineService = context.getBean(PipelineService.class);
Using #Autowired you are injecting an object implementing AllFileRepository. This singleton was created at startup hence you can just use is with allFileRepository.findByGuid("");
In a SPRING BOOT application i have a class as below
#Service
public class XYZ{
}
I want to use above in other class ABC
public class ABC{
#Autowired
private XYZ xyx;
}
It throws error that XYZ could not be found. I already have #SpringBootApplication in the class where the main method is written. Hence, this will automatically enable #ComponentScan on the package.My understanding is , since XYZ has been annoted with #service, spring scans and creates and registers that bean. How can i access that bean in other class without using xml configuration ?
Well your ABC class also need to have the #Service or #Component annotation. Otherwise, you will get a warning with the following message.
Autowired memebers must be defined in valid Spring bean (#Component|#Service|...).
#Service
public class ABC {
#Autowired
private XYZ xyx;
}
Your class ABC is not in spring boot context, that is reason you can't get a bean. you might get it the following way:
Create an ApplicationContextProvider
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context = applicationContext;
}
}
After you need to call the following way:
public class ABC {
public void method() {
XYZ xyz = ApplicationContextProvider.getApplicationContext().getBean(XYZ.class);
}
}
If you don't want to configurate #ComponentScan. you need to put the Class XYZ and ABC in the same level directory with Spring boot application runner class
your class ABC should also be a spring-managed
you can do this by making it a Component
#Component
public class ABC{
#Autowired
private XYZ xyx;
}
or provide it as a Bean
#Configuration
public SomeConfig{
#Bean
public ABC abc(){
return new ABC();
}
}
Use #ComponentScan("<your root package>") in you Spring boot application runner class so that it inspects all the components and create the bean which can be autowired. Also Calling class should be a component annotated with #Componenent. If you create object of ABC as new ABC() then
ABC abc = new ABC();
ContextProvider.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(abc); //Autowiring dependencies
//Then use the abc object which will have instance of XYZ populated.
ContextProvider implementation
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* #author dpoddar
*
*/
#Component("ContextProvider")
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ContextProvider implements ApplicationContextAware {
#Autowired
private static ApplicationContext CONTEXT;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CONTEXT = applicationContext;
}
/**
* #return the cONTEXT
*/
public static ApplicationContext getApplicationContext() {
return CONTEXT;
}
/**
* Get a Spring bean by type.
**/
public static <T> T getBean(Class<T> beanClass) {
return CONTEXT.getBean(beanClass);
}
/**
* Get a Spring bean by type.
**/
public static <T> T getBean(String beanName,Class<T> beanClass) {
return CONTEXT.getBean(beanName, beanClass);
}
/**
* Get a Spring bean by name.
**/
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
}
I have my repository interface as
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface MyRepository extends JpaRepository<Tokens, Long>{
}
and the service impl file in where I want to use above repository is not a
#Component or #Service because its object is created using new. So it is not allowing autowiring or injecting the repository in my service impl class.
Is there any other way around to implement in such case?
There are a couple of options available.
First of all, you can use it as a normal dependency and use a factory bean to instantiate your object.
#Component
public class MyFactoryBean {
private Repository repository;
#Autowired
public MyFactoryBean(Repository repository) {
this.repository = repository;
}
public Instance getInstance(Parameter parameter) {
return new Instance(repository, parameter);
}
}
If your parameter does not change at runtime, you can provide a bean in your configuration:
#Configuration
public class MyConfiguration {
private Repository repository;
#Autowired
public MyConfiguration(Repository repository) {
this.repository = repository;
}
#Bean
public Instance instance(){
Parameter parameter = ... // value in a config file or static value
return new Instance(repository, parameter);
}
}
Also, you can use the AutowireCapableBeanFactory to inject dependencies as normal for objects not managed by Spring. Remember that you need to use setter injection tough.
private AutowireCapableBeanFactory beanFactory;
#Autowired
public MyFactoryBean(AutowireCapableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void doStuff() {
MyService service = new MyService();
beanFactory.autowireBean(service); // dependencies will be autowired
}
I'm trying to write an integration test but I'm having trouble with autowiring the repository in the test.
I receive this exception:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.observer.media.repository.ArticleRepository]: Specified class is an interface.
Edit:
I added PersistenceConfig.class with #EnableJpaRepositories, code below. This results in Exception org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
I also tried to add Application.class in #SpringBootTest(classes = {} in an catch all attempt but that throws Error creating bean with name 'articleRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.observer.media.model.Article
ScraperRunnerIntegrationTest (the config classes only contain beans of domain classes):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {
ApplicationConfig.class,
PersistenceConfig.class,
DeMorgenTestConfig.class,
Article.class,
ScraperRunner.class,
DeMorgenWebsiteScraper.class,
ArticleService.class,
DeMorgenPageScraper.class,
JsoupParser.class,
DeMorgenArticleScraper.class,
GenericArticleScraper.class,
ImageMetaScraper.class,
ArticlePrinter.class,
ArticleRepository.class
})
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}
Repository:
import org.observer.media.model.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
List<Article> findAll();
Article findByHash(String hash);
Article findByHeadline(String headline);
List<Article> findArticleByHeadlineAndCompanyName(String headline, String companyName);
#Query("SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END FROM Article a WHERE a.hash = :hash")
boolean existsByHash(#Param("hash") String hash);
}
PersistenceConfig.class:
package org.observer.media.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#Configuration
#EnableJpaRepositories("org.observer.media.repository")
public class PersistenceConfig {
}
You need to provide only the classes, annotated as #Configuration to #SpringBootTest.
I have modified the original example from here to use the #SpringBootTest annotation.
So the following configuration works:
#Configuration
#ComponentScan("hello")
public class AppConfig {
}
Note the #ComponentScan annotation.
And then in my test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={AppConfig.class})
public class CustomerRepositoryTests {
#Autowired
private CustomerRepository customerRepository;
}
And it did a trick. You can try to do the same in your example.
I had the same exception org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Specified class is an interface on my integration test.
I had resolved without using a PersistenceConfig.class but changed only the annotaion used in my integration class that was wrong. I have used the following annotation:
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
because I didn't want use a in-memory database.
Your code could be the following:
#ComponentScan(basePackages = {"org.observer"})
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}
Maybe you can try #AutoConfigureDataJpa or #AutoConfigureXxx annotations.
I've got simple structure project:
com.database.mongodb.config.MongoConfig - MongoDB configuration used in #Import
com.database.mongodb.repository.ParameterRepository - interface which extends MongoRepository
com.server.main.MainClass - Main class of my program.
I just want to use my ParameterRepository everywhere I need it, for instance, in a custom class with some tasks in package com.server.task.CustomClass. I tried:
#Autowired
private ParameterRepository repo;
But it returns NullPointerException. I tried to create abstract class Task with annotation #Component, but it also doesn't work.
How can I make it possible?
My implementations:
MongoConfig.class
#Configuration
#EnableMongoRepositories(basePackages = {"com.database.mongodb.repository"})
public class MongoConfig extends AbstractMongoConfiguration {
#Override
#Bean
public Mongo mongo() throws Exception {
return new MongoClient("localhost:27017");
}
#Override
protected String getDatabaseName() {
return "mydb";
}
}
ParameterRepository.class
#Repository
public interface ParameterRepository extends MongoRepository<Parameter, Integer> {
}
MainClass.class
#Import(MongoConfig.class)
#SpringBootApplication
public class SparkServer implements CommandLineRunner {
#Autowired
private WeatherParameterRepo weatherParameterRepo;
...
You should move your ParameterRepository to the com.server.main package (or a sub-package).
What you could also do is add #ComponentScan({"com.database.mongodb.repository", "com.server.main"}) to your MainClass class.