Moving spring annotation to common package - java

I have an annotation named #UserHasAccess which is used to check user's access based on some service call. In the advice, I have some dependency to call the service. This is the advice class:
public class UserHasAccessAdvice{
#Autowired
ServiceClient client;
//Other methods
}
Now I am planning to move this annotation to a common project so that I can use it in all the projects. When I tried, I am getting compilation error as ServiceClient is not present in the common project. How can I reuse this annotation by moving it to a common package? How to inject the service class based on the target project?
I am using Spring Boot 2.x

Related

Getting Spring Boot to load annotated resources defined inside runtime dependencies

I am developing a bunch of Java/Sprint Boot webservices that will all have several identical (ideally, reusable) Spring-based resources:
many annotation-based Spring Security configurations
several #Services and #Components
many annotation-based event handlers
annotation-based exception handler
#ControllerAdvice that extends ResponseEntityExceptionHandler
annotation-based configuration files (#Configuration)
Ideally I could place these in a library (shared JAR file) and reuse them across the different webservice projects. However I don't believe Spring Boot will scan the entire dependency graph of libraries and load them based on their annotations.
Does anybody know of a way to encourage Spring Boot to do this? For example if I package the following class into a reusable/shareable library JAR:
#ControllerAdvice
#Slf4j
public class ApiExceptionHandler extends ResponseEntityExceptionHandler implements ApiContractConstants {
// ... common exception handling code to be used by
// all services
}
And then pull that in to a Spring Boot webservice (via Maven/Gradle) as a runtime dependency, how do I get Spring Boot to scan, find and load that exception handler for me?
The description makes me think of #SpringBootApplication. The property scanBasePackages of #SpringBootApplication defines base packages to scan for annotated components.
#SpringBootApplication(scanBasePackages = {"org.example"})

Spring can't find CrudRepository beans when using ComponentScan

I'm trying to create project structure that would allow me to add/remove modules by simply having them on classpath. With #ComponentScan("com.companyname") annotation in my Spring Application it detects and creates annotated components from modules. But I get errors when trying to autowire my CrudRepository anywhere:
Field repo in com.companyname.somemodule.services.SomeService required a bean of type 'com.companyname.somemodule.repos.SomeRepo' that could not be found.
So I thought that maybe it somehow can't create repos if they are defined in one of modules, so I wen't ahead and added test repo to my base SpringApplication and to my surprise I got:
Field repo in com.companyname.modularapp.TestService required a bean of type 'com.companyname.modularapp.TestRepo' that could not be found.
Then I just removed my #ComponentScan annotation and suddenly TestRepo worked as I intended, I was able to persist and read Test entities normally. So apparently ComponentScan somehow either screw up creation of CrudRepository, or it's later detection.
I define my repos like this:
#Entity
public class Test {
#Id
private long id;
}
public interface TestRepo extends CrudRepository<Test, Long>{}
I'm trying out Spring Boot 2.0.0.M7 with this project but I doubt that's the cause.
Did I miss something?
Also you can define package for Repositories scan by :
#EnableJpaRepositories("com.companyname")
or in XML config
<jpa:repositories base-package="com.companyname"/>
If you are using spring-boot you might as well drop the #ComponentScan annotation, as there is one already defined in the #SpringBootApplication annotation. Maybe there's a conflict of some sort between them, it's hard to tell without looking at the code.
If you customizing package scans in your project, then probably you need to manually configure bean which requires path to scan, e.g. for JPA you can create your own bean of LocalContainerEntityManagerFactoryBean (you can find auto-configuration example here -
org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration#entityManagerFactory - class link from spring-boot 1.5.*).
But, if you do not require package scan customization, just put class annotated with #SpringBootApplication in project root, and pass it to spring::run method.

How to Configure Dependency Injection in a Library Project?

How to Configure Dependency Injection in a Library Project?
Let me illustrate this question with the following example.
Maven Library Project
ReservationAPI
com.example.reservation-api
This project contains a convenience class called ReservationApiClient which uses a RestTemplate (from the Spring Framework) for making HTTP calls.
Is it possible to make the RestTemplate field #Autowired in this library project instead of instantiating it myself?
Maven Executable Project
org.company.application
This project is a Spring Boot application and uses the above ReservationAPI as a dependency. This app will create a #Bean for the convenience class ReservationApiClient contained in that library and will then execute its public methods which in turn make HTTP requests.
What is a good strategy and/or best practices for the scenario described above?
You can do this if you include autowiring in your library project although that means it would always need to be used with a Spring application context to get the value unless you also have getter/setter methods to use as well. However, I don't think using RestTemplate as an autowired object makes sense since there is nothing specific about a RestTemplate and unless you name the beans there is only one bean definition for a class. All of the methods for the RestTemplate require the URI there anyhow. So in this case I would just use the bean for your ReservationApiClient in your application.
One other way to do it is if you want to include Spring dependencies in your library (which I guess you already are by using RestTemplate) you can declare your ReservationApiClient as a #Service or #Component and then use the #ComponentScan annotation in your main Spring Boot project to search that library for components to include in the bean registry.
Another option is to use a feature like Spring Boot's Autoconfigure to create factories that use third party libraries and configure them per properties in your application settings. The auto configuration documentation would be a good place to start with this. You can see the starter projects they have on GitHub and then the associated Autoconfigure classes they have associated with these.
Let me know if any of this does not make sense.

Using a Java written #Controller in Grails

I have created a Java class in Groovy Project in src/java folder.
I have used #Controller annotation to create a REST Webservice.
Code look like this
and then I configured component in resources.groovy
xmlns context: 'http://www.springframework.org/schema/context'
context.'component-scan'('base-package': 'com.**.**')
which matches package of the above piece of code.
When I'm trying to invoke service using URL its not able to recognize this.
Component scanning is already enabled by Grails, so delete yours. If you want annotated Spring beans to be discovered and registered, add one or more package names to the grails.spring.bean.packages list in Config.groovy, e.g.
grails.spring.bean.packages = ['com.foo.bar', 'some.other.package']

Classpath scanning of dependencies

I have a 3-tier application: web-service, service-layer and domain-layer. The web service is present in a web application (WAR). The service-layer and domain-layer are two JAR projects. The dependencies are:
web-service --> service-layer --> domain-layer
In the service layer, the services are annotated with #Service. In the domain-layer, the DAOs are annotated with #Repository. The web service implementation class uses the services of the service-layer JAR, so it keeps one instance of each service which is automatically injected (#Autowired).
The dependencies are well defined in my POMs. When I deploy my WAR on Tomcat, I get the following exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.mycompany.project.services.MyService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:952)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:821)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:735)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:551)
... 37 more
I quote from one relevant part in the Spring docs:
The scanning of classpath packages requires the presence of
corresponding directory entries in the classpath. When you build JARs
with Ant, make sure that you do not activate the files-only switch of
the JAR task.
I've checked and the service-layer JAR is present in the WEB-INF/lib directory.
Any idea?
Thanks
EDIT: I have only one context file which is located in the web-service layer project (WAR) under src/main/webapp/WEB-INF. In this context, I've enabled classpath scanning as follows:
<context:component-scan base-package="com.mycompany.project" />
The package com.mycompany.project is the base package of my project, under which there are the web-service (com.mycompany.project.server), service-layer (com.mycompany.project.services) and domain-layer (com.mycompany.project.domain) packages.
I've solved the issue. I don't understand why what I've done was causing such an issue. Each service implements an interface that defines its public methods. In my web service implementation class, the references to the services used the implementation classes and not the interfaces. I just changed them to use the interface, and I don't get the issue anymore. Could anyone explain me what's wrong with using the services implementation classes instead of the interfaces for the autowiring?
This is an answer for your EDIT:
The reason why referring to the interface worked but the concrete implementation failed is probably to do with the dynamic proxies that Spring creates for cases where you have your services annotated with #Transactional etc. What happens in such cases is that the type of your beans are not the implementation type anymore, but wrap around your impementation type. So when you have #Autowired by implementation type, it just cannot find it by type (which is the default).
Your fix is very appropriate, as a dynamic proxy continues to derive from the interfaces that you have defined for your implementation and so can inject by interface type - the reference that I have provided does a better job explaining this.
make sure you used <context:component-scan base-package="your.service.package"/>
check your autowired strategy is byName or byType; if byName, the Service annotation's name value should be right.
if problem still exist, check spring's log, it will print all found components' name, you could know the service is founded or not.
Can you show your component scanning configuration? If this is not set up correctly then Spring may not be discovering your service.
You want something like:
<context:component-scan base-package="your.service.package"/>
Edit:
I think the problem is that your #Service annotation is on the interface rather than implementation class.
If you annotate your service implementation then your web controller can use either:
#Autowired
private ExampleService service;
or
#Autowired
private ExampleServiceImpl service;

Categories