Spring Boot - Get all methods with custom annotation - spring

I have a custom annotation:
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
annotation class Listener
Used like this:
#Service
class MyService {
#Listener
fun onNewThing(thing: Thing) {
...
}
}
In another service, every time something happen, I want to call each function annotated with #Listener and with a parameter of type Thing.
How can I do it without looping through all the beans in the context and checking all the methods?

You can use the following:
Set<Method> methodsAnnotatedWith = new Reflections("com.example.spring.aop.api", new MethodAnnotationsScanner()).getMethodsAnnotatedWith(BusinessFunction.class);

you can use org.reflections of java :
Set<Method> allMethods = new Reflections().getMethodsAnnotatedWith(yourAnnotation.class);
for (Method m : allMethods) {
if (m.isAnnotationPresent(yourAnnotation.class)) {
//YOUR LOGIC
}
}

Related

Evaluate property from properties file in Spring's #EventListener(condition = "...")

I would like to make the execution of an event handler dependent on whether or not a property is set to true in a properties file.
#EventListener(ContextRefreshedEvent.class, condition = "${service.enabled}")
public void onStartup() { }
However, this does not seem to work. I am getting the following error on startup:
org.springframework.expression.spel.SpelParseException: EL1043E:(pos 1): Unexpected token. Expected 'identifier' but was 'lcurly({)'
Is it possible to use a property from a properties file as a condition here?
The issue is condition argument is expecting a SPEL.
This works try it out.
In your bean where you have this #EventListener, add these lines
public boolean isServiceEnabled() {
return serviceEnabled;
}
#Value("${service.enabled}")
public boolean serviceEnabled;
change your declaration of evnt listener like this
#EventListener(classes = ContextRefreshedEvent.class, condition = "#yourbeanname.isServiceEnabled()")
public void onStartup() { }
change yourbeanname with the correct bean name .
I had the same annoying experience (with Spring Boot 2.4.2 on Java11).
In my case I had the boolean property in a #ConfigurationProperties class anyways in the same java file and still struggled a bit. First the #ConfigurationProperties need to be annotated as #Component to actually be a valid Bean and can be used in SpEL.
And I had to use the same long attributeName for the ConfigurationProperties in the Service itself and the EventListener Annotation for the Bean resolution to work fine. I needed some the ConfigurationProperties values also in another place of the Service, that's why they needed to be (Constructor) Autowired as well...
So this worked for me:
#ConfigurationProperties("my.custom.path")
#Component //Important to make this a proper Spring Bean
#Data //Lombok magic for getters/setters etc.
class MyCustomConfigurationProperties {
boolean refreshAfterStartup = true;
}
#Service
#RequiredArgsConstructor //Lombok for the constructor
#EnableConfigurationProperties(MyCustomConfigurationProperties.class)
#EnableScheduling
public class MyCustomService {
private final MyCustomConfigurationProperties myCustomConfigurationProperties;
#EventListener(value = ApplicationReadyEvent.class, condition = "#myCustomConfigurationProperties.refreshAfterStartup")
public void refresh() {
//the actual code I want to execute on startup conditionally
}
}

Spring Boot: How to pass context from a custom Aspect to Rest Controller Class

I have defined a custom aspect which does a look up for the resource does few validations and fails fast. If the validations are successful how can I pass on the resource to my Rest Controller class to avoid extra DB lookups
You can do something like this:
#Aspect
#Component
public class SomeAspectClass {
#Around("execution(* com.example.controller.yourController.yourMethod (SomeClass)) && args(someObject)")
public Object sendDataToControllerMethod(ProceedingJoinPoint proceedingJoinPoint, SomeClass someObject) {
//Do your validation, db related works
// "someObject" can be filled, changed etc. here
YourMethodReturnClass yourMethodReturnObject = (YourMethodReturnClass) proceedingJoinPoint.proceed(new Object[] {someObject});
return yourMethodReturnObject;
}
}

Spring framework and java like Object collectors In Scala

