How to customize Jersey using Spring using only annotations

I have a Servlet 3.0 web application that uses both Spring and Jersey. I have currently configured it using SpringServlet, configured as a filter in web.xml, and resource classes annotated with both @Path and @Component . Here is a snippet of web.xml:

 <filter> <filter-name>jersey-serlvet</filter-name> <filter-class> com.sun.jersey.spi.spring.container.servlet.SpringServlet </filter-class> <init-param> <param-name> com.sun.jersey.config.property.packages </param-name> <param-value>com.foo;com.bar</param-value> </init-param> <init-param> <param-name>com.sun.jersey.config.feature.FilterForwardOn404</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>jersey-serlvet</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

This setting works, but I really want to get this setting only with annotations - there is no web.xml configuration. My first attempt was to remove the above SpringServlet configuration and create a class that extends Application . Here is a snippet of this:

 @ApplicationPath("/*") public class MyApplication extends PackagesResourceConfig { public MyApplication() { super("com.foo;com.bar"); HashMap<String, Object> settings = new HashMap<String, Object>(1); settings.put(ServletContainer.FEATURE_FILTER_FORWARD_ON_404, true); this.setPropertiesAndFeatures(settings); } } 

This works in that the JAX-RS resources are registered and I can hit them at their urls, but they throw NullPointerExceptions when they try to use their properties with autosave ... it makes sense because I assume that the resources Jersey is now loading and Spring beans is not managed, so there is no auto device.

Despite a fair search, I cannot find a way to load Jersey resources like Spring beans with annotations only. Is there such a way? I really don't want to write a bunch of code for resources to manually extract the Spring context and call the DI if I can help it.

If the annotations just don't work, then I can live with the filter configuration in web.xml, if I can specify the Application class to load instead of the list of packages for scanning. If I can get rid of the list of packages there and just specify an instance of the Application class, then I will be pleased.

Obviously, it would be great if someone had a definitive answer for me, but I would also be grateful for any pointers or hints on where else I could look or try something.

Thanks Matt

+7
source share
5 answers

