Spring-MVC + Spring-websocket + #Cacheable don't work - java

I have got a project on Spring-MVC and Spring-websockets and I try to plug cache on my service layer. These are my configuration:
#Configuration
#ComponentScan(basePackages = {
"com.example"
})
#PropertySource("classpath:/configuration.properties")
#EnableWebMvc
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableCaching
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCache = new EhCacheManagerFactoryBean();
ehCache.setConfigLocation(new ClassPathResource("ehcache.xml"));
ehCache.setShared(true);
return ehCache;
}
#Bean
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehcache().getObject());
}
//...different settings by mvc
}
and my websocket configuration:
#Configuration
#EnableAsync
#EnableWebSocket
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/", "/topic/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/locations").withSockJS();
}
#Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(4).maxPoolSize(10);
}
}
I want to use #Cacheable annotation on my service layer:
#Service
public class StoreServiceImpl implements StoreService {
private static final Log logger = LogFactory.getLog(StoreServiceImpl.class);
#Autowired
private StoreRepository storeRepository;
#Override
#Cacheable("stores")
public Store findById(String storeId) {
return storeRepository.findById(storeId);
}
//... others methods
}
but if I have included annotation #EnableWebSocketMessageBroker then the cache doesn't work, because aop interceptors do not use it, so
if I haven't included #EnableWebSocketMessageBroker then cache and AOP interceptors work well.
The documentation on the websocket I found this information:
In some cases a controller may need to be decorated with an AOP proxy
at runtime. One example is if you choose to have #Transactional
annotations directly on the controller. When this is the case, for
controllers specifically, we recommend using class-based proxying.
This is typically the default choice with controllers. However if a
controller must implement an interface that is not a Spring Context
callback (e.g. InitializingBean, *Aware, etc), you may need to
explicitly configure class-based proxying. For example with
<tx:annotation-driven />, change to <tx:annotation-driven proxy-target-class="true" />
I tried use #EnableCaching(proxyTargetClass = true), but it didn't help.
Has anyone encountered this problem?

I decided this problem:
I changed mode in #EnableAsync(mode = AdviceMode.ASPECTJ) and it works.
I think it depends of the order initialization BeanPostProcessors

Related

spring boot boostrap bean override

In my spring.factories I have such a definition:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
MyBootstrapConfiguration
In MyBootstrapConfiguration I have :
#Configuration
public class MyBootstrapConfiguration {
#Bean
#ConditionalOnMissingBean
public ApiClient apiClient() {
return someClient;
}
}
Now in my test (junit-5 and #SpringBootTest), I would like to override this bean. Notice the #ConditionalOnMissingBean... If I can hook somehow to a "before" my bootstrap is started, to provide that ApiClient, that method apiClient would obviously not be called and I would be happy.
Please notice that MyBootstrapConfiguration is not something I have control of - its an invariant to me. For non-bootstrap configuration this is easily doable, but is there a way to do this for bootstrap?
Given a test configuration class:
#Configuration
public class TestBootstrapConfiguration implements Ordered {
#Bean
public ApiClient testApiClient() {
return testApiClient;
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
In src/test/resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
TestBootstrapConfiguration

Can not call interceptor when using #AutoConfigureAfter(WebMvcAutoConfiguration.class) and annotation-driven

While implementing the interceptors in my spring boot application I came across a weird problem which is as follows:
My Application class code:
#Import(CommonConfig.class)
#EnableAsync
#SpringBootApplication
#EnableSwagger2
#EnableScheduling
#EnableSpringDataWebSupport
#ImportResource({ "classpath:META-INF/applicationContext.xml" })
#AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class Application extends SpringBootServletInitializer {
private static final Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
.......
................
}
#Bean
public TokenInterceptor intializeTokenInterceptor() {
System.out.println("Adding interceptors");
return new TokenInterceptor();
}
#Bean
public WebMvcConfigurerAdapter adapter() {
return new WebMvcConfigurerAdapter() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("Adding interceptors");
registry.addInterceptor(intializeTokenInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
};
}
}
Inside my application-context I have mentioned tag (mvc:annotation-driven) as I need this for initializing my bean classes.
Now, if I test my application by removing the annotation-driven tag, my interceptors are getting called. However, if I keep this tag, my interceptors are not getting called, (which I believe is overriding the implementation of WebMvcConfigurerAdapter bean from WebMvcAutoConfiguration.class somehow).

Add some extra configuration to beans defined in Spring starters

TL;DR
What is the best way to add some configuration to already created beans, e.g. to bean created by Spring Auto Configuration mechanism?
UseCase
I'm trying to configure a ContentNegotiatingViewResolver in a best possible way. I tried to create a new instance of that object in my MvcConfiguration class, and configure everything in that place. It worked, but I was thinking about something more elegant.
And I found a WebMvcAutoConfiguration, with viewResolver(BeanFactory beanFactory) method, that creates a ContentNegotiatingViewResolver bean. I decided I would like to use this, since it's better to use existing code, than duplicate it.
But how can I add some more configuration to that bean? I tried with something like this:
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ContentNegotiatingViewResolver contentNegotiatingViewResolver;
#Autowired
private ThymeleafViewResolver thymeleafViewResolver;
#Bean
public ViewResolver jsonViewResolver() {
return new JsonViewResolver();
}
#PostConstruct
public void postConstruct() {
contentNegotiatingViewResolver.setViewResolvers(Arrays.asList(jsonViewResolver(), thymeleafViewResolver));
}
}
and configure everything in #PostConstruct method but I wonder, if it is the best way.
Make use of Autowiring by constructor.
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
private ContentNegotiatingViewResolver contentNegotiatingViewResolver;
private ThymeleafViewResolver thymeleafViewResolver;
#Autowired
public MvcConfiguration(ContentNegotiatingViewResolver contentNegotiatingViewResolver,ThymeleafViewResolver thymeleafViewResolver){
this.contentNegotiatingViewResolver=contentNegotiatingViewResolver;
this.thymeleafViewResolver=thymeleafViewResolver;
ViewResolver jsonViewResolver= new JsonViewResolver();
this.contentNegotiatingViewResolver.setViewResolvers(Arrays.asList(jsonViewResolver, thymeleafViewResolver));
}
}
Alternate solution :
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation();
registry.viewResolver(jsonViewResolver());
super.configureViewResolvers(registry );
}
}

