AOP Spring Before Advice not working - spring

The method DefaultProduitGeneriqueService.valider is not catched by the method traceWhenReturnedValueDoesntExistOrNotNecessary and I don't understand why?
package fr.generali.nova.atp.service.metier.impl;
public class DefaultProduitGeneriqueService extends DefaultService implements IProduitGeneriqueService, IBacAware {
...
#Override
#Traceable(value = ETraceableMessages.VALIDATION_PRODUIT_GENERIQUE, hasReturnedValue=Traceable.HAS_NOT_RETURNS_VALUE)
public void valider(ElementNiveauUn element) {
...
}
...
}
package fr.generali.nova.atp.logging.advisor;
#Aspect
public class TraceableAdvisor {
#Before(value = "execution(* fr.generali.nova.atp.service.metier.impl.*.*(..)) && #annotation(traceable) && args(element)", argNames = "element,traceable")
public void traceWhenReturnedValueDoesntExistOrNotNecessary(ElementNiveauUn element, Traceable traceable) {
...
}
}

Assuming that the service interfaces are in package fr.generali.nova.atp.service.metier.api:
package fr.generali.nova.atp.service.metier.api;
public interface IProduitGeneriqueService {
void valider(ElementNiveauUn element);
}
And the service implementations are in package fr.generali.nova.atp.service.metier.impl:
package fr.generali.nova.atp.service.metier.impl;
public class DefaultProduitGeneriqueServiceImpl implements IProduitGeneriqueService {
#Override
#Traceable(value = ETraceableMessages.VALIDATION_PRODUIT_GENERIQUE, hasReturnedValue=Traceable.HAS_NOT_RETURNS_VALUE)
public void valider(ElementNiveauUn element) {
// TODO: implement
}
}
Your aspect should look like this:
package fr.generali.nova.atp.logging.advisor;
#Aspect
public class TraceableAdvisor {
#Before(value = "execution(* fr.generali.nova.atp.service.metier.api.*.*(..)) && #annotation(traceable) && args(element)", argNames = "element,traceable")
public void traceWhenReturnedValueDoesntExistOrNotNecessary(ElementNiveauUn element, Traceable traceable) {
// TODO: implement
System.err.println("traced...");
}
}
The default proxy strategy for Spring AOP is JDK interface-based proxies, so Your pointcut expression should match the interface method execution, not the implementation method execution, and Your poincut expression may match either interface mothod execution or implementation method execution.
And remember to include an AspectJAutoProxyCreator in your configuration using for example <aspectj-autoproxy /> tag.
And here is a simple test to prove everyting is working:
public class TraceableAdvisorTest {
#Configuration
public static class TestConfiguration {
#Bean
public IProduitGeneriqueService produitGeneriqueService() {
return new DefaultProduitGeneriqueServiceImpl();
}
#Bean
public TraceableAdvisor traceableAdvisor() {
return new TraceableAdvisor();
}
#Bean
public AnnotationAwareAspectJAutoProxyCreator autoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}
}
private AnnotationConfigApplicationContext testApplicationContext;
#Test
public void testTraceWhenReturnedValueDoesntExistOrNotNecessary() {
this.testApplicationContext = new AnnotationConfigApplicationContext();
this.testApplicationContext.register(TestConfiguration.class);
this.testApplicationContext.refresh();
IProduitGeneriqueService service = BeanFactoryUtils.beanOfType(this.testApplicationContext, IProduitGeneriqueService.class);
System.err.println("BEFORE");
service.valider(null);
System.err.println("AFTER");
}
}
The err output is:
BEFORE
traced...
AFTER
For all combinations:
fr.generali.nova.atp.service.metier.api.*.*(..)
fr.generali.nova.atp.service.metier.impl.*.*(..)
fr.generali.nova.atp.service.metier..*.*(..)

Make sure both beans are properly configured, either through annotations or in your appCtx.
It looks like your Aspect is definitely right, but how about the other class? Is it Spring enabled?
Also, if both classes are indeed configured correctly, are you sure that the instance being passed is a Spring bean and not a "new" instance from a constructor?

Related

advice is not executing if the annotations contains parameters

