EntityScan in library project breaks SpringBootApplication auto configuration - spring

I'm trying to build a library with spring which will handle task management for other spring boot applications. My library includes services, repositories and entities. Library will use data source of parent project for entities. My target is using task management library in other spring projects with only using #EnableTask annotation.
To do that, I have prepared my library and it works as I expected. But when I tried to import this library to a spring boot application, repositories and entities from library were not available.
My EnableTask annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(TaskConfig.class)
public #interface EnableTask {
}
And TaskConfig
#Configuration
#ComponentScan("com.cbidici.task")
public class TaskConfig {
}
To make repositories from library registred I have changed my configuration class as below.
#Configuration
#ComponentScan("com.cbidici.task")
public class TaskConfig {
#Bean
public TaskRepository taskRepository(EntityManager entityManager) {
JpaRepositoryFactory jpaRepositoryFactory=new JpaRepositoryFactory(entityManager);
return jpaRepositoryFactory.getRepository(TaskRepository.class);
}
}
And it worked...
Now the part I mess up everything! With this configuration I've got excception that my Task entity is not a managed type.
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.cbidici.task.entity.Task
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:552) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:74) ~[spring-data-jpa-2.1.2.RELEASE.jar:2.1.2.RELEASE]
To make entities registered, I've tried to add EntityScan to my TaskConfig class. This messes up SpringBootApplication and my parent project does not scan entities in itself.
I've found this issue and there is a work around solution in it.
https://github.com/spring-projects/spring-boot/issues/6141
But, this solution includes adding #EnableJpaRepositories, #EntityScan to my parent project which I don't want to.
I think I need to find a way to register entities in library manually as I did for repositories but I couldn't.

Related

#EnableJpaRepositories/#EntityScan annotations mandatory in spring data jpa configuration setup?

I was going through this tutorial where the instructor was configuring Spring Data JPA. He had created the Entity classes and Repository interfaces and then added the #EnableJpaRepositories and #EntityScan annotations on the main application as follows:
#SpringBootApplication
#ComponentScan({"com.test.controller", "com.test.services"})
#EnableJpaRepositories("com.test.repository")
#EntityScan("com.test.entity")
public class MainApplication{
public static void main(String args[]){
SpringApplication.run(MainApplication.class, args[]);
}
}
I was creating the same project on the side, which had the same Entity classes and Repository interfaces but my main application didn't have these annotations. Instead, I added only #SpringBootApplication. Despite the absence of the said annotations, I found the code to be working well and fetching data from the db without issues. So my question is, what is the advantage of adding these annotations to the code ? Is it just for specifying the package where you can find the corresponding files, or are there any other advantages ?
By default, Spring searches for entities and repos in package where you placed your main class (and below this package). For example having these packages:
java
-com
---pack1
-----AnyEntity.java
---pack2
-----Main.java
Spring won't be able to find AnyEntity automatically. In this situation you need to specify where the entity is using #EntityScan. When it comes to repositories, you deal with it similarly using #EnableJpaRepositories.

Spring bean not getting Autowired from custom library

