HttpSessionEventPublisher not working in java config - java

I have a mature java web application that uses spring security (4.2.3, java8, tomcat8), and includes a view that allows a user to query the session registry to see who else is logged in. This depends on HttpSessionEventPublisher to notify the session registry when a user logs out.
The original implementation used web.xml config, but I have mostly reimplemented that using java config now. The last thing I refactored was the HttpSessionEventPublisher.
When I moved it from a web.xml declaration into my WebSecurityConfigurerAdapter it stopped working. Specifically, logged-out sessions are no longer being removed from the registry.
I know the bean is being instantiated (the factory method is being called).
This is the java config suggested in the reference docs:
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
Is there some trick needed to get this to work in java config?

HttpSessionEventPublisher should be registered as a standard listener not as spring bean. check the documentation.
So, I think you need to register it using WebApplicationInitializer as follows:
public final class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(final ServletContext ctx) {
ctx.addListener(new HttpSessionEventPublisher());
}
}

Related

Spring 4: run some initialization code before application context refresh

I am using Spring 4 (not Spring Boot) in the web application. I need to run some initialization code before any of the beans in the application context would be created. I tried to create implementation of org.springframework.context.ApplicationContextInitializer and register it in spring.factories but was not picked up for some reason. How can I do it?
As it turned out implementing of org.springframework.context.ApplicationContextInitializer was a right way. Because in my project I do not use Spring MVC implementation of this initializer should be registered in web.xml instead of spring.factories. Here is an example:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>my.company.MyContextInitializer</param-value>
</context-param>
This should work. If not, please post your code.
#Component
public class SampleBootstrap implements ApplicationListener<ContextRefreshedEvent> {
....
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Do Something();
}
}

Spring Boot Camel - Autowiring issues in Camel components

I am using Spring Boot 1.5.7 and Apache Camel 2.19.3, using Spring Boot AutoConfiguration provided by spring-boot-starter-camel
It is pretty basic Spring Boot and Camel initialized as in their tutorial, so we have a RouteBuilder component that does exactly that.
#Component
public class CIFRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
// build routes
}
}
We have a Configuration that defines some beans we need in our application
#Configuration
public class MyConfiguration {
#Bean
public void Map<String, Object> map() {
return new HashMap<>()
}
}
Finally, we have a custom InflightRepository implementation that should be scanned by auto-configuration and added to the CamelContext which basically works, but for some reason, the component doesn't get initialized properly. Means, its dependencies are not initialized but the bean is instantiated and injected in my Application.
#Component
public class MyCustomInflightRepository extends DefaultInflightRepository {
#Autowired
private Map<String, Object> map;
#Override
public void add(Exchange exchange) {
super.addExchange(exchange);
// ....
}
}
The problem now is that map remains (null), I also tried adding a #PostConstruct initializer method but it doesn't get called.
As far as I was able to reconstruct, it seems to be connected to premature in CamelAutoConfiguration where the CamelContext bean gets instantiated (done in private method afterPropertiesSet.
InflightRepository inflightRepository = getSingleBeanOfType(applicationContext, InflightRepository.class);
if (inflightRepository != null) {
LOG.info("Using custom InflightRepository: {}", inflightRepository);
camelContext.setInflightRepository(inflightRepository);
}
If MyCustomInflightRepository doesn't implement InflightRepository, the bean is initialized correctly, but indeed not recognized by Camel. When disabling auto-configuration, the bean's dependencies are injected.
So, either I'm doing the impossible by Spring standards or there's something fishy with the Camel component for Spring.
I'm a bit quick on resolving this (I wanted to post this two days ago already^^), but a colleague figured out what could be the problem.
When using CamelAutoConfiguration the InflightRepository bean (or practicially everything for which Camel tries to get a matching bean here), the context is accessed before property resolvers are fully initialized which leads to the bean being initialized (and cached in context) before any auto-wired properties can be resolved.
I'm not a Spring expert but this behavior is a bit problematic in my opinion because uninitialized beans are pulled into the CamelContext when you rely on Spring DI for your custom components.
To be sure, I'll raise this with the maintainers...
By the way, my simple solution was to manually setting the in-flight repository in context configuration (as suggested)
#Bean
public CamelContextConfiguration camelConfig() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
context.setInflightRepository(new MyCustomInflightRepository(/* Dependencies go here */ ));
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
}
};
}
Also it seems to be an issue when use camel-http-starter in your project which isn't recommended, they claim it is deprecated.
So, either don't do DI (regardless if via property or constructor injection) for your camel-managed beans or skip that starter.
The problem is that a Map<String,Object> is too vague for Spring to be able to understand what you want; I think the default behavior is that it'll give you all beans keyed by name.
Instead, be more specific, or possibly provide the necessary parameters as constructor arguments and configure them explicitly in an #Bean method (it's a good idea to always use constructor injection anyway).