I'm trying to get some advice to execute.
When I use annotation without parameters its do execute but when the annotation includes parameters it's not.
#Aspect
class a{
#Pointcut("execution(#com.annotations.AnnotationName* *(..))")
void someMethod() {}
#Around("someMethod()")
public Object aroundSomeMethod(ProceedingJoinPoint pjp) throws Throwable
{
// some code
}
}
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AnnotationName
{
public String someString();
public boolean someBoolean();
}
The use of the annotation:
#AnnotationName(
someString= "string",
someBoolean = false
)
private void mycode()
{//code }
Following aspect code would advice a target method annotated with #AnnotationName
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("#annotation(annotationName) && within(so.qn69016852..*)")
private void someMethod(AnnotationName annotationName) {}
#Around("someMethod(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someString());
System.out.println(annotationName.someBoolean());
return pjp.proceed();
}
}
Couple of corrections/observations .
Spring AOP cannot advice a private method of a Spring bean. The mycode() method should be in a bean and ideally public. ( Refer )
The Aspect should also be a spring bean. This can be achieved by annotating the aspect with #Component
Remember to limit the scope : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#writing-good-pointcuts
You may also go through this answer from #kriegaex to understand why an #annotation has a global scope.
Update :
The code shared by OP also works with modifying a typo ( a space between the AnnotationName and * in the pointcut expression ) . The observations shared earlier holds good here as well.
#Component
#Aspect
public class SomeMethodAspect {
#Pointcut("execution(#so.qn69016852.anno.AnnotationName * so.qn69016852..*.*(..))")
private void someMethod() {}
#Around("someMethod() && #annotation(annotationName)")
public Object aroundSomeMethod(ProceedingJoinPoint pjp,AnnotationName annotationName) throws Throwable
{
System.out.println(annotationName.someBoolean());
System.out.println(annotationName.someString());
return pjp.proceed();
}
}

Spring - How to know if cglib or jdk dynamic proxy was applied to a bean

Im studying for the Spring Core cert exam, and i'm doing some testing of the framework.
I'd like to know if there is a way to know if a Bean was proxied by CGLIB or the JDK library.
I already know the basic concepts like if you declare a Bean using the interface Spring will use the JDK to proxy it (unless you tell it otherwise). And if you declare a bean directly on a class it will proxy it by inheritance using CGLIB.
What I would like to know is what should I look for while debugging to check which library was used.
Given the following code, when I debug it, I dont see any difference in the instances of the beans created. I was expecting to see something like ConcreteBean$CGLIB in the bean that has no interface...
EDIT: i now understand that proxies are only created by spring when functionality needs to be added by a PostProcessor, but still, i'd like to know what to look for in the debugger to find if CGLIB was applied or not.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MainConfig.class)
public class ProxiesTest {
#Autowired
RandomBean randomBean;
#Autowired
ConcreteBean concreteBean;
public void setUp() {
}
#Test
public void randomBeanTest() {
randomBean.doSomething();
}
#Test
public void concreteBeanTest() {
concreteBean.doSomething();
}
}
public class ConcreteBean {
public void doSomething() {
String concreteBean = "hello";
}
#PreDestroy
public void destroy() {
System.out.print("ConcreteBean Destroy");
}
}
public interface RandomBean {
public void doSomething();
public void destroy();
}
public class RandomBeanImpl implements RandomBean {
#Autowired
ApplicationContext context;
public void doSomething() {
context.getParentBeanFactory();
}
public void destroy() {
System.out.print("RandomBean destroyed");
}
}
#Configuration
#ComponentScan(basePackages = "com.certification.postprocessors")
public class MainConfig {
#Bean
public ConcreteBean concreteBean(){
return new ConcreteBean();
}
#Bean
public RandomBean randomBean() {
return new RandomBeanImpl();
}
}
When a bean is wrapped by a Spring CGLIB proxy it states $$EnhancerBySpringCGLIB.
A JDK proxy is shown as $Proxy
It looks like this in the debugging console

how to avoid using context.getbean in spring

