I am new to Spring and AOP. I am trying this simple thing where I have created a custom annotation which when placed before any method should execute some code.
This is the annotation I created
// Declares a custom annotation that validates json
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonSchemaAnnotation {
}
Next I created the Spring Aspect class which holds the logic
#Aspect
public class UpdateUIMetadataInterceptor {
#Pointcut("execution(public * com.fico.cardinal.cm.*.*(..))")
public void anyPublicMethod() {
System.out.println("Running");
}
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
}
And this is my simple test class
public class ValidationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/configuration.xml");
String jsondata = "{\"id\": \"EXPENSE_REPORT\",\"properties\": {\"transactionType\": \"EXPENSE_REPORT\"},\"sections\": []} ]}";
ValidationTest test = new ValidationTest();
test.jsonValidationTest("dummy", jsondata);
((AbstractApplicationContext) context).close();
}
#JsonSchemaAnnotation
public void jsonValidationTest(String dummy, String jsondata) {
System.out.println("Success");
}
The problem is my spring aop never gets triggered. I have included a bean in my configuration.xml
<aop:aspectj-autoproxy>
<aop:include name="UpdateUIMetadataInterceptor" />
</aop:aspectj-autoproxy>
<bean id="updateUI" class="com.fico.cardinal.cm.interceptor.UpdateUIMetadataInterceptor" />
Can anyone point out what I am missing?
You have several problems with your code:
You should create your ValidationTest object as a bean managed by Spring and not using new
<aop:include name="UpdateUIMetadataInterceptor" /> should be <aop:include name="updateUI"/>; you can actually just stick with <aop:aspectj-autoproxy/> for simplicity here
ProceedingJoinPoint is not supported for before aspects, so remove it; you can use JoinPoint instead if you need access to arguments
JsonSchemaAnnotation jsonSchemaAnnotation parameter should be present for validateJson method of your aspect, as pointed out by frant.hartm
I think you need either fully qualified name or a parameter in the method:
FQN:
#Before("anyPublicMethod() && #annotation(your.package.JsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
Parameter:
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp, JsonSchemaAnnotation jsonSchemaAnnotation ) throws Throwable {
System.out.println("Running");
}
Source: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts
(and you also need to use the bean, as Dmitry Kuskov pointed out
Related
I want to capture the argument of #transactional if it is applied at class level.
for e.g. if #transactional applied at method level like :-
class A {
#transactional(readOnly= true)
public void someMethod(){
// some code...
}
}
then I am able to Intercept and capture the formal argument i.e. readOnly with this code like :-
#Aspect
#Component
#Order(0)
public class ReadOnlyRouteInterceptor {
private static final Logger logger = LoggerFactory.getLogger(ReadOnlyRouteInterceptor.class);
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Transactional transactional) {
if (transactional.readOnly())
//do something
}
However the above code will not work if #transactional applied at class level as :-
#transactional(readOnly= true)
class A {
public void someMethod(){
// some code...
}
}
Now in order to Intercept the #transactional annotation which is applied at class level I have following code :-
#Pointcut("#within(org.springframework.transaction.annotation.Transactional *)")
public void beanAnnotatedWithTransactional() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithTransactional()")
public void publicMethodInsideAClassMarkedWithATransactional() {}
My actual problem here is I am unable to check the value of readOnly flag if #transactional is applied at class level.
For type level annotation :
#Around("#within(transactional)")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
boolean readOnly = transactional.readOnly();
...
return pjp.proceed();
}
For method level annotation:
#Around("execution(public * *(..)) && #annotation(org.springframework.transaction.annotation.Transactional)")
public Object myMethod(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Transactional annotation = method.getAnnotation(org.springframework.transaction.annotation.Transactional.class);
boolean value = annotation. readOnly();
...
return pjp.proceed();
}
Another (cleaner) option for method level :
#Around("#annotation(transactional)")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
}
or
With more control over tager :
#Around("execution(#org.springframework.transaction.annotation.Transactional public * *(..)) && #annotation("transactional")
public Object myMethod(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
}
You may use the TransactionSynchronizationManager to get reference to the transaction details.
Following code provides the readonly details of the current active transaction.
import org.springframework.transaction.support.TransactionSynchronizationManager;
#Component
#Aspect
public class TestTransactionalAspect {
#Pointcut("#within(org.springframework.transaction.annotation.Transactional)")
public void beanAnnotatedWithTransactional() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("publicMethod() && beanAnnotatedWithTransactional()")
public void publicMethodInsideAClassMarkedWithATransactional(ProceedingJoinPoint pjp) {
try {
System.out.println("Intercepted "+pjp.toShortString());
if (TransactionSynchronizationManager.isActualTransactionActive()) {
System.out.println("Is transaction readonly : "+TransactionSynchronizationManager.isCurrentTransactionReadOnly());
}
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Update:
Assuming this is not a Spring Boot project , #EnableTransactionManagement is mandatory for your spring application to work with transactions. Data will get persisted to database without transactions as well.
#EnableTransactionManagement annotation has to be used on
#Configuration classes.
Following is a sample code to enable transaction management in spring
#Configuration
#EnableTransactionManagement
public class AppConfig {
#Bean
public FooRepository fooRepository() {
// configure and return a class having #Transactional methods
return new JdbcFooRepository(dataSource());
}
#Bean
public DataSource dataSource() {
// configure and return the necessary JDBC DataSource
}
#Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
}
Based on the answers above I tried with the following code it is very simple.
/**
* This Aspect advice will be called only if transactional applied at method level
*/
#Around("#annotation(transactional)")
public Object proceedWithMethodLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
Transactional transactional) {
if( transactional.readOnly() ) {
//do something
}
/**
* This Aspect advice will be called only if transactional annotation applied at class level
*/
#Around("#within(transactional)")
public Object proceedWithClassLevelAnnotation(ProceedingJoinPoint proceedingJoinPoint,
Transactional transactional)
if( transactional.readOnly() ) {
//do something
}
}
I was wondering if it is possible to do the following trick with jersey restful resources:
I have an example jersey resource:
#Path("/example")
public class ExampleRessource {
#GET
#Path("/test")
#CustomPermissions({"foo","bar"})
public Response doStuff() {
//implicit call to checkPermissions(new String[] {"foo","bar"})
}
private void checkPermissions(String[] permissions) {
//stuff happens here
}
}
What I want to achieve is: before executing each resource's method to implicitly check the rights from the annotation by calling the checkPermissions method without actually writing the call inside the method body. Kind of "decorating" each jersey method inside this resource.
Is there an elegant solution? For example with jersey Provider?
Thx!
With Jersey 2 can use ContainerRequestFilter.
#Provider
public class CheckPermissionsRequestFilter
implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
}
}
We can get the annotation on the called method through the ResourceInfo class
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext crc) throws IOException {
Method method = info.getResourceMethod();
CheckPermissions annotation = method.getAnnotation(CheckPermissions.class);
if (annotation != null) {
String[] permissions = annotation.value();
}
}
You can use this annotation
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckPermissions {
String[] value();
}
And annotate the resource class or the resource method with #CheckPermissions({...})
See more at Filters and Interceptors
UPDATE
The annotation above allows for annotating classes also. Just for completeness, you'll want to check the class also. Something like
Class resourceClass = info.getResourceClass();
CheckPermissions checkPermissions = resourceClass.getAnnotation(CheckPermissions.class);
if (checkPermissions != null) {
String[] persmission = checkPermissions.value();
}
I am using Spring4 along with Spring Boot.
Before I tired to use AOP, my Bean(CommandService),which is used in the controller, is auto injected well, but after I tired to use AOP to collect some debug message, the bean becomes null!
Here is my Application.java
#Configuration
#EnableAutoConfiguration
#ComponentScan({"hello","wodinow.weixin.jaskey"})
public class Application extends {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
LogUtil.info("Beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
LogUtil.info(beanName);
}
LogUtil.info("Application Boots completes!");
}
#Bean
public CommandService commandService(){
LogUtil.debug("CommandService.getInstance()"+ CommandService.getInstance()) ;//here indeed I could see spring executes this and returns a object when application boots
return CommandService.getInstance();//This returns a singleton instance
}
}
My controller that throws null pointer:
#Controller
public class CoreController {
#Autowired
CommandService commandService;//here the service is null after using aop
//...some request methods
}
The Aspect which I added just now:
//if I comment out these two annoations, the bean will be auto injected well
#Aspect
#Component
public class LogAspect {
#Pointcut("execution(* wodinow.weixin.jaskey..*.*(..))")
private void debug_log(){};
#Around("debug_log()")
public void debug(ProceedingJoinPoint joinPoint) throws Throwable{
LogUtil.debug("enter "+joinPoint.getSignature());
try{
joinPoint.proceed();
LogUtil.debug("returns from "+joinPoint.getSignature());
}
catch(Throwable t){
LogUtil.error(t.getMessage()+"occurs in "+joinPoint.getSignature(),t);
throw t;
}
}
}
I am new to Spring, can anybody help me with this?
Your #ComponentScan is trying to resolve and autowire your dependencies into CoreController. When it tries to resolve the dependency it finds the #Bean in your Application class. It then tries to resolve this dependency by calling Application.commandService(). When this method is called, it sees the matching #Pointcut and invokes your advice method. Since your #Advice is not returning anything, the callers will also see that nothing was returned, and it will say that that resolution of that dependency returned null.
The fix here is just to change your #Around advice to return the value of your invocation.
#Around("debug_log()")
public Object debug(ProceedingJoinPoint joinPoint) throws Throwable{
LogUtil.debug("enter "+joinPoint.getSignature());
try{
// return the invocation
return joinPoint.proceed();
}
catch(Throwable t){
LogUtil.debug(t.getMessage()+"occurs in "+joinPoint.getSignature(),t);
throw t;
}
}
You are used
joinPoint.proceed();
without return just add return to be
return joinPoint.proceed();
I want to have a AspectJ pointcut for methods annotated with #Scheduled. Tried different approaches but nothing worked.
1.)
#Pointcut("execution(#org.springframework.scheduling.annotation.Scheduled * * (..))")
public void scheduledJobs() {}
#Around("scheduledJobs()")
public Object profileScheduledJobs(ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("testing")
}
2.)
#Pointcut("within(#org.springframework.scheduling.annotation.Scheduled *)")
public void scheduledJobs() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Around("scheduledJobs() && publicMethod()")
public Object profileScheduledJobs(ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("testing")
}
Can anyone suggest any other way to have around/before advice on #Scheduled annotated methods?
The pointcut that you are looking for can be specified as below:
#Aspect
public class SomeClass {
#Around("#annotation(org.springframework.scheduling.annotation.Scheduled)")
public void doIt(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
pjp.proceed();
System.out.println("After");
}
}
I am not sure whether that's all you require or not. So I'm going to post the other parts of the solution as well.
First of all, notice the #Aspect annotation on the class. It is required for the methods in this class to be applied as advice.
Also, you need to make sure that the class that has the #Scheduled method is detectable via scanning. You can do so by annotation that class with #Component annotation. For ex:
#Component
public class OtherClass {
#Scheduled(fixedDelay = 5000)
public void doSomething() {
System.out.println("Scheduled Execution");
}
}
Now, for this to work, the required parts in your spring configuration would be as follows:
<context:component-scan base-package="com.example.mvc" />
<aop:aspectj-autoproxy /> <!-- For #Aspect to work -->
<task:annotation-driven /> <!-- For #Scheduled to work -->
How to make an aspect that targets all public methods that belong to a class that is marked with specific annotation? In following method1() and method2() should be processed by the aspect and method3() should not be processed by the aspect.
#SomeAnnotation(SomeParam.class)
public class FooServiceImpl extends FooService {
public void method1() { ... }
public void method2() { ... }
}
public class BarServiceImpl extends BarService {
public void method3() { ... }
}
If I put annotations on method level, this aspect will work and match the method calls.
#Around("#annotation(someAnnotation)")
public Object invokeService(ProceedingJoinPoint pjp, SomeAnnotation someAnnotation)
throws Throwable {
// need to have access to someAnnotation's parameters.
someAnnotation.value();
}
I am using Spring and proxy based aspects.
The following should work
#Pointcut("#target(someAnnotation)")
public void targetsSomeAnnotation(#SuppressWarnings("unused") SomeAnnotation someAnnotation) {/**/}
#Around("targetsSomeAnnotation(someAnnotation) && execution(* *(..))")
public Object aroundSomeAnnotationMethods(ProceedingJoinPoint joinPoint, SomeAnnotation someAnnotation) throws Throwable {
... your implementation..
}
This works in Spring Boot 2:
#Around("#within(xyz)")
public Object method(ProceedingJoinPoint joinPoint, SomeAnnotation xyz) throws Throwable {
System.out.println(xyz.value());
return joinPoint.proceed();
}
Note that based on the method argument type (SomeAnnotation xyz), Spring and AspectJ will know which annotation you are looking for, so the xyz does not have to be the name of your annotation.
Using #target and reading the type level annotation with reflection works.
#Around("#target(com.example.SomeAnnotation)")
public Object invokeService(ProceedingJoinPoint pjp) throws Throwable {