Add a object with custom annotation in springboot - spring

I need to add an object like #Slf4j's "log" in a service class, for example:
#Slf4j
#Service
public class MyService {
public void anyMethod() {
log.info("any log");
}
}
in this case, #Slf4j is injecting the "log" object, is there any way I can do this with a custom annotation with another object ?

Related

Custom class level annotation in spring boot

I created a custom class level annotation.
Below is the aspect class:
#Aspect
#Slf4j
#Component
public class LoggingAspect {
private String generateLogMessage() {
return ("Entering method");
}
#Before("#within(mypackage.logging.Loggable) || #annotation(mypackage.logging.Loggable)")
public void logMethodEntry(JoinPoint joinPoint) {
String logMessage = generateLogMessage();
log.debug(logMessage);
}
}
Below is my custom annotation:
#Target(value = {ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable { }
When I use the annotation at class level it works. But if I am calling different methods within the same class the logging is done only once. I want to do the logging for each methods called using the class level #Loggable annotation.
I tried to use it at method level it works. But I want to use it at class level.

Abstract GORM Data Service not Being Injected by Spring

I have a GORM Data Service abstract class that I'm trying to wire it into a Controller class. Given the below sample code as-is, the myDomainClassDataService object will be null when the application starts and I will get an exception when I attempt to call its methods. If I apply the #Autowired annotation to myDomainClassDataService in the MyController class, then I will receive a NoSuchBeanDefinitionException. We are using GORM with a neo4j database.
The code works when running through IntelliJ locally, but not at all when built and deployed in an AWS CodePipeline. This is part of an upgrade from Grails v4 to v5, where the code was previously working without any issues.
// gradle.properties
grailsVersion=5.1.7
grailsGradlePluginVersion=5.1.3
groovyVersion=3.0.7
springBootVersion=2.6.6
spring-framework.version=5.3.19
gormVersion=7.2.1
// Sample GORM Data Service class
import grails.gorm.services.Service
import org.myorg.auth.UserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.MessageSource
#Service(MyDomainClass)
abstract class MyDomainClassDataService {
UserService userService
MessageSource messageSource
#Autowired
#Qualifier("messageSource")
final void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource
}
abstract MyDomainClass get(Serializable id)
abstract List<MyDomainClass> list(Map args)
abstract Long count()
abstract void delete(Serializable id)
abstract MyDomainClass save(MyDomainClass myParam)
def updateMyDomainClassApplicability(MyDomainClass myDomainClass, Boolean selectedApplicableFlag) {
// Concrete method. Do something.
}
def getSomethingFromMyDomainClass(myParam){
// Concrete method. Do something.
}
// More concrete methods below.
}
// Sample Controller class
import org.myorg.assessment.MyDomainClassDataService
class MyController {
MyDomainClassDataService myDomainClassDataService
def index(Integer max) {
// Concrete method. Do something.
}
// More concrete methods below.
}
Any thoughts or suggestions would be greatly appreciated.

Springboot #Transactional - How to make spring call super when it creates service proxy?

I have intermediate skills in Springboot and can develop simple to medium complex applications but this one is throwing me off a little bit.
I have a #Service that extends Guava's AbstractIdleService and a #Controller that has instance of the service autowired.
In the service, I have a method, say performTransaction() that is annotated with #Transactional (ofcourse to let Spring take care of transactions) and it does create a proxy of the service class. But when I debug the application I can see that the proxy that is injected in the controller contains all super class (AbstractIdleService) class fields as null meaning super() is not called when the proxy of my service was created. Because of this the application is failing to initialize when calling the super class's (AbstractIdleService) startAsync method. I am not really sure how to make this work.
I have added some code below for illustration.
This is my service class:
import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;
#Service
public class MyService extends AbstractIdleService {
#Transactional
public void performTransaction() {
}
}
This is the controller that uses the above service:
#Controller
public void MyController {
private MyService service;
private EurekaRegistration eurekaRegistration;
#Autowired
public MyController(MyService service, EurekaRegistration eurekaRegistration) {
this.service = service; // This is where the proxy contains all AbstractIdleService fields null
this.eurekaRegistration = eurekaRegistration;
}
#EventListener({ ApplicationPreparedEvent.class })
public void handleApplicationPreparedEvent() {
this.service.startAsync(); // This line throws NPE, as delegate in the parent is null
this.service.awaitRunning();
}
}
I must use AbstractIdleService as that is the organizational convention and I need to invoke some startup code in the startAsync method. But invoking startAsync method on the service is causing a NullPointerException as it uses a parent field that is null because the proxy initialization never called super.
I'm not 100% sure if this is the actual problem, but try something like this (untested):
Interface for proxy:
import com.google.common.util.concurrent.Service;
public interface MyService extends Service {
void performTransaction();
}
Implementation:
import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;
#Service
public class MyServiceImpl extends AbstractIdleService implements MyService {
#Transactional
public void performTransaction() {
}
}
Controller, declare service interface, not implementation:
#Controller
public void MyController {
private MyService service;
private EurekaRegistration eurekaRegistration;
#Autowired
public MyControlle(MyService service, EurekaRegistration eurekaRegistration) {
this.service = service;
this.eurekaRegistration = eurekaRegistration;
}
}

