how to instantiate spring data jpa repository without autowired - java

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
}

Related

Consider defining a bean of type 'com.example.conferencedemo.services.SessionService' in your configuration

I am trying to implement as of enterprise level, there they have folders like Repository,Service,ServiceImpl
In Services they have interface with method declaration
In ServiceImpl they have class implementing the interface of services
In Repository they have all Repository interfaces
BeanInjection is a class where we have all repositories and service classes and interfaces with
#Autowired annotation.
When I tried to implement "#Autowired" to service class getting this Error.
Tried this no help link
Tried this no help but getting loop error link
Controller.java
public class SessionController extends BeanInjectionService {
#GetMapping
public ResponseEntity<List<Session>> list(){
LOGGER.info("Request received to view the sessions");
List<Session> sessions = sessionService.findAll();
LOGGER.info("Successfully fetched all the sessions");
return new ResponseEntity<>(sessions, HttpStatus.OK);
}
SessionService.java(Interface)
public interface SessionService {
List<Session> findAll();
}
SessionServiceImpl.java(Class)
public class SessionServiceImpl extends BeanInjectionService implements SessionService {
#Override
public List<Session> findAll(){
return sessionRepository.findAll();
}
BeanInjectionService.java(Class)
public class BeanInjectionService {
#Autowired
public SessionRepository sessionRepository;
**// Error Showing here while starting application
// Consider defining a bean of type 'com.example.conferencedemo.services.SessionService' in your configuration.**
#Autowired
public SessionService sessionService;
#Autowired
public SpeakerRepository speakerRepository;
#Autowired
public SpeakerService speakerService;
}
SessionRepository.java(Interface)
public interface SessionRepository extends JpaRepository<Session,Long> {
}
Thanks in advance
I find using BeanInjectionService a little weird, but I'll answer around it.
Unless you add #Service on SessionServiceImpl, you can't autowire it.
Circular dependency - If you do step 1, it will create a circular dependency because SessionServiceImpl needs its superclass object(BeanInjectionService) to be created first. But BeanInjectionService cannot be created unless it finds an object of SessionServiceImpl.
To break the circular dependency, you have only one option. Don't extend BeanInjectionService. Rather, autowire SessionRepository directly into SessionServiceImpl.
#Service
public class SessionServiceImpl implements SessionService {
#Autowired
private SessionRepository sessionRepository;
#Override
public List<Session> findAll(){
return sessionRepository.findAll();
}
}

Spring Boot/Data: Generic service class for crud operations

Let's say I want to create a REST API which performs basic CRUD operations on several entities. For that I've created generic interface:
public interface CrudService<T>{
//generic CRUD methods
}
And its implementation for Foo entity:
#Entity
public class Foo {
}
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>{
}
#Service
#Transactional
public class FooCrudServiceImpl implements CrudService{
#Autowired
private FooRepository repository;
//CRUD methods implementation
}
#RestController
class FooController{
#Autowired
private CrudService<Foo> crudService;
//Controller methods
}
What I want to avoid now is creating service implementation for each entity with basically the same logic. So I tried to create a generic service class which can be called from multiple controllers(FooController, BarController etc.):
#Service
#Transactional
class GenericCrudServiceImpl<T> implements CrudService{
#Autowired
private JpaRepository<T, Long> repository;
//generic CRUD methods implementation
}
and pass that service class to each controller where the entity type would be specified. The problem is that there will be multiple repository beans that could be injected into GenericCrudServiceImpl (FooRepository, BarRepository etc.) and just by specifying the type of JpaRepository Spring still doesn't know which bean to inject. I don't want to call repository beans directly from controller classes to maintain seperation of responsibilities.
Additionally, for some reason this problem doesn't occur on controller level where I inject CrudService interface and Spring understands which bean should it choose, which messes with my whole understanding of dependency injection.
Is there a way to create such a generic service class? Other posts on stackoverflow didn't provide me with an answer.
Bonus question: what's the difference between using a #Qualifier annotation and injecting a specific implementation (in this example FooCrudServiceImpl instead of CrudService in controller class)? In both cases pointing to different use implementation requires changing one line of code.
What about that:
#Transactional
public class GenericCrudServiceImpl<T> implements CrudService{
private JpaRepository<T, Long> repository;
public GenericCrudServiceImpl(JpaRepository<T, Long> repository) {
this.repository = repository;
}
}
And Spring configuration:
#Configuration
public PersistanceConfiguration {
#Bean
public JpaRepository<Foo, Long> fooJpaRepository() {
...
}
#Bean
public JpaRepository<Foo, Long> booJpaRepository() {
...
}
#Bean
public CrudService<Foo> fooService(JpaRepository<Foo, Long> fooJpaRepository) {
return new GenericCrudServiceImpl(fooJpaRepository);
}
#Bean
public CrudService<Foo> booService(JpaRepository<Foo, Long> booJpaRepository) {
return new GenericCrudServiceImpl(booJpaRepository);
}
}
And Controller
#RestController
class FooController{
// Injection by bean name 'fooService'
#Autowired
private CrudService<Foo> fooService;
//Controller methods
}

Creating an object of an #Service bean, is it possible?

i have an restful application with all the #Service,#RestController,#Repository beans. and im autowiring required beans.
Now i want to use the #service class in another class that is not managed by spring, is that possible?
These 2 classes are also in 2 diffrent maven projects if that makes any diffrence
i have tried creating a new object , as expected to no awail.
i have also tried creating diffrent constructors also to no awail.
i have googled for some anwsers but havent found any so now i turn to you experts ;)
The class i want to use!
#Service
public class ProductService {
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository){
this.repository = repository;
}
}
the Restcontroller
#RestController
#RequestMapping(path = "/product")
public class ProductResource {
#Autowired
ProductService service;
}
Repo
public interface ProductRepository extends JpaRepository<Product,Long> {
}
Here is where i want to create the service.
public static void main(String[] args) {
String chromeDriver = args[0];
String method = args[1];
String domainName = args[2];
ProductService service = new ProductService();
System.setProperty("webdriver.chrome.driver", chromeDriver);
Runner runner = new Runner(method,domainName);
runner.run();
}
As far as I know, you can instantiate a Spring-managed class in a non-Spring-managed class as long as the Spring-managed class (in this case the service) doesn't contain any Spring dependencies in it, as those will remain null (non initialized) because they are never gonna be injected by Spring.
Your service looks good to me because the ProductRepository field is not #autowired, but in your code you're creating the ProductService like this:
ProductService service = new ProductService();
While this parameterless constructor doesn't exist in the ProductService class:
#Service
public class ProductService
{
ProductRepository repository;
#Autowired
public ProductService(ProductRepository repository)
{
this.repository = repository;
}
}
I think this is not possible (I mean, it technically is, but it is not going to work).
To have access to your service class your second application needs to have access to the same Spring Context.
This is because Spring Context in your base application defines necessary dependencies that your second application doesn't have access to. One of the examples for this is ProductRepository repository (which is a way Spring Boot app will talk with the database), have no Spring Context and thus information on DB location and connection URL.
In your case you need to replicate the class and mechanism to connect to DB with DB configuration in your second project.
There's a workaround to "inject" dependencies manually. Create a managed component that stores the reference to the ApplicationContext in a static variable, so that you can access from non-managed classes:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext applicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextProvider.applicationContext = applicationContext;
}
}
public class NoManagedClass {
private final ManagedClass bean;
public NoManagedClass() {
this.bean = ApplicationContextProvider.applicationContext().getBean(ManagedClass.class);
}
}
However, be aware that your non-managed objects should be created after the application context is set. You still have to start your Spring application in the main method so that the beans are initialized.