There have been several arguments around not using ApplicationContext.getBean() to get a bean reference, of which most are based on logic that it violates the principles of Inversion of control.
Is there a way to get reference to prototype scoped bean without calling context.getBean() ?
Consider to use Spring Boot!
Than you can do something like this...
Runner:
#SpringBootApplication
public class Runner{
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
Some Controller:
#Controller
public class MyController {
// Spring Boot injecting beans through #Autowired annotation
#Autowired
#Qualifier("CoolFeature") // Use Qualifier annotation to mark a class, if for example
// you have more than one concreate class with differant implementations of some interface.
private CoolFeature myFeature;
public void testFeature(){
myFeature.doStuff();
}
}
Some cool feature:
#Component("CoolFeature") // To identify with Qualifier
public class CoolFeature{
#Autowired
private SomeOtherBean utilityBean;
public void doStuff(){
// use utilityBean in some way
}
}
No XML files to handle.
We can still access context for manual configurations if needed.
Suggested reading:
Spring Boot Reference
Pro Spring Boot
This type of problem can be solved using method injection, which is described in more detail here: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-method-injection
This is the most common approach to create prototype bean:
abstract class MyService {
void doSome() {
OtherService otherService = getOtherService();
}
abstract OtherService getOtherService();
}
#Configuration
class Config {
#Bean
public MyService myService() {
return new MyService() {
OtherService getOtherService() {
return otherService();
}
}
}
#Bean
#Scope("prototype")
public OtherService otherService() {
return new OtherService();
}
}

Spring Aspects (AspectJ) does not seem to work

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()")

Testing Mock Bean in Spring with Spock

I'm being hit with the issue that spock doesn't allow Mocks to be created outside of the specification - How to create Spock mocks outside of a specification class?
This seems to be still outstanding so am asking is that giving that i've got a complex and nested DI graph what is the most efficient way to 'inject' a mock representation deep in the graph?
Ideally, I have one bean definition set for normal deployment and another when running unit tests and it is this definition set being the applicable Mocks
e.g.
#Configuration
#Profile("deployment")
public class MyBeansForDeployment {
#Bean
public MyInterface myBean() {
return new MyConcreateImplmentation();
}
}
&&
#Configuration
#Profile("test")
public class MyBeansForUnitTests {
#Bean
public MyInterface myBean() {
return new MyMockImplementation();
}
}
Since Spock 1.1, you can create mocks outside of a specification class (detached mocks). One of the options is DetachedMockFactory. Take a look at the documentation or my answer to the question you linked.
You could try to implement a BeanPostProcessor that will replace the beans that you want with test doubles, such as shown below:
public class TestDoubleInjector implements BeanPostProcessor {
...
private static Map<String, Object[]> testDoubleBeanReplacements = new HashMap<>();
public void replaceBeanWithTestDouble(String beanName, Object testDouble, Class testDoubleType) {
testDoubleBeanReplacements.put(beanName, new Object[]{testDouble, testDoubleType});
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (testDoubleBeanReplacements.containsKey(beanName)) {
return testDoubleBeanReplacements.get(beanName)[TEST_DOUBLE_OBJ];
}
return bean;
}
In your test, setup your mocks like shown below before initializing the application context. Make sure to include the TestDoubleInjector as a bean in your test context.
TestDoubleInjector testDoubleInjector = new TestDoubleInjector()
testDoubleInjector.replaceBeanWithTestDouble('beanToReplace', mock(MyBean.class), MyBean.class)
It could be done using HotSwappableTargetSource
#WebAppConfiguration
#SpringApplicationConfiguration(TestApp)
#IntegrationTest('server.port:0')
class HelloSpec extends Specification {
#Autowired
#Qualifier('swappableHelloService')
HotSwappableTargetSource swappableHelloService
def "test mocked"() {
given: 'hello service is mocked'
def mockedHelloService = Mock(HelloService)
and:
swappableHelloService.swap(mockedHelloService)
when:
//hit endpoint
then:
//asserts
and: 'check interactions'
interaction {
1 * mockedHelloService.hello(postfix) >> { ""Mocked, $postfix"" as String }
}
where:
postfix | _
randomAlphabetic(10) | _
}
}
And this is TestApp (override the bean you want to mock with proxy)
class TestApp extends App {
//override hello service bean
#Bean(name = HelloService.HELLO_SERVICE_BEAN_NAME)
public ProxyFactoryBean helloService(#Qualifier("swappableHelloService") HotSwappableTargetSource targetSource) {
def proxyFactoryBean = new ProxyFactoryBean()
proxyFactoryBean.setTargetSource(targetSource)
proxyFactoryBean
}
#Bean
public HotSwappableTargetSource swappableHelloService() {
new HotSwappableTargetSource(new HelloService());
}
}
Have a look at this example https://github.com/sf-git/spock-spring

Resources