Spring AOP annotation based pointcuts for ElementType.TYPE?

I have a service implementation carrying a class-wide #Transactional annotation. I also have an aspect that uses the #Around advice to retry failed transactions. I'm now trying (for type-safety reasons) to make the pointcut definition annotation based:
#Around("#annotation(TransactionRetryable)")
TransactionRetryable.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface TransactionRetryable {
}
Service layer:
#Transactional
public class ... {
#Override
#TransactionRetryable
public String forceError() {
throw new RuntimeException(someNastyMessage);
}
In that form, it only applies to method, not whole classes. However, the #Transactional annotation propagates from class level to each method. Is there a way to avoid putting the #TransactionRetryable annotation above each method and simply once above the class like the #Transactional annotation? Desired form:
#Transactional
#TransactionRetryable
public class ... {
#Override
public String forceError() {
throw new RuntimeException(someNastyMessage);
}
This pointcut would advise all public methods of a class annotated with #TransactionRetryable:
#Around("execution(public * *(..)) && within(#your.package.TransactionRetryable *)")

Spring annotations - #Configuration to invoke spring bean auto-building

If I declare a class using #Bean and then component scan for the class, spring will instantiate the class by invoking it's constructor and injecting constructor args and injecting any fields marked with #Inject. For simplicity's sake, lets call this spring auto-building.
I dislike component scan and wish to avoid it completely (I don't wish to discuss my reasons for not liking it). I would like to use a #Configuration object instead but would still like to have the auto-building functionality available to me. Is it possible to invoke spring to auto-build my objects instead of explicitly having to pass all the constructor arguments in my #Configuration object?
Lets assume that I have a bean:
public class MyServiceImpl implements MyService {
public MyServiceImpl(Dependency1 d1, Dependency d2) { ... }
....
}
I could define a configuration object like this:
#Configuration
public class MyConfiguration {
// lets assume d1 and d2 are defined in another #Configuration
#Inject
Dependency1 d1;
#Inject
Dependency2 d2;
#Bean
public MyService myService() {
// I dislike how I have to explicitly call the constructor here
return new MyServiceImpl(d1, d2);
}
}
But now, I have explicitly had to call the MyServiceImpl constructor myself so I will have to keep updating this as my constructor changes over time.
I was hoping that I could declare an abstract method so that spring auto-building could take place:
#Configuration
public abstract class MyConfiguration {
#Bean
public abstract MyServiceImpl myService();
}
But this doesn't work. Is there a way that I can invoke spring auto-building without using a component scan?
In Google Guice, this can be done via the Binder:
https://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/Binder.html
In Tapestry IOC, this can be done via the ServiceBinder:
http://tapestry.apache.org/ioc-cookbook-basic-services-and-injection.html#IoCCookbook-BasicServicesandInjection-SimpleServices
Update
Based on spod's answer, I was able to achieve what I was after (thanks!). Test case included for anyone that wants to do the same:
import java.util.Date;
import javax.inject.Inject;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class AutoBuildConfigurationTest {
#Configuration
public static class MyConfiguration {
#Inject
private AutowireCapableBeanFactory beanFactory;
#Bean
public Date date() {
return new Date(12345);
}
#Bean
public MyService myService() {
return autoBuild(MyService.class);
}
protected <T> T autoBuild(Class<T> type) {
return type.cast(beanFactory.createBean(type, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true));
}
}
public static class MyService {
private Date date;
public MyService(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
#Test
public void testAutoBuild() {
ApplicationContext appContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyService myService = appContext.getBean(MyService.class);
Assert.assertEquals(12345, myService.getDate().getTime());
}
}
The java based container configuration doesnt depend on doing a component scan in any way. Its merely a different approach for the XML based component configuration. With the XML configuration you'd just have to declare your bean with the MyServiceImpl class in case its already #inject annotated. Spring would recognize the annotations and take care of them. If you really want to instanciate MyServiceImpl from a #Configuration java class without calling the constructor yourself, then you'd have to make use of the bean factory (havent tested it, just give it a try):
#Configuration
public class MyConfiguration {
#Autowired AutowireCapableBeanFactory beanFactory;
#Bean public MyService myService() {
return beanFactory.createBean(MyServiceImpl.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
}
}

Resources