I have created by own library(com.custom.mylib) which returns a string like below.
#Component
public class MyLibrary{
#Value("${str.message}")
private String message; //This val should come from app which is going to use this lib
public String readMessage() {
return message;
}
I have create a project which is going to use above library. I have included the lib as pom dependency .But when I try to call library method from my app. I get the error below.
How to resolve it?
#Autowired
private MyLibrary myLibrary;
Consider defining a bean of type 'com.custom.mylog.MyLibrary' in your
configuration.
I also have below in application.properties file so that library can pick the value up
str.message=Hello world
I got the solution it seems.I need to create META-INF file and do org.springframework.boot.autoconfigure.EnableAutoConfiguration=<fully_qualified_name_of_configuration_file>
as given here
Spring Boot: autowire beans from library project
As it has to be used as a external library, you can instantiate it throught a #Configuration file:
#Configuration
public class AppConfiguration {
#Bean
public MyLibrary createMyLibraryInstance() {
return new MyLibrary();
}
}
The rule I used is the follow (this is not an universal rule):
In your domain classes (Controller, Service) : use #Autowired in your constructor. It is the recommanded way to inject your dependencies.
You want to use external classes : implements a Java Configuration with #Configuration annotation, to instanciate your external classes as beans.
You want to create custom utilities classes : decorate it with #Component.
When you have more than on implementation, use #Qualifier and define your beans in a #Configuration class.

Can I get another #Configuration only through #ComponentScan

I am using spring-boot 2.0.4; I have a bunch of services and they have a common configuration class marked with #Configuration.
I want to move this to a common dependency which will have this #Configuration, and based on the need, any micro-service can use #ComponentScan to activate this configuration from dependency.
I have done this for #Component classes, and it's working fine. I activate any particular component I need by adding it into #ComponentScan. How can I activate the configuration in a similar manner(based on need).
Here are the code examples:
Common Configuration:
package abc.department.common.configs.mongo
#Component
public class AbcMongo {
#Bean
public MongoTemplate mongoTemplate() {
// ... create MongoTemplate.
return createdMongoTemplate;
}
}
Here is a class which uses the above dependency:
#Configuration
#ComponentScan("abc.department.common.configs.mongo")
public class MyServiceConfigs {
}
Similarly, I want to do something like this:
package abc.department.common.configs.security.web
#Configuration
#EnableWebSecurity
public class AbcWebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ... do common configs;
}
}
and now, if a service would need web-security config, it could get like:
#Configuration
#ComponentScan({"abc.department.common.configs.mongo","abc.department.common.configs.security.web"})
public class MyServiceConfigs {
}
#Configuration is meant to specify the beans, for example:
#Configuration
public class MyMongoConfiguration {
#Bean
public MongoTemplate mongoTemplate() {
return new ...
}
#Bean
public MySampleBean mySampleBean(MongoTemplate tpl) {
return new MySampleBean(tpl);
}
}
But if so why do you need to work with #Component at all (at least for the beans you create)?
Configuration is a special bean used by Spring framework to load other beans and it can be viewed as a "substitution"/alternative technique to component scanning.
I believe that, if you have some infrastructure configuration that loads a bunch of "infrastructure beans" (shared jar if I get you right), then the services that use this jar should only say "Hey, I want to load this configuration" and not to scan inside the packaging structure of that jar. Why do I think so?
What if you decide to add new beans into a new package in the infra, should external services change their code and define an additional folder to scan? - Probably no.
What if you decide to move the infra to another package?
Now in Spring there are two simple ways to do this that come to mind:
Way 1: Use #Import Annotation
#Configuration // this is from "shared artifact"
class MyInfraConfiguration {
}
#Configuration // this is from an "applicative service" that uses the infra jar in dependencies
#Import(MyInfraConfiguration.class)
class ServiceAConfiguration {
}
Way 2: Use Spring Factories mechanism
The first way has a drawback: You need to know in a Service what infra configuration exactly is. If you see it as a drawback, consider using spring factories.
Spring factories allow registering the infra configuration in some file so that spring boot will load it in service one automatically, you won't even need to mention MyInfraConfiguration in the Service Configuration, just add a dependency to the infra jar and it will work.
In the infra component create:
META-INF/spring.factories
And add there:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycompany.myinfra.whatever.InfraConfiguration
That's it.
Now if you want to customize the loading of beans in the infra configuration, like, a creation of Mongo related templates only if some properties are available, you might want to use #Conditional. Now, although this is kind of out of scope for this question, I mention this because in conjunction with spring factories this can create a very flexible way to manage your configurations

Spring Hibernate won't implement Repository Class from Interface