#Autowired dependency in custom Spring Data Repository is null

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

How to prevent Spring from injecting #Autowired references inside a mock?

I want to test a class using Spring + JUnit + Mockito but I don't manage to make it work properly.
Let's say my class references a Service:
#Controller
public class MyController
{
#Autowired
private MyService service;
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
And this Service references a Repository:
#Service
public class MyService {
#Autowired
private MyRepository repository;
public void whatever() {}
public void create() {
repository.save();
}
}
When testing the MyController class, I want the service to be mocked. The problem is: even when the service is mocked, Spring tries to inject the repository in the mock.
Here is what I did. Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {
#Autowired
private MyController myController;
#Test
public void testDoSomething() {
myController.doSomething();
}
}
Configuration class:
#Configuration
public class MyControllerTestConfiguration {
#Bean
public MyController myController() {
return new MyController();
}
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
And the error I get: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency
I tried to initialize the mock using Mockito's #InjectMocks annotation but this fails because the #PostConstruct method is called before the mocks injection, generating a NullPointerException.
And I cannot simply mock the repository because in real life that would make me mock A LOT of classes...
Can anyone help me on this?
Use constructor instead of field injection. That makes testing a lot easier.
#Service
public class MyService {
private final MyRepository repository;
#Autowired
public MyService(MyRepository repository) {
this.repository = repository;
}
public void whatever() {}
public void create() {
repository.save();
}
}
-
#Controller
public class MyController {
private final MyService service;
#Autowired
public MyController(MyService service) {
this.service = service;
}
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
This has several advantages:
You don't need Spring in your tests. This allows you to do proper unit tests. It also makes the test incredibly fast (from seconds to milliseconds).
You cannot accidentally create an instance of a class without its dependencies which would result in a NullPointerException.
As #NamshubWriter pointed out:
[The instance fields for the dependencies] can be final, so 1) they cannot be accidentally modified, and 2) any thread reading the field will read the same value.
Discard the #Configuration class and write a test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#Mock
private MyRepository repository;
#InjectMocks
private MyService service;
#Test
public void testDoSomething() {
MyController myController = new MyController(service);
myController.doSomething();
}
}
Use interfaces, especially if you use some kind of AOP (transactions, security, etc), i.e. you'll have interface MyService and class MyServiceImpl.
In configuration you'll have:
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
you should put the #InjectMocks annotation in your controller and #Mock in your service, look:
#Autowired
#InjectMocks
private MyController myController;
#Autowired
#Mock
private MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDoSomething() {
myController.doSomething();
}

Categories