Below is the part of my application that uses Servlet 3.0, Spring, Jersey 1.8 and does not have web.xml:

 public class WebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.setConfigLocation("com.myapp.config"); final FilterRegistration.Dynamic characterEncodingFilter = servletContext.addFilter("characterEncodingFilter", new CharacterEncodingFilter()); characterEncodingFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*"); characterEncodingFilter.setInitParameter("encoding", "UTF-8"); characterEncodingFilter.setInitParameter("forceEncoding", "true"); final FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy()); springSecurityFilterChain.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*"); servletContext.addListener(new ContextLoaderListener(context)); servletContext.setInitParameter("spring.profiles.default", "production"); final SpringServlet servlet = new SpringServlet(); final ServletRegistration.Dynamic appServlet = servletContext.addServlet("appServlet", servlet); appServlet.setInitParameter("com.sun.jersey.config.property.packages", "com.myapp.api"); appServlet.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", "com.myapp.api.SizeLimitFilter"); appServlet.setLoadOnStartup(1); final Set<String> mappingConflicts = appServlet.addMapping("/api/*"); if (!mappingConflicts.isEmpty()) { throw new IllegalStateException("'appServlet' cannot be mapped to '/' under Tomcat versions <= 7.0.14"); } } 

}

+2
source

I could not get my perfect result, but I was able to make some progress, so I will post here if this helps someone else. I was able to use the Spring servlet to specify my application class, thereby removing the list of packages from web.xml.

The necessary changes to web.xml are in the init parameters (filter mapping is not displayed, but is still required):

 <filter> <filter-name>jersey-serlvet</filter-name> <filter-class> com.sun.jersey.spi.spring.container.servlet.SpringServlet </filter-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <!-- Specify application class here --> <param-value>com.foo.MyApplication</param-value> </init-param> </filter> 

And then in the application class I had to change the way I called the super constructor a bit:

 public MyApplication() { super("com.foo", "com.bar"); // Pass in packages as separate params HashMap<String, Object> settings = new HashMap<String, Object>(1); settings.put(ServletContainer.FEATURE_FILTER_FORWARD_ON_404, true); this.setPropertiesAndFeatures(settings); } 

Still, it’s not quite what I was, but at least it draws a bit more configs into Java code and from web.xml, which is important for me as I try to hide this detail.

+1
source

Two spring options for the mind (not intended for puns).

  • Perhaps you can extend SpringServlet with your own class and add appropriate servlet 3.0 annotations to it.
  • According to your approach to switching from the SpringServlet class to Application you can solve the problem without autosaving by including spring build time or byte load time. This allows spring to inject objects created by anyone, not just objects created by Spring. See "Using AspectJ for dependencies of embedding domain objects using Spring . "
+1
source

First of all, in a servlet 3.0 container, you really don't need web.xml .

But with Jersey 2.0, you can set a flag to scan the entire web application for annotated resources:

 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>jersey</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.servlet.provider.webapp</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 

Spring will be enabled automatically if you enable this jar:

  <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-spring3</artifactId> <version>2.3.1</version> </dependency> 
+1
source

I used Jersey with my previously done project using SpringMVC. I based my code on the official Spring documentation.

 public class WebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // Don't create the Listener that Jersey uses to create. // There can only be one linstener servletContext.setInitParameter("contextConfigLocation", "<NONE>"); AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); // Add app config packages context.setConfigLocation("config.package"); // Add listener to the context servletContext.addListener(new ContextLoaderListener(context)); // Replacing: // <servlet-name>ServletName</servlet-name> // <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class> // <init-param> // <param-name>com.sun.jersey.config.property.packages</param-name> // <param-value>webservices.packages</param-value> // </init-param> // <load-on-startup>1</load-on-startup> AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); ServletRegistration.Dynamic appServlet = servletContext.addServlet("ServletName", new DispatcherServlet(dispatcherContext)); appServlet.setInitParameter("com.sun.jersey.config.property.packages", "org.sunnycake.aton.controller"); appServlet.setLoadOnStartup(1); appServlet.addMapping("/RootApp"); } } 

Configuration classes in config.package :

 // Specifies that there will be bean methods annotated with @Bean tag // and will be managed by Spring @Configuration // Equivalent to context:component-scan base-package="..." in the xml, states // where to find the beans controlled by Spring @ComponentScan(basePackages = "config.package") public class AppConfig { /** * Where will the project views be. * * @return ViewResolver como el XML */ @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); return viewResolver; } } 

Hibernation configuration

 // Specifies that there will be bean methods annotated with @Bean tag // and will be managed by Spring @Configuration // Equivalent to Spring tx in the xml @EnableTransactionManagement // Equivalent to context:component-scan base-package="..." in the xml, states // where to find the beans controlled by Spring @ComponentScan({"config.package"}) // Here it can be stated some Spring properties with a properties file @PropertySource(value = {"classpath:aplicacion.properties"}) public class HibernateConfig { /** * Inyected by Spring based on the .properties file in the * \@PropertySource tag. */ @Autowired private Environment environment; /** * Here it created a Session Factory, equivalent to the Spring config file one. * * @return Spring Session factory */ @Bean public LocalSessionFactoryBean sessionFactory() { LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); // Uses the datasource sessionFactory.setDataSource(dataSource()); // Indicates where are the POJOs (DTO) sessionFactory.setPackagesToScan(new String[]{"dto.package"}); // Se asignan las propiedades de Hibernate sessionFactory.setHibernateProperties(hibernateProperties()); return sessionFactory; } /** * Propiedades de la base de datos (Según environment) * * @return Nuevo DataSource (Configuración de la base de datos) */ @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName")); dataSource.setUrl(environment.getRequiredProperty("jdbc.url")); dataSource.setUsername(environment.getRequiredProperty("jdbc.username")); dataSource.setPassword(environment.getRequiredProperty("jdbc.password")); return dataSource; } /** * Hibernate properties * * @return Properties set with the configuration */ private Properties hibernateProperties() { Properties properties = new Properties(); // Dialect (Mysql, postgresql, ...) properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect")); // Show SQL query properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql")); properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql")); return properties; } /** * Inyected by sessionFactory */ @Bean @Autowired public HibernateTransactionManager transactionManager(SessionFactory s) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(s); return txManager; } } 
0
source

All Articles