How to let spring auto implement abstract methods - spring

As we know, Spring data can implement the user-defined interface automatcially. How to create the library with the same ablitity? For example:
interface Service {
#Println("The metod 'a' is invoked)
void a(); //Hope spring implement it automatically
#Println("The metod 'b' is invoked)
void b(); //Hope spring implement it automatically
void c();
}
This interface has three methods, a, b, and c. the annotation #Println has been declared on a and b, that means they should be implemented automatically. Method c has no annotations, that means it should be implenented by the developer, the developer can define an abstract class that only override methods c, like this
#Compomenet
public abstract class ServiceImpl extends Service {
#Override
public void c() {
System.out.println("User invokes method 'c'");
}
}
I know how to use JDK proxy/cglib/ASM to generate byte code at runtime, but I don't know how to change the bean registrition behavior of spring framework. I want create a library that can let spring auto implement abstrat methods by my bytecode generator.

This answer should address your question: Component Scan for custom annotation on Interface
Basically, you extend one class (ClassPathScanningCandidateComponentProvider) to make sure you component scan picks up abstract classes and interfaces; you then provide a ImportBeanDefinitionRegistrar implementation to register the beans.

Related

Spring Boot: scanning components into an injectable list or set

Spring Boot here. I have dozens and dozens of classes that are all subclass/implementations of the same interface:
public interface AnimalService {
void eat();
// etc.
}
#Component
public class DogService implements AnimalService {
#Override
public void eat() { return ... }
// etc.
}
// many many many of these
I have a #Service-annotated class (AnimalService) that needs to be injected with each one of these subclasses:
#Service
public class AnimalProcessingService {
#Autowired
private List<AnimalService> allAnimalServices; // DogService, SharkService, etc.
// the rest of the class definition is omitted intentionally
}
Now I could do this injection the hard way:
#Configuration
public class AnimalConfig {
private DogService dogService;
private SharkService sharkService;
// ...etc.
#Bean
public List<AnimalService> allAnimalServices() {
return Arrays.asList(dogService, sharkService /*, ...etc. */);
}
}
But there's gotta be an easier way to do this, right?
How can inject this List<AnimalService> without having to manually create the list? Does this method allow you to filter out certain candidates (like if I just want a subset of the animal services)? Ideally something annotation-based (even if I have to define my own annotations) that works with Spring autowiring/DI/component scanning. Thanks for any and all help here!
Essentially you're looking for #ComponentScan, which is a Spring (not Boot) annotation. It allows you to define a list of packages for Spring to scan for #Components (or "sub-annotations" such as #Service), to automatically instantiate beans of those classes and add them to the Spring context. So you can consider it a more automated way of the more manual #Bean method declaration.
Since you're using Spring Boot, however, you might want to look into #SpringBootApplication which, when used, enables component scan automatically. All you have to do is making sure your #Component classes are defined in the same package as, or sub-packages of, the #SpringBootApplication-annotated class.
Once you've enabled component scanning, you can just inject a List<AnimalService> where you need it, like in a constructor, and do your processing (filtering?) there.

Is it possible to cut into protected method inside abstract class using Spring AOP?

I am trying to execute some code after one protected method inside abstract class gets called. It looks something like this.
abstract class SomeAbstractClass : ServiceOne, ServiceTwo {
protected fun doSomething(p1: String, p2: Int): SomeEntity {
// some business logic
return SomeEntity()
}
}
I want to create annotation class like the one below and annotate method (above) with it and I want it to execute after class method returns value. I tried this and many other variations but nothing worked for me.
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class EntityCreated {
#Aspect
#Component
class EntityAspect {
#AfterReturning("#annotation(EntityCreated)")
fun afterSuccessfulCreate(jp: JoinPoint) {
// Created, execute something here
}
}
}
While protected methods are off limits for JDK proxies when proxying interfaces, the CGLIB proxies used by Spring to extend classes can intercept protected methods. But in order to proxy a Kotlin class, you have to open it, so it is not final and can be proxied.
Please note, however, that if you override a protected method in Java, that overridden method will not "inherit" the super method's annotations. This is a Java limitation and unrelated to AOP. Annotation inheritance only works from parent to sub classes, if the annotation bears the #Inherited meta annotation. There is a way to emulate annotation inheritance using an advanced form of a technique called ITD (inter-type definition) in native AspectJ, but not in simple Spring AOP.
The exact answer to your question depends on your exact use case, your sample code is too fragmentary to answer in more detail.

Spring Boot 2.5 and Spring Data: #NoRepositoryBean unexpected behaviour in multi-module project