#Transactional Services

I passed one day and a half looking for answer, but this thing is going to put me crazy!
My teammates and I are working on an project, based on springboot. I work specificaly on the administration part, which is a web administration.
There are mainly three layers on my project: Controllers which use Services which use Repositories.
I want my project work with #Transactional for the Service layer (we made some successful efforts until now to use only annotations for configuration).
But, it seems that it doesn't work: One of my service throws a RuntimeException and no rollback is done. I allready read all the proposition in the others sibling subjects. The only thing, related to my problem, that i'm not sure to do neatly is the contexts configuration. Eventhow, i'm not sure that it's really my problem.
I show you the actual configuration:
#SpringBootApplication
#EnableScheduling
#EnableTransactionManagement
public class Application extends SpringBootServletInitializer {
#Value("${ajp.port}")
private int ajpPort;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {};
tomcat.addAdditionalTomcatConnectors(createConnector(ajpPort));
return tomcat;
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> {
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/static/401.html");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/static/404.html");
container.addErrorPages(error401Page, error404Page);
};
}
#Bean
public EmailValidator emailValidator() {
return EmailValidator.getInstance();
}
private static Connector createConnector(int ajpPort) {
Connector connector = new Connector("AJP/1.3");
connector.setPort(ajpPort);
return connector;
}
}
The web config:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private RequestProcessingTimeInterceptor requestProcessingTimeInterceptor;
#Autowired
private CertificateInterceptor certificateInterceptor;
#Autowired
private ProfilesAuthorizationInterceptor profilesAuthorizationInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestProcessingTimeInterceptor);
registry.addInterceptor(certificateInterceptor);
registry.addInterceptor(profilesAuthorizationInterceptor);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setExposeContextBeansAsAttributes(true);
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/admin/css/**").addResourceLocations("/WEB-INF/admin/css/").setCachePeriod(CACHE_PERIOD);
registry.addResourceHandler("/admin/img/**").addResourceLocations("/WEB-INF/admin/img/").setCachePeriod(CACHE_PERIOD);
registry.addResourceHandler("/admin/js/**").addResourceLocations("/WEB-INF/admin/js/").setCachePeriod(CACHE_PERIOD);
registry.addResourceHandler("/admin/plugins/**").addResourceLocations("/WEB-INF/admin/plugins/").setCachePeriod(CACHE_PERIOD);
}
}
A Controler-like:
#RestController
#RequestMapping("/pathA")
public class ControlerA {
#Autowired
public ServiceA serviceA;
#RequestMapping(value = "{id}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public A getA(#PathVariable long id) {
return serviceA.getA(id);
}
}
A Service-like (interface + implémentation):
public interface ServiceA {
A getA(long id);
}
#Service
#Transactional
public class ServiceAImpl implements ServiceA {
#Autowired
public RepositoryA repositoryA;
public A getA(long id) {
(...)
A a = repositoryA.findOne(id);
a.updatesomething(something);
repositoryA.update(a);
doOtherThing(a); //throw RuntimeException
(...)
return a;
}
}
And the Repository:
#Repository
public interface RepositoryA extends JpaRepository<A, Long> {
(...)
}
Here is the configuration of the MySQL database:
# Configuration de la base de donnée
spring.datasource.url=jdbc:mysql://localhost/name_innodb
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.testOnBorrow=true
spring.datasource.validationQuery=SELECT 1
I know that repository transaction works by default (I saw it, when a SQLException happen). But in the service layer, nothing happen (cf. the throwing exception line) ; when the exception is thrown, the update is done and not rollback. Then it mean that my #Transactional is ignored.
Edit :
I manage to get a transaction like I want, adding #Transactional on the method getA(...) of the Controller. It works, but it's not the place to manage Transaction.
Then my question is: How can I make it work?
Ok, after some days of brainstorming, I found!
The only reasonnable answer is to take care about your Configuration class. My problem was only a crossover configuration problem which leaded to a DispatcherServlet configuration who caused the mess.
Related Subject: For web MVC Spring app should #Transactional go on controller or service?
Edit:
I add some details because it'll be hard to find some information in order to separate context. And I'm still calibrating the configuration because there's no complete and exhaustive information about all the spring's annotations.
You could create parent and child context like this:
#SpringBootApplication
#ComponentScan({"com.mycompany.service", "com.mycompany.interceptors","com.mycompany.manager"})
#PropertySource("file:config/application.properties")
public class ParentConfig{
public static void main(String[] args) {
new SpringApplicationBuilder()
.parent(ParentConfig.class)
.child(ChildConfig1.class, ChildConfig2.class, ChildConfig3.class, ..., ChildConfigN.class)
.run(args);
}
(...)
}
I'm still wondering why I must add the #PropertySource in order children are aware of property values, why "classpath:path" have not work in #PropertySource, why I have to add a static PropertySourcesPlaceholderConfigurer for using #Value in my children (before I do that, i.e without this hierarchical contexts, every context was aware of the properties)
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
and I'm still playing with annotations in order every configuration work.
Edit:
I finally have found something in order to work correctly with spring configuration: Different configurations must respect packaging hierarchy.
I stop working with parent and child configuration and let spring work. I ordonate my different config class like this:
MainConfig
|
|__________my.package.mvc.MVCConfig
|
|__________my.package.schedulers.SchedulerConfig
|
|
and so on..
And in my MainConfig I add:
#ComponentScan({"my.package.mvc", "my.package.services", "my.package.interceptors","my.package.managers", "my.package.schedulers"})
And everything is good now! Mostly, MVCConfig can not create conflict with services, because of the different hierarchy.

Java config for spring interceptor where interceptor is using autowired spring beans

I want to add spring mvc interceptor as part of Java config. I already have a xml based config for this but I am trying to move to a Java config. For interceptors, I know that it can be done like this from the spring documentation-
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor());
}
}
But my interceptor is using a spring bean autowired into it like follows-
public class LocaleInterceptor extends HandlerInterceptorAdaptor {
#Autowired
ISomeService someService;
...
}
The SomeService class looks like follows-
#Service
public class SomeService implements ISomeService {
...
}
I am using annotations like #Service for scanning the beans and have not specified them in the configuration class as #Bean
As my understanding, since java config uses new for creating the object, spring will not automatically inject the dependencies into it.
How can I add the interceptors like this as part of the java config?
Just do the following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
LocaleInterceptor localInterceptor() {
return new LocalInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeInterceptor());
}
}
Of course LocaleInterceptor needs to be configured as a Spring bean somewhere (XML, Java Config or using annotations) in order for the relevant field of WebConfig to get injected.
The documentation for general customization of Spring's MVC configuration can be found here, and specifically for Interceptors see this section
When you handle the object creation for yourself like in:
registry.addInterceptor(new LocaleInterceptor());
there is no way the Spring container can manage that object for you and therefore make the necessary injection into your LocaleInterceptor.
Another way that could be more convenient for your situation, is to declare the managed #Bean in the #Configuration and use the method directly, like so:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public LocaleInterceptor localeInterceptor() {
return new LocaleInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( localeInterceptor() );
}
}
Try to inject your service as a constructor parameter. It is simple.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
ISomeService someService;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleInterceptor(someService));
}
}
Then reconfigure your interceptor,
public class LocaleInterceptor extends HandlerInterceptorAdaptor {
private final ISomeService someService;
public LocaleInterceptor(ISomeService someService) {
this.someService = someService;
}
}
Cheers !

Categories