Spring Boot Autowiring From Another Module - java

I am trying to establish connection between 3 modules in my project. When I try to reach my object with #Autowired error shows up. I'll explain my scenario a little bit.
MODULES
All of these modules have been connected inside of pom.xml. Lets talk about my problem.
C -> ROUTE.JAVA
.
.
.
#Autowired
public CommuniticationRepository;
#Autowired
public Core core;
.
.
.
B -> CORE
public class Core {
private int id;
private String name;
private Date date;
public Core(int id, String name, Date date) {
this.id = id;
this.name = name;
this.date = date;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
ERROR
Field communicationRepositoryin com.demo.xyz.A.RestControllers.Route required
a bean of type 'com.demo.xyz.A.CommunicationRepository' that could not be
found.
Action:
Consider defining a bean of type 'com.demo.xyz.A.CommunicationRepository' in
your configuration.
A - > REPOSITORY.JAVA
#Component
#Repository
public interface CommunicationRepository extends CrudRepository<Communication, Date> {
List<Communication> findByDate(Date date);
void countByDate(Date date);
}

You should remove #Component and #Repository from CommunicationRepository if it is a spring data JPA repository.
You should define configurations in modules A and B.
#Configuration
#EnableJpaRepositories(basePackages ={"com.demo.xyz.A"})
#EntityScan(basePackages = {"com.demo.xyz.A"})
#ComponentScan(basePackages = {"com.demo.xyz.A"})
public class ConfigA {
}
// If you have no spring managed beans in B this is not needed
// If Core should be a spring managed bean, add #Component on top of it
#Configuration
#ComponentScan(basePackages = {"com.demo.xyz.B"})
public class ConfigB {
}
Then, in C, where you bootstrap the application, you should import the configurations for module A and module B. At this point, any beans from A and B will be available for autowiring in C.
#Configuration
#Import(value = {ConfigA.class, ConfigB.class})
public class ConfigC {
}

Basically if you want to use #Autowired annotation on top of any attribute and use it, Obviously there should be an initialized bean in the spring context to Autowire it to your usages. So here your problem is in your spring context, there is no such bean to autowire.
So the solution is you need to have those beans inside your spring context, there are multiple ways to get this done,
The classes that you need beans auto initialized inside the spring context as #Component
Ex :
#Component
public class Car{
or you can manually have a configuration file which returns such beans
Ex :
#Bean
public Car setCarBean(){
return new Car();
}
And this bean returning should be inside a #Configuration class.
please refer
Then if you are really sure that you have done with this, then correct #ComponentScan should work
EDIT
#SpringBootApplication
#ComponentScan(basePackages = { "com.demo.xyz.A", "com.demo.xyz.B"})
public class Application {

Try to add scanBasePackages in the Application class.
The default scan is for the package in which the Application class.
#SpringBootApplication(scanBasePackages = "com.demo.xyz")
public class Application {...}

Related

ConfigurationProperties for final fields doesn't work

I need to set data from application.yml file to my config class but when I trying to do it I get an error:
TestConfig is annotated with #ConstructorBinding but it is defined as a regular bean which caused dependency injection to fail.
My application.yml file looks like the following:
test:
app:
id: app_id
My TestConfig class looks like this:
#Configuration
#ConfigurationProperties(prefix = "test.app")
#ConstructorBinding
public class TestConfig {
private final String id;
public TestConfig(String id) {
this.id = id;
}
}
I'm trying to do like this but it doesn't work for me.
Where I was wrong?
According to :
https://www.baeldung.com/configuration-properties-in-spring-boot#immutable-configurationproperties-binding
You will need to remove the #Configuration from your TestConfig.class.
Furthermore, it's important to emphasize that to use the constructor binding, we need to explicitly enable our configuration class either with #EnableConfigurationProperties or with #ConfigurationPropertiesScan.
--------- Edited -----
#ConfigurationProperties(prefix = "test.app")
#ConstructorBinding
public class TestConfig {
private final int id;
public TestConfig (int id)
this.id = id
}
public String getId() {
return id;
}
}
#SpringBootApplication
#ConfigurationPropertiesScan
public class YourApp{
public static void main(String[] args) {
SpringApplication.run(YourApp.class, args);
}
}

Is it necessary to use #Configuration while working with spring annotations

I am working with a simple spring application to check #Configuration and #Bean(java based configuartion only),The program is working with both #Configuration and without it.So is it necessary to have it.
Here is my code,
Student.java
package com.cg.spring;
public class Student {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
Faculty.java
package com.cg.spring;
public class Faculty {
private int empId;
private String name;
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Faculty [empId=" + empId + ", name=" + name + "]";
}
}
MyConfig.java
package com.cg.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MyConfig {
#Bean
public Student stu()
{
return new Student();
}
#Bean
public Faculty fac()
{
return new Faculty();
}}
Client.java
package com.cg.spring;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Client {
public static void main(String[] args) {
ApplicationContext context=new
AnnotationConfigApplicationContext(MyConfig.class);
Student stu=(Student)context.getBean(Student.class);
Faculty fac=(Faculty)context.getBean(Faculty.class);
stu.setName("ajay");
stu.setId(101);
System.out.println(stu);
fac.setEmpId(202);
fac.setName("Kiran");
System.out.println(fac);
}}
The output is same with or without the #Configuration
Student [id=101, name=ajay]
Faculty [empId=202, name=Kiran]
Even tried with autowiring,it is also working without #Configuration
Student.java
package com.cg.spring;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
private int id;
private String name;
#Autowired
private Faculty faculty;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Faculty getFaculty() {
return faculty;
}
public void setFaculty(Faculty faculty) {
this.faculty = faculty;
}
#Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}}
Client.java
package com.cg.spring;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Client {
public static void main(String[] args) {
ApplicationContext context=new
AnnotationConfigApplicationContext(MyConfig.class);
Student stu=(Student)context.getBean(Student.class);
Faculty fac=(Faculty)context.getBean(Faculty.class);
stu.setName("ajay");
stu.setId(101);
System.out.println(stu);
fac.setEmpId(202);
fac.setName("Kiran");
System.out.println(fac);
stu.setFaculty(fac);
System.out.println(stu.getFaculty());
}}
When using Java based configuration with Spring you basically have 2 options (as you already noticed). You have the option to annotate a class with #Configuration and have all the #Bean annotated methods available as beans. However you can also do this without the #Configuration annotation. The latter is called the so called lite mode.
When using #Configuration classes the beans defined in there are regular Spring beans and when calling one method from another this will always result in the same instance of a bean. Spring detects the #Configuration classes and treats them in a very special way (it will create a proxy for those classes).
When using lite-mode the #Bean methods are basically nothing more than factory methods, although they participate in (part of) the lifecycle of Spring Beans. When calling them each call will get you a new bean. Which means that, inter bean dependencies, will get you new instances each time the method gets called.
Also I realised that if #Configuration is used defining dependencies as private is not permitted.
#Configuration
public class ProgrammingConfig {
// define bean for the fortune service
#Bean
private FortuneService javaFortuneService() {
return new JavaFortuneService();
};
// define bean for java coach and inject its dependencies
#Bean
public Coach javaCoach() {
return new JavaCoach(javaFortuneService());
}
}
will yield
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: #Bean method 'javaFortuneService' must not be private or final; change the method's modifiers to continue
Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: #Bean method 'javaFortuneService' must not be private or final; change the method's modifiers to continue
whilst without the #Configuration annotation the program works even with a private dependency
#Configuration is required because it indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime. #Configuration annotation used for many other reasons. For example, #Configuration is meta-annotated with #Component, therefore #Configuration classes are candidates for component scanning (typically using Spring XML's <context:component-scan/> element) and therefore may also take advantage of #Autowired/#Inject like any regular #Component. More details references are available to the following Link -
Annotation Type Configuration
#Bean annotation on methods that are declared in classes not annotated with #Configuration is known as “lite” mode. In the “lite” mode, #Bean methods cannot declare inter-bean dependencies. Ideally, one #Bean method should not invoke another #Bean method in ‘lite’ mode.
Spring recommends that #Bean methods declared within #Configuration classes for full configuration. This kind of full mode can prevent many bugs.

NullPointerException on CrudRepository

I have created a repository but when I call my repository it gives a NullPointerException everytime. Can someone help me figure out why?
My repository
#Repository
public interface WorkflowObjectRepository extends CrudRepository<WorkflowObject, String> {
#Override
WorkflowObject findOne(String id);
#Override
void delete(WorkflowObject workflowObject);
#Override
void delete(String id);
#Override
WorkflowObject save(WorkflowObject workflowObject);
}
My Object
#Data
#Entity
#Table(name = "workflowobject")
public class WorkflowObject {
#GeneratedValue
#Column(name="id")
private String id;
#Column(name = "state_name")
private String stateName;
}
My test
public class Test {
#Autowired
static WorkflowObjectRepository subject;
public static void main(String[] args) {
final WorkflowObject obj = new WorkflowObject();
obj.setId("maha");
obj.setStateName("test");
subject.findOne("maha");
}
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/vtr?
autoReconnect=true
spring.datasource.username=vtr
spring.datasource.password=vtr
The problem is you are trying to autowire a static data member
#Autowired
static WorkflowObjectRepository subject;
What happens in your case is static is getting initialized before the bean so you are autowiring on null, just remove the static and deal with it as instance variable.
repositories are singletones so no point of making them static
In order to work properly with #Autowired you need to keep in mind that spring scans annotations to allow automatically classes load.
If you need to #Autowired some class, the class Test needs to have some annotation, that tells to Spring Boot to find it.
Your Test class need to have #Component, #Service, #Controller, #Repository, etc. Otherwise #Autowired will not work because Spring Boot not know your class and will not process it.
You can find some explanation here

Spring boot custom auto configuration for entities

I'm working on a Spring Boot project which consists of multiple smaller projects. They all share a common project which consists of helper classes and such. On this common project I'm trying to create a Service, Repository, Entity and a Controller which could be shared and selectively enabled along of all other projects (debug endpoints with persisted data, each project has a separate database).
I'm thinking the ideal solution for this is to create a configuration bean which should be defined in order to enable these features or something along those lines.
At the moment I have this setup. Common entity:
#MappedSuperclass
public class SomeEntity {
#Id
#GeneratedValue
protected Long id;
#Column(unique = true)
protected String name;
public SomeEntity() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
The service defining common methods:
public abstract class SomeEntityService<T extends SomeEntity> {
private final SomeRepository<T> someRepository;
public SomeEntityService(SomeRepository<T> someRepository) {
this.someRepository = someRepository;
}
public T getSomeEntity(String name) {
return someRepository.findByName(name);
}
public List<T> getSomeEntities() {
return someRepository.findAll();
}
public abstract void init();
}
Common controller:
#RestController
#RequestMapping(value = "/entities")
#ConditionalOnBean(value = SomeEntityService.class)
public class SomeController<T extends SomeEntity> {
private final SomeEntityService<T> someEntityService;
#Autowired
public SomeController(SomeEntityService<T> someEntityService) {
this.someEntityService = someEntityService;
}
#RequestMapping(method = RequestMethod.GET)
public List<T> getSomeEntities() {
return someEntityService.getSomeEntities();
}
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public T getSomeEntity(#PathVariable String name) {
return someEntityService.getSomeEntity(name);
}
}
Common repository:
#NoRepositoryBean
public interface SomeRepository<T extends SomeEntity> extends JpaRepository<T, Long>, JpaSpecificationExecutor<T> {
T findByName(String name);
}
The full project can be found here.
Now in this example, if I implement SomeService, SomeEntity and SomeRepository, the controller bean gets created (notice the #ConditionalOnBean annotation on the controller) and everything works fine. However I do not want to redefine the entity and repository as all the needed implementation is already there, however I cannot find any documentation on how to disable the creation of these beans based on some conditions. So the questions would be:
How can I disable the creation of specific #Entity annotated classes?
How can I do the same for #Repository annotated classes?
Is there a better way of doing this sort of thing?
Edit:
A more concrete question would be - how could I exclude selected entities from scanning based on some condition, is it possible to do this in spring?
For example, create a set of entities only is some specific bean is created or some property in application.properties file is defined.
What about using #ConditionalOnProperty and configuring it through properties files?
This article has the following to say:
In Spring Boot, you can use the #ConditionalOnProperty annotation to enable or disable a particular bean based on the presence of a property. This is very useful if you want to provide optional features to your microservice.

Hibernate naming strategy per entity

I have one global naming strategy but for a few entities I want to use a different one. Is it possible in jpa or hibernate?
clarification: i don't want to use #Table(name="xxx") nor #Column(name="xxx"). i'm asking about naming strategy component (described for example here: Hibernate naming strategy). that's a component that infer the column and table names for you
I don't see a way in the Hibernate source code. The EntityBinder is coming up with names using ObjectNameNormalizer.NamingStrategyHelper, which gets the naming strategy from either Configuration.namingStrategy (the global one) or from a complex path which goes through MetadataImpl and lands nowhere (no usages).
So you're likely stuck with overriding field names manually. I don't even see an obvious way to get context about the field, so I think even a split-brain naming strategy looks like it's out of the question.
Update: After seeing #anthony-accioly's answer, I thought I that last sentence may have been wrong. So I tested it as follows
package internal.sandbox.domain;
#Entity
public class SomeEntity {
private String id;
private String someField;
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSomeField() {
return someField;
}
public void setSomeField(String someField) {
this.someField = someField;
}
}
with a JpaConfiguration as follows
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories("internal.sandbox.dao")
#Import(DataSourceConfiguration.class)
public class JpaConfiguration {
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(DataSource dataSource) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL82Dialect");
vendorAdapter.setDatabase(Database.POSTGRESQL);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("internal.sandbox"); // note, no ".domain"
factory.setDataSource(dataSource);
Properties properties = new Properties();
properties.setProperty("hibernate.cache.use_second_level_cache", "false");
properties.setProperty("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
factory.setJpaProperties(properties);
return factory;
}
...
a Spring Data DAO as follows
public interface SomeEntityDao extends CrudRepository<SomeEntity, String> {
}
and an integration test as follows
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ApplicationConfiguration.class, JpaConfiguration.class})
public class SomeEntityDaoIntegrationTests {
#Autowired
private SomeEntityDao someEntityDao;
#Test
public void testSave() {
SomeEntity someEntity = new SomeEntity();
someEntity.setId("foo");
someEntity.setSomeField("bar");
this.someEntityDao.save(someEntity);
}
}
I put breakpoints in the ImprovedNamingStrategy, and classToTableName() was called with "SomeEntity" and propertyToColumnName() was called with "someField".
In other words, package information isn't being passed in, so at least in this setup, it can't be used to apply a different naming strategy based on package name.

Categories