I have two separate projects. One project contains my Application logic and Controllers in org.patrick.application, and one separate project contains my Hibernate entities, Dao, and models in org.patrick.hibernate. My problem is that Spring will not instantiate a implementing class for my CrudRepository.
Here is my Application.java class annotations in my Application project:
package org.patrick.application;
#SpringBootApplication
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = { "org.patrick.hibernate" })
#EntityScan(basePackages = { "org.patrick.hibernate" })
#ComponentScan(basePackages = { "org.patrick.hibernate", "org.patrick.application" })
These annotations should scan my second Hibernate project for all of my Hibernate objects.
My Hibernate repository looks like this:
package org.patrick.hibernate;
#Repository
public interface PatrickDao extends CrudRepository<MyModel, Long>
This repository does not have a class implementation. I am expecting Spring to populate this implementation for me.
Now, inside of my application org.patrick.application, I am trying to use the Dao like so:
package org.patrick.application;
#Autowired
private PatrickDao patrickDao;
This is causing my Application project to fail to start because of the following error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [org.patrick.hibernate.PatrickDao]: expected at least 1 bean which qualifies as autowire candidate.
I know that the core problem is that Spring is not implementing this interface -
because if I provide my own PatrickDaoImpl in the org.patrick.hibernate package, then the Application project will start just fine. This confuses me because I have the proper annotations on my Application.java class, and yet the Repository cannot be implemented by Spring for some reason.
Is there anything further I need to do in order to get Spring to implement the class for my Repository interface? In previous testing, this behavior works if everything is under the same package.
I found the problem. For this particular Model, the definition looked as such:
package org.patrick.hibernate;
public class MyModel implements Serializable {}
This Model did not have the #Entity annotation. If this annotation is missing, then Spring will give no warnings as to why it cannot implement the repository interface. In my case, it simply threw the NoSuchBeanDefinitionException exception.
I updated my Model with the proper annotations:
package org.patrick.hibernate;
#Entity
public class MyModel implements Serializable {}

Plugin System in Spring Boot for modular applications

I looking for dynamically loading jar in spring boot after compiling, for example I will put jars in some folder and when spring boot is started, all jars from this folder will be injected into spring boot app. I don't know how can I do this with spring boot, and if You know can help me with this, with some example.
I need this jars to have #Service, #Controller as this will be module (plugin), with adding capabilities to my spring boot app.
Is possible to do this with spring boot, and if it is possible, please provide me with some sample code.
Thanks in advance.
UPDATE:
I found something https://www.youtube.com/watch?v=F-sw2pFdcDw https://code.google.com/p/jspf/
UPDATE 2: I can't get #Controller bean from plugin jar registered in Spring Boot
Have a look at FlexiCore, an open-source framework that brings modularity to spring boot utilizing plugins(jars) loaded at runtime See wizzdi and FlexiCore.
for example FlexiCore allows you to create a project ( compiled into a seperate jar from your main application) that contains a spring bean as follows:
#Component
#Extension
public class HelloWorldService implements ServicePlugin{
public String hello() {
return "Hello World!";
}
}
it will be automatically be loaded once placed inside the designated plugins folder, it basically allows a full support for most(all) of spring boot features , so for example you can add a RestController bean to your jar as well , FlexiCore will automatically load that bean allowing you to call the controller as if it was in your main application jar:
#RestController
#Extension
public class TestEntityController implements Plugin {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
private TestEntityService testEntityService;
#PostMapping("/createTestEntity")
public TestEntity createTestEntity(#RequestParam(name="name", required=false, defaultValue="Stranger") String name) {
return testEntityService.createTestEntity(name);
}
#GetMapping("{id}")
public TestEntity getTestEntity(#PathVariable("id")String id) {
return testEntityService.getTestEntity(id);
}
}
Disclaimer: I am the CTO of wizzdi, the company powering FlexiCore.
One option is definitely to just use broad #ComponentScan. If you add new jar to classpath the annotated classes from that jar will get discovered via #ComponentScan, #Controllers will get mapped etc.
The XML equivalent here would be placing xml configuration files somewhere to your classpath (META-INF folder being obvious choice) and import them all using wildcard. The idea is the same. If the plugin jar file is on classpath you will get the xml file imported and the beans (controllers, ...) will get loaded.
There are drawbacks to this approach like the modules not being isolated but its definitely option for simpler applications.
You can find a sample spring boot web project here.
By dynamically loading jars I assume you want to add dependencies to your project. For this you can update pom.xml of the sample project and put your dependencies here.

Resources