In Spring framework and Java world, there is an interesting object collector pattern that I use.
For example consider below -
public interface Calculator {
SomeOutput calculate(SomeInput input);
}
#Component
public class CalImpl1 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
#Component
public class CalImpl2 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
Now this can easily injected in another class using Spring DI
#Component
public class Main {
//This line collects all to implementors of this and set it here.
#Autowired
public List<Calculator> calculators;
//other methods
}
Now problem is I am not sure how same thing can be achieved in scala. I have done some search and found cake pattern (http://loicdescotte.github.io/posts/scala-di/) used in scala but that didn't seem to achieve same thing as object collectors like above. I also want to follow open close principle which I think gets violated in cake pattern but using object collectors I can easily achieve it.
is there a way achieve same object collectors like implementation in scala?
There are templates in lighbend activator that illustration using spring DI on Play, Akka and Scala applications. Please see this: https://www.lightbend.com/activator/templates#filter:spring
I haven't used Spring as DI, I usually use Guice (explicitly used because it's default on play framework 2) and Implicits parameters both as a compilation DI.
Sample:
class B
class X(x: Int)(implicit c: B)
//DI - mostly define in main method/application
implicit val c: B = new B
val x = new X(2)
Explicitly using java.util.List worked for me. This is not the prettiest solution but it shows that it basically works. Haven't tried that but implementing a corresponding PropertyEditor you could stick with the Scala types.
trait Calculator {
def calculate(input: SomeInput) : SomeOutput
}
#Component
class CalImpl1 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class CalImpl2 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class Main #Autowired()(calculators: java.util.List[Calculator]) {
// or inject field if constructor injection is not desired
// #Autowired
// var calculators: java.util.List[Calculator] = _
}
object Main {
def main(args: Array[String]) = {
val ctx = new AnnotationConfigApplicationContext("your package name here")
val main = ctx.getBean(classOf[Main])
// calculators should now be wired in the returned instance
}
}

How to initialise/wire beans in Grails Spock unit tests?

I'm wanting to test a Grails controller that contains a bean (I'll move it to a Service when I get it working, but I just want to keep it simple now).
//resources.groovy
beans {
myBean(com.me.MyBean)
}
// MyBean.java
// this needs to be in java as it is playing with spring-data-neo4j
package com.me;
public class MyBean {
String show() {
return "Hello";
}
}
// TestController.groovy
package com.me
import com.me.MyBean
class TestController {
def myBean
def index() {
render myBean.show()
}
}
// TestControllerSpec.groovy
package com.me
import grails.test.mixin.TestFor
import spock.lang.Specification
import com.me.*
#TestFor(TestController)
class TestControllerSpec extends Specification {
def myBean
def setup() {
defineBeans {
myBean(com.me.MyBean) {bean->
bean.autowire = true
}
}
}
def cleanup() {
}
def "show() returns Hello"() {
when:
def rc = controller.myBean.show()
def rc2 = myBean.show()
then:
rc == "Hello"
rc2 == "Hello"
}
}
Within TestControllerSpec, myBean is null. controller.myBean is also null. I think this is because Spring is not picking the bean up and wiring it in. I gather that in unit tests not all spring beans are available, but what do I need to do to get controller.myBean to be instantiated and wired up correctly?
You must be mocking the myBean as below
def myBean = Mock(MyBean)
or
MyBean myBean = Mock()
and then stub out method for your need if required as below:
myBean.show >> "test data"
and then assign it to controller object which is already mocked for you.
controller.myBean = myBean
and there you go.
Or optionally you can stub out myBean and give stubbed implementations. For example,
MyBean myBean = Stub(){
show() >> {return "sample text"}
}
controller.myBean = myBean
The reason for doing this is we are not testing the integration of application entities like controller, views or domain but we are testing a single unit i.e. a method and hence we should be just testing it and for integration we should be using integration test cases which would be similar in everything except you won't require any mocking in normal scenarios.
Edit:
found another useful feature to mock services or beans using defineBeans closure as below:
defineBeans {
adapter(Adapter)
helperService(HelperService)
}
This will allow beans to be accessed from grailsApplication.
Hope it helps.

Grails domain class initialization

My Grails app has the following Spring bean defined in spring/resources.groovy
calendarService(CalendarService) { bean ->
bean.initMethod = "init"
}
This method looks something like:
class CalendarService {
void init() {
User.findByEmail("foo#doo.com")
}
}
When I call the dynamic finder findByEmail I get a MissingMethodException. My guess is that I'm trying to call this method too early, i.e. before the domain classes have had the dynamic finders added to their metaclass. One solution would be to call CalendarService.init() myself from Bootstrap.init, rather than instructing Spring to call it, but is there a better solution?
Thanks,
Don
You're right, as described in this post, if you need the dynamic methods you'd better go with BootStrap.groovy
BootStrap {
def calendarService
def init() {
calendarService.init()
}
}
The following works without any config in resources.groovy
class CalendarService {
#PostConstruct
private void init() {
User.findByEmail("foo#doo.com")
}
}

Resources