In JBoss How do I trigger the code in my war? [duplicate]

I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?

Java servlet to remember object instance [duplicate]

I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?

OpenEntityManagerInViewFilter annotation config

In order to overcome LazyInitializationException I've decided to use OpenEntityManagerInViewFilter but I couldn't do it annotation style. I've tried set it up two ways:
First in onStartup method from WebApplicationInitializer:
OpenEntityManagerInViewFilter entityManagerInViewFilter = new OpenEntityManagerInViewFilter();
entityManagerInViewFilter.setEntityManagerFactoryBeanName("entityManagerFactory");
entityManagerInViewFilter.setPersistenceUnitName("defaultPersistenceUnit");
FilterRegistration.Dynamic filter = sc.addFilter("openEntityManagerInViewFilter", entityManagerInViewFilter);
filter.addMappingForUrlPatterns(null, false, "/*");
Second by creating new class that extends OpenEntityManagerInViewFilter and has annotation #WebFilter:
#WebFilter(urlPatterns = {"/*"})
public class MainFilter extends OpenEntityManagerInViewFilter {
public MainFilter() {
setEntityManagerFactoryBeanName("entityManagerFactory");
setPersistenceUnitName("defaultPersistanceUnit");
}
}
Each time I got "No bean named 'entityManagerFactory' is defined" or "No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined". My entity manager factory is defined in #Configuration class.
How can I configure this filter without web.xml file?
Because the boot process can be pretty unclear (which can lead to wrong configurations), I decided to give a detailed answer, but also a "too long; didn't read" for those who want to jump to the answer immediately.
TL;DR
The easiest way is one of these two options:
Option 1: OpenEntityManagerInViewInterceptor
This is closer to Spring than OpenEntityManagerInViewFilter and thus may be easier to configure when you also need to configure it with other beans.
Use something like this for your web configuration:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan(basePackages = "mypackages")
public class WebConfig implements WebMvcConfigurer {
#Autowired
private EntityManagerFactory emf;
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(emf);
registry.addWebRequestInterceptor(interceptor);
}
}
The important part the addInterceptors(), in which the interceptor is created and added to Spring Web.
And to point Spring Web to config class, you need something like this:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{MainConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
MyServletInitializer will be automatically detected by Spring, because it's a subclass of AbstractAnnotationConfigDispatcherServletInitializer, which is an implementation of WebApplicationInitializer. And Spring Web will automatically be detected by the Servlet container, because Spring Web contains a service provider implementation of ServletContainerInitializer by default, which gets detected automatically. This implementation is called SpringServletContainerInitializer.
Option 2: OpenEntityManagerInViewFilter
If you don't want to use the interceptor, you can use the filter.
The filter has a wider range scope in which an entityManager can be shared per HTTP request, but interceptors are closer to Spring, making it easier to wire beans with it (in case needed).
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{MainConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new OpenEntityManagerInViewFilter()};
}
}
Additional notes:
Make sure that you have correct names of entityManagerFactoryBean. See documentation of OpenEntityManagerInViewFilter, or my detailed description below.
Make sure that configurations aren't imported multiple times, otherwise you can get hard to debug problems. For more info, see my detailed description below.
Why/when to use OpenEntityManagerInViewFilter
OpenEntityManagerInViewFilter is an implementation of the OSIV pattern (Open Session In View) for JPA. (A Session is the native Hibernate version of an EntityManager. So for JPA it can be called: Open EntityManager In View pattern.) In this pattern, a single EntityManager is shared/reused within a single HTTP request, and closed when the HTTP request finishes.
The benefits of using the OSIV pattern are:
better database performance compared to when a single HTTP request causes multiple different transactions and entitymangers to be opened
allows JPA to use 1st level caching
easier programming, you don't have to worry about LazyInitializationExceptions, which can be caused when the entityManager is closed and you access methods on entities that require a persistent context
it may be required when you're using custom Spring security filters that use JPA.
But it may also cause the persistent context to be opened longer than needed, which may harm performance.
Some also consider the OSIV pattern as an anti-pattern: https://vladmihalcea.com/the-open-session-in-view-anti-pattern/ This web page also discusses other solutions to avoid both LazyInitializationExceptions and the OSIV pattern.
Servlet 3.0 and configuration options
Since Servlet 3.0, the servlet specification supports pluggability, it allows you to switch from web.xml to programmatic configurations and annotation configurations. Although annotation configurations don't support all features that web.xml and programmatic configurations do, such as ordering of servlets and filters. When using annotations only, it can be cumbersome if you have to extend and existing filters just to be able to configure it, as you do here:
#WebFilter(urlPatterns = {"/*"})
public class MainFilter extends OpenEntityManagerInViewFilter
An easy way to configure the web app in Spring is to use a programmatic configuration. When a Servlet >= 3.0 container starts (such as Tomcat >= 7), it will search for web.xml and for a ServletContainerInitializer service provider implementations (which also requires you to define a file in META-INF/services). The ServletContainerInitializer implementation allows the servlet container to be configured programmatically.
Connecting Spring to the Servlet container
Spring Web by default contains an implemenation of ServletContainerInitializer, called SpringServletContainerInitializer, so that you don't have to create an implementation of ServletContainerInitializer yourself. This makes it easier for framework designer to configure their framework to work with the container.
To make use of this, you have to create an implementation of Springs WebApplicationInitializer interface. This implementation is automatically discoverd by Spring (SpringServletContainerInitializer). It provides you a ServletContext instance, which allows you to configure the container (servlets and filters) programmatically.
An easier way is to create a subclass of AbstractAnnotationConfigDispatcherServletInitializer (which is an implementation of WebApplicationInitializer), so that you don't have to configure everything directly using the ServletContext.
How to achieve OSIV
Multiple ways to achieve OSIV. The best option may differ per project.
In OSIV, you want the EntityManager to the opened/started at the beginning of the HTTP request and to be closed when the corresponding HTTP response ends. Demarcation is the proccess of opening and closing the entityManager and transaction. Depending on what type of web application or framework you are using, you can shift a bit in the exact location of demarcation.
Some demarcation options:
Option 1 - bypass Spring, annotations-only, OpenEntityManagerInViewFilter (not recommended)
You can bypass Spring by letting the OpenEntityManagerInViewFilter be directly detected by the servletContainer, or by subclassing it and annotating it, or by specifying it in web.xml.
This is similar to your approach:
#WebFilter(urlPatterns = {"/*"})
public class MainFilter extends OpenEntityManagerInViewFilter
This approach is not recommended because it's a bit cumbersome.
Option 2 - manually programmatically via WebApplicationInitializer, OpenEntityManagerInViewFilter
In this approach you create an implemenatation of Springs WebApplicationInitializer to get hold of the ServletContext.
Then you can create and configure the openEntityManagerInViewFilter manually:
OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
FilterRegistration.Dynamic registration = registerServletFilter(servletContext, filter);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), true, "/*");
Note you'll also have to create the Spring ApplicationContext and DispatcherServlet yourself. See the documentation of WebApplicationInitializer for an example.
Option 3 - programmatically via AbstractAnnotationConfigDispatcherServletInitializer, OpenEntityManagerInViewFilter (recommended)
This is the main approach to enable OpenEntityManagerInViewFilter in a Spring application.
As described earlier, the servlet container will automatically detect Springs SpringServletContainerInitializer, and then Spring will automatically detect our MyServletInitializer (see below) because its superclass (AbstractAnnotationConfigDispatcherServletInitializer) is an implementation of WebApplicationInitializer.
From MyServletInitializer, Spring will load your core application config, the rootConfigClasses, in which you can define your JPA beans.
Example:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{MainConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new OpenEntityManagerInViewFilter()};
}
}
Spring main config file example:
#EnableJpaRepositories
#Configuration
#EnableTransactionManagement
public class MainConfig {
private Properties hibernateProperties() {
Properties props = new Properties();
props.setProperty("hibernate.hbm2ddl.auto", "update");
props.setProperty("hibernate.dialect", "org.hibernate.dialect.MariaDB10Dialect");
return props;
}
#Bean
public DataSource dataSource() {
Properties properties = new Properties();
properties.put("url", "jdbc:mariadb://localhost:3306/myDatabase");
properties.put("user", "root");
properties.put("password", "myPassword");
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.mariadb.jdbc.MariaDbDataSource");
config.setMaximumPoolSize(10);
config.setDataSourceProperties(properties);
return new HikariDataSource(config);
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager jpaTxManager = new JpaTransactionManager();
jpaTxManager.setEntityManagerFactory(emf);
return jpaTxManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource());
HibernateJpaDialect jpaDialect = new HibernateJpaDialect();
emf.setJpaDialect(jpaDialect);
emf.setJpaProperties(hibernateProperties());
emf.setPackagesToScan("mypackage");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
emf.setJpaVendorAdapter(vendorAdapter);
return emf;
}
}
Spring web config example:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan(basePackages = "mypackages")
public class WebConfig {}
Option 4: no filter, #Transactional on Spring controller methods
If your application doesn't require filters, and your template layer is rendered from within a Spring controller, than you can demarcate the controller method with #Transactional instead of using pure OSIV.
In this case, make sure #EnableTransactionManagement is set on the Spring configuration and the controller method is annotated with #org.springframework.transaction.annotation.Transactional
Note that this doesn't work when you are for example using JSP or other common templates, because then the text isn't generated in the controller method. Instead, a model and view object is created, which is then forwarded to the template for rendering, but once the controller method is finished, the entityManager is closed before the rendering, which can cause the LazyInitializationException when you access lazy loaded entity properties.
Option 5 - OpenEntityManagerInViewInterceptor instead of a filter (recommended)
Whereas filters are part of the servlet api, webRequestInterceptors are part of Spring Web and thus a bit closer to Spring.
One of these webRequestInterceptors is OpenEntityManagerInViewInterceptor, which is the interceptor version of OpenEntityManagerInViewFilter.
The documentation says: "In contrast to OpenEntityManagerInViewFilter, this interceptor is set up in a Spring application context and can thus take advantage of bean wiring."
So where the OpenEntityManagerInViewFilter is initialized in a WebApplicationInitializer (such as AbstractAnnotationConfigDispatcherServletInitializer), OpenEntityManagerInViewInterceptor can be initialized in a Spring configuration class.
Example:
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan(basePackages = "mypackages")
public class WebConfig implements WebMvcConfigurer {
#Autowired
private EntityManagerFactory emf;
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(emf);
registry.addWebRequestInterceptor(interceptor);
}
}
Note that the interceptor is in a deeper layer than the filter. If you have filters that need to use an entityManager, than the interceptors cannot provide it, but the other way around should work.
Usage info about OpenEntityManagerInViewFilter
As described in the documentation of OpenEntityManagerInViewFilter, you have to make sure that a Spring bean exist under the right name, otherwise OpenEntityManagerInViewFilter may give the error that you describe: "No bean named 'entityManagerFactory' is defined".
From the documentation:
Looks up the EntityManagerFactory in Spring's root web application
context. Supports an "entityManagerFactoryBeanName" filter init-param
in web.xml; the default bean name is "entityManagerFactory". As an
alternative, the "persistenceUnitName" init-param allows for retrieval
by logical unit name (as specified in persistence.xml).
In your question, you didn't show how you did define the EntityManagerFactory exactly.
How to test whether it is working
Even when you don't get a LazyInitializationException, it doesn't mean that entitymanager is shared.
To verify whether it is working correctly, you can put org.springframework at DEBUG logging level in your logging framework.
When testing, preferably use a tool like Wget or Curl instead of a webbrowser, because a webbrowser may trigger multiple HTTP requests (such as favicon.ico), which makes the logs less clear.
When a HTTP requests triggers entityManager requests in multiple repositories, all should use the same EntityManager, so for a single HTTP request, you should only see a single line with something like:
21:48:46.872 [http-nio-8080-exec-5] DEBUG o.s.o.j.s.OpenEntityManagerInViewInterceptor - Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
and a single:
21:48:46.878 [http-nio-8080-exec-5] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
And when you have multiple entityManager requests in multiple repositories, you should see that they are using the same thread-bound entityManager. So you should see multiple lines with:
21:48:46.876 [http-nio-8080-exec-5] DEBUG o.s.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [SessionImpl(1847515591<open>)] for JPA transaction
Problems while configuring
I had a difficult to debug problem when trying to disable web.xml to something like web.xml.old, because IntelliJ somehow made a mapping in the IntelliJ project configuration to the renamed web.xml.old, causing it to still use the xml configuration.
Another problem can be when your Spring configurations are somehow imported multiple times, for example if you use #import in the webConfiguration to import the mainConfiguration and you specify them both in your AbstractAnnotationConfigDispatcherServletInitializer, then things may be imported multiple times, causing hard to debug problems.
If your configuration is unclear because you have many, you may want to create a constructor with a simple println message so verify that it's only created once.
WebApplicationInitializer is a fine general-purpose way of registering servlets, filters,and listeners in Java when deploying to a Servlet 3.0 container. But if you’re registering a filter and only need to map that filter to DispatcherServlet, then there’s a
shortcut in AbstractAnnotationConfigDispatcherServletInitializer.
To register one or more filters and map them to DispatcherServlet, all you need
to do is override the getServletFilters() method of AbstractAnnotationConfigDispatcherServletInitializer.
For example, the following getServletFilters()
method overrides the one from AbstractAnnotationConfigDispatcherServletInitializer
to register a filter:
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new MyFilter() };
}
Here is my snippet of WebAppInitializer code
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
protected Class<?>[] getRootConfigClasses() {
System.out.println("*****加载MySqlConfig*******");
return new Class<?>[] { MySqlConfig.class };
}
protected Class<?>[] getServletConfigClasses() {
System.out.println("*****加载WebConfig*******");
return new Class<?>[] { WebConfig.class };
}
protected String[] getServletMappings() {
System.out.println("*****要拦截的请求*******");
return new String[] { "/" };
}
//Register your filter here
protected Filter[] getServletFilters() {
System.out.println("*********加载filter**********");
return new Filter[]{new OpenEntityManagerInViewFilter()};
}
}

Categories