I try to watch my spliterator with AspectJ aspects for logging purpose. However it seems impossible. The aspect never gets called and IntelliJ tell me that the advice advices no method. Im using AspectJ 1.8.1.
You'll find a SSCCE at github: https://github.com/flaviait/StreamSpliteratorAspectJExample
Spliterator with annotated aspect:
public class RepositorySpliterator<T> implements Spliterator<T> {
#Override
#SpliteratorWatch
public boolean tryAdvance(Consumer<? super T> action) {
...
}
}
My Aspect:
#Aspect
#Component
public class SpliteratorWatchAspect {
private static final Logger logger = LoggerFactory.getLogger(SpliteratorWatchAspect.class);
#After("#annotation(spliteratorWatch)")
public void watch(JoinPoint joinPoint, SpliteratorWatch spliteratorWatch) throws Throwable {
...
}
}
And last but not least the Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SpliteratorWatch {
}
I did this serveral times in different contexts and it works fine. Is there any known issue with the Java 8 stream api, spliterators and aspects? Maybe someone can help me. That would be great!
Related
In my application, I have a component that reads data from other system when the application is started.
However, during testing, I don't want this component to be created
#Component
#Slf4j
public class DeviceStatisticsSyncHandler {
#EventListener
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
}
I can use condition to solve this, but other code readers need to understand, so I don't think this is a very good method:
#EventListener(condition = "#deviceStatisticsSyncHandler .isServiceEnabled()")
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
public boolean isServiceEnabled() {
return !serviceEnabled;
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
My application doesn't use Profiles, is there any other method to solve this problem.
Spring Boot version:2.1.3
One possible option is not to load the DeviceStaticsticsSyncHandler at all if you're in a test mode.
The "test.mode" is not a good name here, because the production code contains something tightly bound to the tests.
How about the following approach:
#Component
#ConditionalOnProperty(name ="device.stats.handler.enabled", havingValue = "true", matchIfMissing=true)
public class DeviceStatisticsSyncHandler {
// do whatever you need here, but there is no need for "test.mode" enabled related code here
}
Now in Tests you can define a test property "device.stats.handler.enabled=false" on the test itself or even place that definition in src/test/reources/application.properties so it will be false for all tests in the module.
An obvious advantage is that this definition is pretty much self explanatory and can be easy understood by other project maintainers.
for me, it's not the case of the condition rather environment-related. I will solve this problem using spring profile.
Step 1: Create an Interface first
public interface DeviceStatisticsSyncHandler {
public void handle(ApplicationReadyEvent event);
}
Step 2: Create an Implementation for production
#Component
#Profile("!test")
public class DeviceStatisticsSyncHandlerImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
}
step 3: create an implementation of test
#Component
#Profile("test")
public class DeviceStatisticsSyncHandlerTestImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
//do Nothing
}
}
final step
All you need to do is set/toggle the property
-Dspring.profiles.active=test
or
-Dspring.profiles.active=prod
I found a way to achieve this without any further external configuration required.
The idea is to create a general configuration that applies to all integration tests and use #MockBean there to replace the real bean. So one should create a class like this under the test classpath (i.e. that is not scanned during normal application launch):
#Configuration
public class IntegrationTestConfiguration
{
#MockBean
public DeviceStatisticsSyncHandler deviceStatisticsSyncHandler;
}
I was actually surprised that #MockBean can be used here, but the Javadoc explicitly points that out: Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner..
I'm trying to develop a log interceptor for some methods in our AMQP-Project.
I'm using Springs #Aspect-Annotation and a custom annotation to create a interceptor. Our AMQP-Controller implements the javax.jms.MessageHandler interface and overrides the onMessage(Message)-Method i want to intercept.
The class has the following signature:
public abstract class AMQPController implements MessageListener {
...
#Override
public void onMessage(Message message){
// application logic
}
...
}
My interceptor looks like this:
#Slf4j
#Aspect
#Component
public class LoggedInterceptor {
#Around("execution(public void javax.jms.MessageListener+.onMessage(..))")
public Object intercept(ProceedingJoinPoint joinPoint) {
// application logic
}
}
Every time a new AMQP-Message is triggered the onMessage()-Method is triggered but my interceptor does not seem to be triggered.
Does anyone has an idea why?
Greetings from Germany :-)
Try to use it,
#Around(value = "execution (* javax.jms.MessageListener.onMessage(..))")
For spring boot application.
I have my aspect listen on my private or public method inside my scheduled method.
But it doesn't work. However, the aspect can listen on my scheduled method.
Here is an example on my github.
https://github.com/benweizhu/spring-boot-aspect-scheduled
Does any know the answer or why? or how to resolve it?
Thanks
Aspects will not work on calling other methods within the same class as it cannot be proxied.
It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen
note on proxying private methods :
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
refer : How can I log private methods via Spring AOP?
change the code as below:
#Component
public class Test{
public void reportInPrivateMethod() {
System.out.println("private method");
}
public void reportInPublicMethod() {
System.out.println("public method");
}
}
Now invoke this method :
#Component
public class ScheduledTasks {
#Autowired
private Test test;
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
test.reportInPrivateMethod();
test.reportInPublicMethod();
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
Modify the aspects as per the changes :
#Aspect
#Component
public class Monitor {
#AfterReturning("execution(* com.zeph.aop.ScheduledTasks.reportCurrentTime())")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPrivateMethod())")
public void logServiceAccessPrivateMethod() {
System.out.println("Completed PRIVATE :");
}
#AfterReturning("execution(* com.zeph.aop.Test.reportInPublicMethod())")
public void logServiceAccessPublicMethod() {
System.out.println("Completed PUBLIC: ");
}
}
I struggle to use aspect in Spring MVC project.
Method that is a pointcut is running fine, but without advise.
Here is class, that starts whole spring boot and that is root of spring context:
#Lazy
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Configuration
public class MainSpringBootClass{
public static void main(String[] args)
{
SpringApplication.run(MainSpringBootClass.class, args);
}
}
Here is class with method, that is pointcut.
#Component
#Log
#Aspect
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleClass
{
public void example()
{
System.out.println("example");
}
}
And here is my aspect:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyAspect implements MethodBeforeAdvice
{
#Pointcut("execution(* com.example.MyExampleClass.example())")
public void asd()
{
// pointcut
}
#Before("asd()")
public void login()
{
System.out.println("im am logging in");
}
#Before("execution(* com.example.MyExampleClass.example())")
public void login2()
{
System.out.println("im am logging in2");
}
#Override
public void before(Method method, Object[] args, Object target) throws Throwable
{
System.out.println("aop before");
}
}
And here is my controller:
#RestController
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleController
{
private final MyExampleClass myExampleClass;
#Inject
public AdController(MyExampleClass myExampleClass)
{
this.myExampleClass = myExampleClass;
}
#RequestMapping("/")
public String index()
{
myExampleClass.example();
return "x";
}
}
As You can see, I have been trying to bruteforce correct result with annotations.
I have also seen on some website, that I need specific dependencies, so here are mine (pasting only those, related to aspects):
compile 'org.springframework:spring-aop:+'
compile 'org.aspectj:aspectjrt:+'
compile 'org.aspectj:aspectjweaver:+'
compile 'cglib:cglib:+'
compile 'cglib:cglib-nodep:+'
All dependencies have been successfully downloaded, project compiles and runs fine.
When I hit localhost:8080 then I see returned value "x", and inside logs I see "example".
However, I do not see any advices from spring aop nor aspectj - what am I doing wrong?
I am just using this project as sandbox to learn aspects, so I would be eager to learn, how to do it with each of Spring AOP and AspectJ.
The most important thing for me is to do it all without XML.
EDIT:
I have added simple constructor to MyAspect with println to check, if it is created (as it is normal spring bean with #Component after all) and it does - it is correctly created by spring.
EDIT 2:
IntelliJ IDEA tells me about methods login and login2: "This advice advices no method", but at the same time, I am able to jump (with control-click) from string, that is value in annotations to correct implementations.
All you should need is something like this:
#Aspect
#Component
public class MyAspect {
#Before("execution(* com.example.MyExampleClass.example(..))")
public void logBefore(JoinPoint pjp) throws Throwable {
System.out.println("before...");
}
}
You might have to replace all of the aspectJ dependencies with spring-boot-starter-aop .
Here's an example project that works (see RestControllerAspect.java):
https://github.com/khoubyari/spring-boot-rest-example
Well I really don't know why this does not work:
Every jar needed is in the place. Including aspectjrt.
Basically I start with configuration class:
#Configuration
#ComponentScan(basePackages = { "some.path" })
#EnableAspectJAutoProxy
public class SomeConf { ... }
Then I have my Aspect:
#Component
#Aspect
public class ControllerLoggerAspect {
#Pointcut("execution(* some.path.ATest.*(..))")
private void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
}
Under some.path I have Atest class:
package some.path;
public class ATest {
public void dummyMethod(){
System.out.println("\n\n\nDummy static executed\n\n\n");
}
}
Imagine now that I have controller:
#Controller
#RequestMapping(value = "/mapping")
public class SomeController {
#RequestMapping(value = "/something")
public ResponseEntity<String> publish(#RequestParam("Id") Long[] ids) {
//aspect should be invoked here
new ATest().dummyMethod();
return new ResponseEntity<>("{ \"status\": \"stubbed\"}", HttpStatus.OK);
}
}
Everything is invoked properly except aspect method. No errors, no exceptions, nothing. Any ideas?
Spring AOP only works on Spring beans, i.e. if you want to intercept one of its methods, class ATest needs to be a #Component.
You need to configure the advice you want execute. The pointcut only helps in determining the join points. The advice will be executed, not the pointcut.
You could write a Advice like this using your pointcut "aspectTest()":
#Before("aspectTest()")
public void beforeAdvice(){
System.out.println("\n\n ASPECT WORKING \n\n");
}
Or you could just replace your Pointcut annotation in the example with an advice annotation like this:
#Before("execution(* some.path.ATest.*(..))")
public void aspectTest() {
System.out.println("\n\n ASPECT WORKING \n\n");
}
Here you have a list of all adivces:
#Before("aspectTest()")
#After("aspectTest()")
#AfterReturning(pointcut = "aspectTest()", returning="retVal")
#AfterThrowing(pointcut = "aspectTest()", throwing="ex")
#Around("aspectTest()")