I have the following Spring AOP advice and I can't find out why it is called twice:
#Component
#Aspect
public class LoggingAspects {
Logger logger = LoggerFactory.getLogger(LoggingAspects.class);
#AfterReturning(pointcut = "execution(public * com.A.B.C.service.impl.*.browse(..))",
returning = "retVal")
public Object onBrowse(DomainClass retVal) {
logger.info("#######################Advice Called: +retVal);
return null;
}
}
The configuration is as simple as that:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="loggingCASAspect" class="com.minervanetworks.xtv.stb.service.aspects.LoggingCASAspects"/>
I have also tried the following advice with the same result (called twice).
#AfterReturning(pointcut="#annotation(com.A.B.C.service.impl.LOG)", returning="retVal")
public Object onBrowse(JoinPoint jp, DomainClass retVal) {
logger.info("#######################Advice called! " + jp.toLongString()
+ " Target: " + jp.getTarget()
+ " Signature: " + jp.getSignature()
+ " Kind: " + jp.getKind()
+ " This: " + jp.getThis()
+ " Source Location: " + jp.getSourceLocation());
return null;
}
The debug info from the above logger is:
2011-10-26 11:56:01,887 [INFO][com.A.B.C.service.aspects.LoggingAspects] #######################Advice called! execution(public abstract com.A.B.C.domain.DomainClass com.A.B.C.service.ContentManager.browse(java.lang.String,java.lang.String,java.lang.String,java.lang.Boolean)) Target: com.A.B.C.service.impl.ContentManagerImpl#62ad191 Signature: DomainClass com.A.B.C.service.ContentManager.browse(String,String,String,Boolean) Kind: method-execution This: com.A.B.C.service.impl.ContentManagerImpl#62ad191 Source Location: org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl#d324de2
It is displayed twice with exactly the same values.
1. hint
Do you use JavaConfig or xml?
If you're using both, it could be that the Aspect is being processed two times by the Spring IoC container.
2. hint
I don't annotate aspects with #Component annoation, try to remove it, maybe because of that is being processed twice by Spring.
I found that the advice can be called twice if the aspect is declared as request-scoped. In this case the same bean is registered twice as an interceptor with different names: someAspect and scopedTarget.someAspect. I solved this issue by making the Aspect a singleton.
Version: Spring Boot 2.0.x
It haven't been mentioned, but take care that your pointcut is not matching other method calls.
For example in my case, I was calling different Beans :
Bean A -> Bean B -> Bean C
I wanted to add an aspect to Bean A, but Bean B method signature was also matching the poincut, resolving in 2 methods calls.
I had the same issue caused by the annotation below.
#Aspect
#Service
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
The solution was to remove the Scope request annotation.
#Aspect
#Service
for me,remove the #Component is working
--before--
#Aspect
#Component
class Aop {
#Around("#annotation(*.*.*.*)")
fun around(pjp: ProceedingJoinPoint):Any?{
do something
}
}
--after--
#Aspect
class Aop {
#Around("#annotation(*.*.*.*)")
fun around(pjp: ProceedingJoinPoint):Any?{
do something
}
}
Related
I'm using a Repository that extends a spring data JpaRepository and would like to let it extend another interface.
Previously, my db repository looked like this:
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long> {
}
I have now created another interface TransitTicketRepo as defined below
interface TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket>
}
and now would like to implement the interface with a default method in PublicTransportPricingZoneRepository. I've tried to solve this by changing the code of my PublicTransportPricingZoneRepository to
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long>, TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket> {
// do something
return emptyList()
}
}
but get the following error message when starting the application.
org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Collection PublicTransportPricingZoneRepository.findPossibleTickets(Geometry); Reason: Failed to create query for method public abstract java.util.Collection...
I'm assuming the solution is to somehow tell spring data to stop auto-generating a query for findPossibleTickets but have been unable to find out how.
You can do it this way
In short.:
#Repository
// Your bean that will be glued together by spring
interface PublicTransportPricingZoneRepository extends JpaRepository<...>, PublicTransportPricingZoneCustomRepository {
// Other methods, but everything must be annotated with #Query or possible for spring to guess what it needs to do from its name (google derived methods)
}
interface PublicTransportPricingZoneCustomRepository {
// define custom methods
List<String> nameIsNotImportant();
}
#Service
// naming here is important - it must be names as interface + Impl, otherwise spring won't pick it up
// apart from that, it's a regular bean - you can Autowire, etc
class PublicTransportPricingZoneCustomRepositoryImpl implementats PublicTransportPricingZoneCustomRepository {
// Implement custom methods
#Overridden
public List<String> nameIsNotImportant() {
// impl
}
}
I'm facing an issue, like if my repository injected was not a Singleton.
I have a repository (in reality many, but let's make it simple) marked as #Singleton
#Module
#InstallIn(SingletonComponent::class)
class AppModule {
#Provides
#Singleton
fun provideSettingsRepository(#ApplicationContext context: Context): SettingsRepository {
return SettingsRepositoryImpl(context)
}
}
Here the implementation of my repository :
class SettingsRepositoryImpl(context: Context) : SettingsRepository {
private var _flow = MutableStateFlow("init value")
override fun getFlow(): StateFlow<String?> = _flow.asStateFlow()
override fun setMyValue(value:String) {
_flow.value = value
}
}
When I use it apart of my service (in some viewModels or others class with DI), it work perfectly. Today I implemented an AIDL service and wanted to do some DI. I had to use field injection because the service constructor has to be empty. It seems like the update made from my application isen't reported on my "TestApplication" who consume the Service (like if I had two instance of my repository).
Here the code of my service :
#AndroidEntryPoint
class AppService : Service() {
#Inject lateinit var settingsRepository: SettingsRepository
fun someActionOnMyRepository() {
settingsRepository.setMyValue("whatever")
}
}
When I set the value from my UI (viewModel or any other class who as the repository injected), it's not updated in my service. The flow doesn't contains the new value (tested by debug or logcat).
I'm expecting my settingsRepository to be a singleton. What am I missing ? Is it because the field injection ?
Best regards
Ok, the problem was not from Hilt but about how i declared my service in my AndroidManisfest.xml
<service android:name=".services.MyAppService"
android:process=":remote" <----- THIS
android:exported="true">
Making it like that make it on another process. That mean it's like another app instance (so no more Singleton / SharedPreferences).
I have this service (all in kotlin):
#Service
class MyService {
fun getSomeString(): String = "test"
}
And this integration test class:
#RunWith(SpringRunner::class)
#SpringBootTest
#EmbeddedKafka // used on some kafka tests
class BasicTests {
and method:
#Test
fun `test method count`() {
// here I have a kafka producer sending a message to a topic that ends up
// calling myService.getSomeString via #KafkaListener from other services
verify(someObjectRelatedToMyService, atLeast(1)).getSome()
}
In the place of someObjectRelatedToMyService I tried to use
#Autowired
lateinit var myService: MyService
But then I got Argument passed to verify() is of type MyService and is not a mock!
But when I use
#Mock
lateinit var myMock: MyService
I get Actually, there were zero interactions with this mock.
And actually, to me it makes sense, since my mock wasn't called, but my real service at the application was.
Is it possible to count method calls from my real object?
You can spy on the real object to count method calls on it like this:
#Test
fun `test method count`() {
Mockito.spy(someObjectRelatedToMyService)
verify(someObjectRelatedToMyService, atLeast(1)).getSome()
}
As you can see, the only thing you have to do is to call the spy method which enables tracking interactions with the target object.
When adding this call before the verify method, you should not get the error anymore that the object is not a mock.
[Posting here since no rep to comment] Have you tried using a #Spy? Then you could specify which methods to mock and which methods to call. I supposed you can also apply Mockito.verify on spies...
i develop a micro services application with Kotlin Webflux (Reactor3), Eureka, Zuul and Feign. Except that I always have an error when I make a call to an API via my micro service Feign. It looks like he can not deserialize the data. Could you please tell me if Feign is compatible with Flux and Monno?
thank you
{
"timestamp": "2019-05-29T07:39:43.998+0000",
"path": "/hobbies/",
"status": 500,
"error": "Internal Server Error",
"message": "Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of reactor.core.publisher.Flux (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 1, column: 1]"
}
Feign doesn't provide support for Mono/Flux deserialiazation. There exists alternative feign library who fully support it: feign-reactive.
Note though, this is a rewrite of feign which fully use reactive code, differ from OpenFeign's Feign core.
Here's a snippet on how to use it, alongside with normal Feign, taken from the sample app.
#SpringBootApplication(exclude = ReactiveLoadBalancerAutoConfiguration.class)
#RestController
#EnableReactiveFeignClients
#EnableFeignClients
public class FeignApplication {
#Autowired
private GreetingReactive reactiveFeignClient;
#Autowired
private Greeting feignClient;
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
#GetMapping("/greetingReactive")
public Mono<String> greetingReactive() {
return reactiveFeignClient.greeting().map(s -> "reactive feign! : " + s);
}
#GetMapping("/greeting")
public String greeting() {
return "feign! : " + feignClient.greeting();
}
}
In addition to Adhika Setya Pramudita response, I would like to mention that in order to return Mono in controller, you must use Spring WebFlux instead of Spring MVC
I was not able to make #Adhika Setya Pramudita solution working and gut tells me that it cannot even run due to mixing #EnableReactiveFeignClients and
#EnableFeignClients which are require corresponding #EnableWebFlux or #EnableWebMvc and thus defining both may compile but will fail in runtime.
Since op did not mention target language I'm feeling like to share Kotlin setup that works in my case:
build.gradle.kts
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.playtika.reactivefeign:feign-reactor-core:2.0.22")
implementation("com.playtika.reactivefeign:feign-reactor-spring-configuration:2.0.22")
implementation("com.playtika.reactivefeign:feign-reactor-webclient:2.0.22")
Config.kt
#Configuration
#EnableWebFlux
#EnableReactiveFeignClients
class Config {
}
MyEntity.kt
class MyEntity #JsonCreator constructor(
#param:JsonProperty("my_value") val my_value: String
)
MyFeignClient.kt
#Component
#ReactiveFeignClient(
url = "\${package.service.my-service-url}",
name = "client"
)
interface MyFeignClient {
#GetMapping(value = ["/my/url?my_param={my_value}"], consumes = ["application/json"])
fun getValues(
#PathVariable(name = "my_value") myValue: String?,
): Mono<MyEntity?>?
}
Then here goes code in some service class:
val myClient: MyFeignClient = WebReactiveFeign.builder<MyFeignClient>()
.contract(ReactiveContract(SpringMvcContract()))
.target(MyFeignClient::class.java, "http://example.com")
// feel free to add .block() to get unpacked value or just chain your logic further
val response = myClient.getValues(param)
I'm having a little trouble getting a logging aspect set up using SpringAOP + AspectJ. I would like an "Around" method to fire when either a class or method is annotated with the #Loggable annotation. Below is my advice code:
#Around(value = "execution( * *(..)) && target(bean) && #annotation(loggable)", argnames "bean, loggable")
public void test1(ProceedingJoinPoint method, Object bean, Loggable loggable) { }
#Around(value = "execution( * *(..)) && target(bean) && #within(loggable)", argnames "bean, loggable")
public void test2(ProceedingJoinPoint method, Object bean, Loggable loggable) { }
#Around(value = "execution( * *(..)) && target(bean) && (#annotation(loggable) || #within(loggable))", argnames "bean, loggable")
public void test3(ProceedingJoinPoint method, Object bean, Loggable loggable) { }
test1 and test2 fire. test3 does not, and it's the one that I really want. Any thoughts on why this might be?
First of all, there are syntax errors in your pointcuts. It is not lower-case argnames but argNames and you are missing an = in between parameter name and value. So it must be argNames = "bean, loggable".
Secondly if your advice returns void it will only match methods returning void as well. The more general case is to return Object in the advice to really match all methods.
Last but not least, you should see a warning which explains the problem with the third pointcut. This is displayed in your Eclipse IDE or on the AspectJ compiler's (ajc) log output:
ambiguous binding of parameter(s) loggable across '||' in pointcut
This means that you cannot say "bind one value or the other to the parameter 'loggable'". What if both conditions match? Which one should be assigned? You have two options, assuming your fully-qualified class name is de.scrum_master.app.Loggable:
A) No reference to #Loggable annotation needed:
This is the simple case. If #Loggable does not have any parameters you need to read, it is not necessary to bind it to a parameter. BTW, if you want your pointcut to also capture static methods, you should not bind target() either because the target would be null. Maybe in Spring-AOP this is irrelevant because it only works with Spring Beans anyway, but in full-featured AspectJ it would make a difference because it is more powerful.
#Around(value = "execution(* *(..)) && (#annotation(de.scrum_master.app.Loggable) || #within(de.scrum_master.app.Loggable))")
public Object withoutLoggableReference(ProceedingJoinPoint thisJoinPoint) {
Object bean = thisJoinPoint.getTarget();
System.out.println(thisJoinPoint + " -> " + bean);
return thisJoinPoint.proceed();
}
Or, equivalently:
#Around(value = "execution(* (#de.scrum_master.app.Loggable *.*)(..)) || execution(#de.scrum_master.app.Loggable * *.*(..))")
public Object withoutLoggableReference(ProceedingJoinPoint thisJoinPoint) {
Object bean = thisJoinPoint.getTarget();
System.out.println(thisJoinPoint + " -> " + bean);
return thisJoinPoint.proceed();
}
B) Reference to #Loggable annotation needed:
You have no other choice than to go with two separate pointcuts if you want to bind the annotations to parameters. Maybe you could use a utility method doing the actual logging in order to avoid code duplication in your advice.