2

I am working with Spring Framework 4.3.3 in a Web Environment:

I have two contexts:

  • RootApplicationContext
  • ServletApplicationContext

I know the ServletApplicationContext contains all the beans about the web side, for example @Controller. Furthermore ServletApplicationContext is able to access all the context or beans from the RootApplicationContext for example @Service, @Repository etc. Until here I am fine.

Note it applies for @Configuration classes too. (Infrastructure)

Therefore with the previous introduction we can think in the following way:

  • ServletApplicationContext --> RootApplicationContext

Something important to take in consideration is that the inverse is not possible.

Therefore

  • RootApplicationContext --> ServletApplicationContext

is not possible. It has sense and is Ok. Server side should not access the Web side

About AspectJ. I have the following:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}

Here one important point:

  • This AopConfig is scanned by RootApplicationContext
    • I trust that ServletApplicationContext can refer that @Configuration through the access of RootApplicationContext

Ok, when I run my @Test methods.

When I execute a Test class from the server side I use

  • @ContextConfiguration(classes={RootApplicationContext.class} )
    • Only RootApplicationContext

And AOP works fine. I can confirm through AOP + logging the following process:

  • @Service -> @Repository

When I execute a Test class from the Web side I use:

  • @ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
    • RootApplicationContext and ServletApplicationContext

And AOP works fine. I can confirm through AOP + logging the following process:

  • @Controller -> @Service -> @Repository

Now for production I have:

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootApplicationContext.class};
    }

   @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ServletApplicationContext.class};
    }

But when I export the project for a .war file and through an URL/URI a Controller is executed the expected behaviour or process works fine. But about AOP through AOP + logging the following process happens:

  • @Service -> @Repository

@Controller does not appear for the output. The expected flow process should be:

  • @Controller -> @Service -> @Repository

So why works in Testing and not in production?

I already did a research and I have found these two posts:

Practically they say that the @Configuration class with @EnableAspectJAutoProxy should be scanned through ServletApplicationContext and not through RootApplicationContext

Even when it is true (according with new experiments), consider that Server side should be tested without a Web Environment.

For other @Beans about infrastructure through @Configuration the relation already explained about ServletApplicationContext --> RootApplicationContext works how is expected. Just with AOP has this situation.

Question 01: So why this behaviour?

Question 02: How keep the AopConfig scanned by RootApplicationContext and get the expected behaviour for production?

Note if AopConfig is scanned by ServletApplicationContext. The following about testing is valid and mandatory for the server side @ContextConfiguration(classes={RootApplicationContext.class, AopConfig.class} ). See the addition of AopConfig.class but I think AopConfig should be scanned by RootApplicationContext.

Community
  • 1
  • 1
Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158

1 Answers1

4

The answer is that @ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class}) in test environment and context inheritance in production is not the same things. In test environment you include RootApplicationContext and ServletApplicationContext as parts of your test application context. In production inheritance is used instead of a simple inclusion, as you described in your question.

Seems that BeanFactoryPostProcessor (which is @EnableAspectJAutoProxy in your case) from parent context not applied to child contexts. To make it work in production you must explicitly define @EnableAspectJAutoProxy in child context too.

In this case Spring context definition should be about as code below:

@Configuration
@Import(AopConfig.class)
public class RootApplicationContext {
    ...
}

@Configuration
@Import(AopConfig.class)
public class ServletApplicationContext {
    ...
}

Or

@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class RootApplicationContext {
    ...
}

@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class ServletApplicationContext {
    ...
}

Related Task

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
Sergey Bespalov
  • 1,746
  • 1
  • 12
  • 29
  • Hello, do you mean `@EnableAspectJAutoProxy` declared twice? It for both context? wondered if it has some performance impact. Apart, I remember an annotation about testing where applies `context inheritance`. Yes, it is `@ContextHierarchy`. I have confirmed the functionality of have two `@EnableAspectJAutoProxy`. I will check through a JIRA if is a bug or not... perhaps something special through a `@Configuration` would be added and works how should be expected. – Manuel Jordan Sep 26 '16 at 12:08
  • Cool! interesting what Spring will say – Sergey Bespalov Sep 28 '16 at 03:12
  • For the moment I am scanning twice from `RootApplicationContext` and `ServletApplicationContext` the *unique* class with `@EnableAspectJAutoProxy` – Manuel Jordan Sep 28 '16 at 13:03
  • Have you tried to use `@EnableAspectJAutoProxy` in both contexts like I suggest? [This](http://stackoverflow.com/questions/3310115/spring-aop-advice-on-annotated-controllers/35638595#35638595) answer confirms that `@EnableAspectJAutoProxy` do not affects in child servlet context. – Sergey Bespalov Sep 28 '16 at 14:49
  • Yes, like I said, my class with `@EnableAspectJAutoProxy` is scanned twice, one for each `Application Context` – Manuel Jordan Sep 28 '16 at 15:58
  • It works now... sorry by the confusion I forget to re run the app – Manuel Jordan Sep 28 '16 at 16:13
  • Pls sure to indicate in your answer that the class annotated with `@EnableAspectJAutoProxy` must be scanned twice by `RootApplicationContext` and `ServletApplicationContext`. Share the JIRA link how reference. Here the Spring version should be take it in consideration for the future too. – Manuel Jordan Sep 28 '16 at 16:15
  • 1
    Probably this is an offtopic, but I am not sure that `@ComponentScan` is better then `@Import` in that case. If you interested why then i can answer in separate question... :) – Sergey Bespalov Sep 30 '16 at 03:15
  • Using `@ComponentScan` you are able to scan components in two ways (even together): **one** by `basePackages` (`String[]` ie: `{"com.abc.a", "com.abc.b"}`) and **two** by `basePackageClasses` (`Class>[]` i.e: `{AbcConfig.class, XyzConfig.class, XServiceImpl.class}`) each class is used to detect its package and scan from there to all sub packages. Even ``@ComponentScan` has a filter to exclude some components to be scanned. `@Import` only has `value`, it apply for `Class>[]`. – Manuel Jordan Sep 30 '16 at 12:43
  • Is possible use `@Import` and it imports a class that has `@ComponentScan` and apply my approach. Normally I use `@Configuration` + `@ComponentScan`. `@Import` is not bad – Manuel Jordan Sep 30 '16 at 12:46