I'm facing the following issue in a legacy code that I can't change. I have a multi module project which defines in the commons module a Spring Data interface as below:
package commons;
...
#NoRepositoryBean
public interface MyCustomRepository<P, I extends Number> extends JpaRepository<MyEntity, Integer>
{
MyEntity getOneAndCheck();
}
In another module I extend this interface as follows:
package data;
...
#Repository
public interface MyRepository extends MyCustomRepository<MyEntity, Integer>
{
...
}
So, the idea is that I don't want that Spring Data generates any implementation for the MyEntity getOneAndCheck() method 'cause it is implemented like this:
package data;
...
public class MyCustomRepositoryImpl implements MyCustomRepository
{
...
#Override
public MyEntity getOneAndCheck()
{
...
}
...
}
However, when I'm starting the application, I get the following exception:
...
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract MyEntity commons.MyCustomRepository.getOneAndCheck()! No property getOne found for type MyEntity!
...
So what it seems to happen is that Spring Data tries to generate a Query for the MyEntity getOneAndCheck() method, despite the #NoRepositoryBean annotation. This works as expected in the application I'm gonna migrate from Spring 3 with Spring Data to Spring Boot 2.5.
Not sure if the described behavior has anything to do with the fact that there are multiple Maven modules and that the repositories, the entities and the DTOs are in different modules. Not sure neither if there should be any difference between the way it runs currently with Spring and the one with Spring Boot. But the result is that all of the dozens of repositories in this legacy application are failing with the mentioned exception.
It might be important to mention that the main class needs to use annotations in order to tune the scanning:
#SpringBootApplication(scanBasePackages = "...")
#EnableJpaRepositories(basePackages={"...", "..."})
#EntityScan(basePackages= {"...", "..."})
public class MyApp
{
public static void main(String[] args)
{
SpringApplication.run(MyApp.class, args);
}
}
Not sure whether these annotations are supposed to change anything from the point of view of #NoRepositoryBean but the issue appeared as soon as I added this Spring Boot main class. It worked okay previously without Spring Boot.
Any suggestion please ?
Many thanks in advance.
Kind regards,
Seymour
There are two things that play together:
Spring Data's default custom implementation
Repository fragments
None of these apply because:
The default custom implementation follows the name of the actual repository. In your case, the implementation is named MyCustomRepositoryImpl whereas the repository name is MyRepository. Renaming the implementation to MyRepositoryImpl would address the issue
Since Spring Data 2.0, the repository detection considers interfaces defined at the repository level as fragment candidates where each interface can contribute a fragment implementation. While the implementation name follows the fragment interface name (MyCustomRepository -> MyCustomRepositoryImpl), only interfaces without #NoRepositoryBean are considered.
You have three options:
extracting your custom method into its own fragment interface and providing an implementation class that follows the fragment name:
interface MyCustomFragement {
MyEntity getOneAndCheck();
}
class MyCustomFragementImpl implements MyCustomFragement {
public MyEntity getOneAndCheck() {…}
}
public interface MyRepository extends MyCustomRepository<MyEntity, Integer>, MyCustomFragment {…}
Set the repositoryBaseClass via #EnableJpaRepositories(repositoryBaseClass = …) to a class that implements the custom method.
If you cannot change the existing code, you could implement a BeanPostProcessor to inspect and update the bean definition for the JpaRepositoryFactoryBean by updating repositoryFragments and adding the implementation yourself. This path is rather complex and requires the use of reflection since bean factory internals aren't exposed.

Why do most of people create an Interface in order to create a service?

I tried to create a service directly through a class without implementing a customized interface. And it works ! So I wonder why do most of people spend time on creating an interface in order to create a service ?
I can point 2 reasons:
It helps decoupling. (Of course it is still possible to create decoupled classes without an interface.)
You added spring in the question tag, so this reason is specific: in many cases Spring needs an interface to properly create a jdk proxy (this is needed when using AOP). It is possible to create proxies without an interface (spring will use CGLIG instead of JDK), but there are some differences "under the hood". Check here.
First of all we are using service layer in order to wrap some business logic to our application.
To make our service layer more abstract from the client we will first create service Interface that will contains some of the abstract methods similar to CURD repository.
Then we need to write logic for our abstract methods in service interface by creating new class like ServiceImplementation.
So the control flow will be Controller to ServiceImplementation.
class StudentController {
ServiceImplementation serviceImpl;
// Endpoints implementation.
GET;
POST;
PUT;
DELETE;
}
interface Service {
List<StudentInfo> getAllStudents();
StudentInfo addStudent(Student std);
StudentInfo updateStudent(Student student);
StudentInfo findStudentById(String id);
boolean deleteStudent(String id);
}
public class ServiceImplementation implements Service{
private StudentRepository studentRepository;
// Implement the abstract methods
}
public interface StudentRepository extends JpaRepository<Student, String>{
}
This is the standard pattern we will follow.

Event Listeners in spring is called twice

