I am trying to avoid dao call from controller class . If the call is done from service package then dao call should be succesfull else I will throw an exception . I dont want to write this logic in each method of dao class so planned to use aspectj to intercept the dao call . How can I prevent dao from controller and allow it from service class only . Shall I use any other api/approach . Any suggetion
package com;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DaoAspect {
@Before(value = "execution(* com.dao.*.*(..))")
public void beforeAdvice( JoinPoint joinPoint) {
// here I want to know the caller package/class
// if its com.service allow ,if com.controller reject
}
}
Disclaimer: I am not a Spring user. Maybe there is an easier on-board means to achieve this via interceptors, Spring Security or what have you. But you asked for an AOP solution.
We first have to differentiate between
@DeclareError
When using AspectJ and just recompiling your legacy library with the AspectJ compiler (drop-in replacement for Java compiler with AOP enhancements), you can use @DeclareError
in order to make compilation fail if a call from the wrong class or package pattern(s) is found. This way you would not need any expensive runtime checks, reflection or other tricks. For your Maven build you can use AspectJ Maven plugin. See my answers here for further information about @DeclareError
:
This is what I recommend: Detect the invalid calls at build time, fix the code in order to make it compile and be sorrow-free during runtime.
call()
pointcutIf however you either don't want to use the AspectJ compiler (even though you tagged the question aspectj and not spring-aop) or have no compile-time influence on the calling code, you can still use AspectJ load-time weaving (LTW) from Spring. Spring AOP is definitely not enough if you want to avoid creating Exceptions for the sole purpose of analysing their callstacks in order to find the caller. Instead, in full AspectJ there is a pointcut named call()
which is unavailable in Spring AOP. You can weave into the calling code via LTW and then use EnclosingStaticPart
in order to find the caller. The only caveat is that in Spring you might use proxies instead of direct calls, which might mess up the caller by indirection, but you can give it a try.
Here is an MCVE in plain Java + AspectJ (no Spring involved):
DAO, service, controller:
package com.dao.ddd;
public class MyDao {
public void doSomething() {
System.out.println("Doing something in DAO");
}
}
package com.service.sss;
import com.dao.ddd.MyDao;
public class MyService {
public void doSomething() {
System.out.println("Doing something in service");
new MyDao().doSomething();
}
}
package com.controller.ccc;
import com.dao.ddd.MyDao;
public class MyController {
public void doSomething() {
System.out.println("Doing something in controller");
new MyDao().doSomething();
}
}
package de.scrum_master.app;
import com.controller.ccc.MyController;
import com.service.sss.MyService;
public class Application {
public static void main(String[] args) {
// Allowed
new MyService().doSomething();
// Forbidden
new MyController().doSomething();
}
}
Driver application:
package de.scrum_master.app;
import com.controller.ccc.MyController;
import com.service.sss.MyService;
public class Application {
public static void main(String[] args) {
// Allowed
new MyService().doSomething();
// Forbidden
new MyController().doSomething();
}
}
Console log without aspect:
Doing something in service
Doing something in DAO
Doing something in controller
Doing something in DAO
Actually the list line should not be printed because a call from the controller to the DAO is forbidden.
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.EnclosingStaticPart;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class ContractEnforcerAspect {
@Before("call(* com.dao..*(..))")
public void beforeAdvice(JoinPoint joinPoint, EnclosingStaticPart enclosingStaticPart) {
System.out.println(" Callee = " + joinPoint.getSignature());
System.out.println(" Caller = " + enclosingStaticPart.getSignature());
if (enclosingStaticPart.getSignature().getDeclaringType().getPackageName().startsWith("com.controller"))
throw new RuntimeException("DAO must not be called from controller");
}
}
Console log with aspect:
Doing something in service
Callee = void com.dao.ddd.MyDao.doSomething()
Caller = void com.service.sss.MyService.doSomething()
Doing something in DAO
Doing something in controller
Callee = void com.dao.ddd.MyDao.doSomething()
Caller = void com.controller.ccc.MyController.doSomething()
Exception in thread "main" java.lang.RuntimeException: DAO must not be called from controller
at de.scrum_master.aspect.ContractEnforcerAspect.beforeAdvice(ContractEnforcerAspect.aj:17)
at com.controller.ccc.MyController.doSomething(MyController.java:8)
at de.scrum_master.app.Application.main(Application.java:11)