I am an issue with Spring Event Listeners In my Web app, Any immediate help will be appreciated.
Event Listeners is registered and called twice, If I have cyclic dependency.
I have service class, this has #transaction annotation on another methods
#Service(PBSTaskService.BEAN_NAME)
public class PBSTaskServiceImpl extends StandardServiceImpl<ITask> implements PBSTaskService,ApplicationListener<SurveyDefinitionPublishedEvent>
{
#Autowired
private AutoSelectTaskSliceRouteSyncService autoSelectTaskSliceRouteSyncService; // CYCLIC Dependency
#Override
public void onApplicationEvent(SurveyDefinitionPublishedEvent event)
{
System.out.println("PBSTSImpl"); // THIS IS CALLED TWICE
}
... Other method with #Transaction Annotation
}
#Service(AutoSelectTaskSliceRouteSyncService.BEAN_NAME)
public class AutoSelectTaskSliceRouteSyncServiceImpl implements AutoSelectTaskSliceRouteSyncService
{
#Autowired private PBSTaskService pbsTaskService; // CYCLIC dependency
}
Now If I remove AutoSelectTaskSliceRouteSyncService dependency from First Class, OnApplicationEvent is called once, else twice.
I debugged and found out that
SimpleApplicationEventMulticaster.getApplicationListeners(myEvent) : Has two proxy object, one wrapped with Cglib and another default one. But it has two only in case if it has cyclic dependency. If I remove Cyclic dependency, it has only one proxy object and that one is enahnces by CGLIB.
my Tx annotation :
I had tried it with proxy-target-class="true or false" but no luck.
You may want to have a look on
https://jira.springsource.org/browse/SPR-7940?focusedCommentId=98988&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-98988
Since Spring 4.2 you can do away with implementing ApplicationListener and use the new #EventListener annotation on methods in any managed bean. This should help you avoid any conflicts.
Below is an example from https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
#Component
public class MyListener {
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
...
}
}
ApplicationEvent Listeners are called twice at many more places in our web app. This is one of scenarios that we caught up.
Reason :
Listeners are registered twice. Two proxy are returned wrapped over one instance of listeners. Proxy returned are 1. Dynamic Jdk Interface proxy 2. Cglib Proxy, when we have #transactions annotations.
To recreate these three point are must:
Your listeners must implements ApplicationListener 2. Your listeners must have cyclic dependency with another class 3.Your listeners must have one method annotated with #Transaction.
I have created a separate project where I am able to reproduce it with spring and hibernate. If 2 and 3 are not present together, then we are safe.
Solution
I tried many tweaks with spring and transaction configuration but no luck. Then finally with my demo project when I moved the transaction code to another class, so that the listeners do not have any #transaction annotations then it worked for me.
In Spring classes anotated with #Service or #Component which implement the ApplicationListener interface are going to receive duplicate events. To resolve the issue, to only receive single events, just remove the #Service or #Compontent annotation.
In a case of circular dependency between Spring beans, Spring Beans machinery might (under certain circumstances) place two versions of a same bean, the bean itself and its Advised wrapper into the list of ApplicationListeners handled by an ApplicationEventMulticaster.
You could, however, implement your custom ApplicationEventMulticaster and fix this bug (it looks like a bug to me).
In a snippet below a custom implementation subclasses Spring's SimpleApplicationEventMulticaster, ignores non-Advised duplicate of a bean, and leaves Advised version of it in the list of ApplicationListeners (most likely you would want an Advised version of your onApplicationEvent method to be called - in a case it is annotated with #Transactional or AOP-advised, but if you need otherwise, the change of algorithm is trivial)
#Component
public class AdviceAwareApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
#Override
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Map<ApplicationListener<?>, ApplicationListener<?>> listenersByNakedInstances = new LinkedHashMap<>();// because superclass returns sorted listeners
Collection<ApplicationListener<?>> applicationListeners = super.getApplicationListeners(event, eventType);
for (ApplicationListener<?> listener : applicationListeners) {
boolean advised = false;
ApplicationListener<?> nakedListener = null;
if (listener instanceof Advised) {
try {
nakedListener = (ApplicationListener<?>) ((Advised)listener).getTargetSource().getTarget();
} catch (Exception e) {
// TODO
}
advised = true;
} else
nakedListener = listener;
if (advised || !listenersByNakedInstances.containsKey(nakedListener))
listenersByNakedInstances.put(nakedListener, listener);
}
return listenersByNakedInstances.values();
}
}
You don't need to anyhow make your custom implementation known to Spring, it's enough to have it as a Spring bean and Spring Application Context will pick it up.
Also, don't forget that if there are more one Spring Application Contexts in the application, your Listener might be called for each of those, but it's altogether different story.
I was running into the same issue with one of my services, created another listner with the same event that was only called once.
So what #SimonH wrote is not always the case, only in some circumstances I could not reproduce:
In Spring classes anotated with #Service or #Component which implement the ApplicationListener interface are going to receive duplicate events.
In my case this lead to a double call of the onApplicationEvent method.
#Service
public class TestClass implements ApplicationListener<MyEvent>{
#Override
public void onApplicationEvent(MyEvent event){
// called twice
}
}
Instead of the code above, I could solve it by creating the Event Listener as an inner class and then call the event method of the parent.
#Service
public class TestClass {
#Component
private class MyListener implements ApplicationListener<MyEvent> {
#Override
public void onApplicationEvent(MyEvent event) {
TestClass.this.onApplicationEvent(event);
}
}
public void onApplicationEvent(MyEvent event){
//Have fun with a single event here
}
}

Resources