📜 ⬆️ ⬇️

Java-based configuration of embedded Jetty / Spring MVC / Spring Security

Someone likes only the xml-configuration, because it allows you to collect all the project settings, if not in one file, then in the same folder with the configuration files for sure, someone does not. For example, I believe that the persistence.xml times are gone irrevocably - it is more convenient to register entity mappings for DBs in entity classes, and it is more convenient to assign mappings of controllers and methods directly to controller classes. The conclusion is obvious: if the developer does not suffer from an anachronism, then one way or another the project will have a mixed setting - part in the annotations, part in the xml-files. This is neither good nor bad, this is a given of time - everyone is used to web.xml , application.xml , springmvc-servlet.xml , etc., but they actively use the annotations of @ Controller, @ Repository, @ Autowired, @ Value , @ ResponseBody , etc. Starting from version 3, Spring added the ability to use Java-based configuration , which made it possible to completely abandon the xml-files in the project settings. Earlier on Habré there was a publication with an analysis of the advantages and disadvantages of some methods over others , with a number of advantages of the xml-configuration I completely agree.

However, this publication is not about the advantages of some or disadvantages of others, and certainly not an attempt to campaign for new technologies and opportunities; this is only a consolidated report on building a web project from scratch to a working draft. And most importantly - with the inclusion of the Jetty server in the embedded project. Actually separate fragments on customization of both embedded Jetty and contexts Spring Application, MVC, Security, in bulk. But in order to understand the intricacies of these settings, not to introduce a complex structure and not to crush the crutches, it took some time and a lot of documentation and source files. But even with a significant adjustment experience, I had to face a number of difficulties, after solving which I had an idea to publish all this in one place.

Previously, I liked the project's architecture more, which was based on a standalone web server (usually Tomcat ), in the settings of which the contexts of web projects were defined, which were defined along with the start of the web server. This architecture is convenient for small projects that are conveniently run in a bundle on one web server, without creating an instance or another web server for each project. But over time, projects become larger, more serious, and this approach ceases to satisfy their needs. A solution with an embedded web server often pays off. It is also convenient for development: launching in IDE is simple, does not require external software dependencies, project deployment and its launch also do not cause difficulties and are done “with one touch”. Thus, the question was not to solve a problem, but simply “a task for the sake of a problem”: get rid of all xml, have an embedded Jetty “on board” and run a web project on Spring MVC using Spring Security .
')
Looking ahead, I’ll say right away that without having a single xml file in the project, I didn’t get rid of all the xml configuration files - it sounds provocative and meaningless, but it’s like this: embedded Jetty in the org.eclipse.jetty.webapp package contains a webdefault file .xml containing the default web application deployment descriptor. You can opt out by writing default servlets DefaultServlet and JspServlet , default listeners and encoders - about 500 lines of xml-code. However, I simply didn’t see anything concisive in this, in order to approach this issue so fundamentally, besides the same or a similar default descriptor exists, perhaps, from the first versions of Jetty, Tomcat ( org.apache.catalina.startup.Tomcat.DefaultWebXmlListener for embedded or $ {Catalina} /conf/web.xml for standalone), GlassFish ( $ {glassfish} /domains/domain1/config/default-web.xml ), JBoss (uses Tomcat), Resin ( $ {resin} / conf /app-default.xml ), Jeronimo (uses Tomcat) - that is, the practice is completely normal for web servers and there is no point in rewriting the standard behavior. Although you can and refuse it. Also, with this configuration, although the deployment descriptor is missing and undefined, the context of the web server is defined, so the need to use the maven-maven-war-plugin plugin with the failOnMissingWebXml = false parameter is not necessary .

To make the article and dependent code complete, I’ll give the source code for non-core resources, such as the controller, the maven configuration, the jsp page ...

The project file pom.xml (the folder structure of the project is not standard - I used it, did not want to change it to the standard one, then in this case there could be different interpretations), I didn’t turn on the plugins - the topic is hackneyed and not worth the space:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.idvsbruck</groupId> <artifactId>jetty-spring-test</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>test</name> <description>Embedded Jetty, Spring Java-based configuration</description> <developers> <developer> <id>IDVsbruck</id> </developer> </developers> <properties> <out.name>${project.artifactId}</out.name> <out.folder>out</out.folder> <source.folder>source</source.folder> <resource.folder>resource</resource.folder> <spring.version>4.1.6.RELEASE</spring.version> <spring.security.version>4.0.0.RELEASE</spring.security.version> <jetty.version>9.3.0.M1</jetty.version> </properties> <build> <finalName>${out.name}</finalName> <directory>${out.folder}</directory> <testOutputDirectory>${out.folder}</testOutputDirectory> <outputDirectory>${out.folder}</outputDirectory> <testSourceDirectory>${source.folder}</testSourceDirectory> <sourceDirectory>${source.folder}</sourceDirectory> <resources> <resource> <directory>${resource.folder}</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> <dependencies> <!-- Embedded Jetty server --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>${jetty.version}</version> </dependency> <!-- Web Application Context Handler --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> <version>${jetty.version}</version> </dependency> <!--    ,     Jetty-   --> <!--   ()  Jetty   --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-annotations</artifactId> <version>${jetty.version}</version> </dependency> <!--   Runtime  JSP, Jetty  JSP --> <!--     jsp-   --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jsp</artifactId> <version>${jetty.version}</version> </dependency> <!-- Spring MVC  --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring ORM  --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Security  --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring.security.version}</version> </dependency> <!--  --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <!-- Hibernate ORM  --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.3.8.Final</version> </dependency> <!--    org.hibernate.validator.constraints      javax.validation.constraints --> <!--       -    --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.3.Final</version> </dependency> <!--  DBCP  Apache --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.0.1</version> </dependency> <!--    MySQL (    ) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> </dependency> <!-- Spring Security          jsr250 --> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency> </dependencies> </project> 

WEB-INF / application.properties project configuration:
application.properties
 base.url=/test base.port=8080 database.url=jdbc:mysql://localhost:3306/test database.user=root database.password=root log4j.rootLogger=INFO, CONSOLE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n log4j.logger.org.hibernate=WARN log4j.logger.org.springframework=WARN log4j.logger.org.springframework.web=WARN log4j.logger.org.springframework.security=WARN log4j.logger.org.eclipse.jetty=WARN log4j.logger.com.idvsbruck.test=DEBUG 

The basic page for the test web project WEB-INF / index.jsp :
index.jsp
 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>Jetty-Spring :: Test</title> </head> <body> <div>Welcome!<br/>Project context path: ${path}</div> </body> </html> 

BaseController Base Controller:
BaseController.java
 @Controller public class BaseController { @RequestMapping(value = "/**", method = RequestMethod.GET) public ModelAndView index(HttpSession session, HttpServletRequest request) { ModelAndView result = new ModelAndView("index"); result.addObject("path", request.getContextPath()); return result; } } 




Configuring Contexts


Application Context - ApplicationContext :
ApplicationContext.java
 @Configuration @ComponentScan(basePackages = "com.idvsbruck.test") @PropertySource({"WEB-INF/application.properties"}) public class ApplicationContext { //     ConfigurableEnvironment  //          embedded Jetty   @PropertySource @Bean public static PropertySourcesPlaceholderConfigurer appProperty() { return new PropertySourcesPlaceholderConfigurer(); } //   ,             //         @Bean(name = "messageSource") public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasenames("WEB-INF/i18n/messages", "WEB-INF/i18n/errors"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } } 

This code is analogous to the following xml configuration:
application.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="/WEB-INF/application.properties"/> <context:annotation-config/> <context:component-scan base-package="com.idvsbruck.test"/> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames" value="/WEB-INF/i18n/messages, /WEB-INF/i18n/errors"/> <property name="defaultEncoding" value="UTF-8"/> </bean> </beans> 

PersistenceContext data subcontext :
PersistenceContext.java
 @Configuration @EnableTransactionManagement public class PersistenceContext { @Autowired private Environment environment; @Bean(name = "sessionFactory") public SessionFactory sessionFactory() throws IOException { final LocalSessionFactoryBean factory = new LocalSessionFactoryBean(); factory.setDataSource(dataSource()); factory.setHibernateProperties(hibernateProperties()); factory.setPackagesToScan("com.idvsbruck.test.entity"); factory.afterPropertiesSet(); return factory.getObject(); } @Bean(name = "dataSource") public DataSource dataSource() { final BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl(environment.getProperty("database.url")); dataSource.setUsername(environment.getProperty("database.user")); dataSource.setPassword(environment.getProperty("database.password")); return dataSource; } @Bean(name = "transactionManager") public HibernateTransactionManager transactionManager() throws IOException { return new HibernateTransactionManager(sessionFactory()); } private static Properties hibernateProperties() { Properties hibernateProperties = new Properties(); hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); hibernateProperties.put("hibernate.connection.autocommit", true); hibernateProperties.put("hibernate.show_sql", false); hibernateProperties.put("hibernate.format_sql", false); hibernateProperties.put("hibernate.generate_statistics", false); hibernateProperties.put("hibernate.hbm2ddl.auto", "update"); hibernateProperties.put("hibernate.use_sql_comments", false); hibernateProperties.put("hibernate.cache.use_query_cache", false); hibernateProperties.put("hibernate.cache.use_second_level_cache", true); return hibernateProperties; } } 

This code is analogous to the following xml configuration (usually placed in the context of the application):
application-persistence.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="${database.url}"/> <property name="username" value="${database.user}"/> <property name="password" value="${database.password}"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.connection.autocommit">true</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.generate_statistics">false</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.use_sql_comments">false</prop> <prop key="hibernate.cache.use_query_cache">false</prop> <prop key="hibernate.cache.use_second_level_cache">true</prop> </props> </property> <property name="packagesToScan" value="com.idvsbruck.test.entity"/> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> 

Servlet context (Spring MVC context) WebContext :
WebContext.java
 @EnableWebMvc @Configuration public class WebContext extends WebMvcConfigurerAdapter { @Autowired private MessageSource messageSource; @Autowired private SessionFactory sessionFactory; //    ,  order  -1 , //             "/**"    @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/javascript/share/**").addResourceLocations("/javascript/share/"); registry.setOrder(-1); } //  resolver ,    ,   //    InternalResourceViewResolver,      UrlBasedViewResolver   //  JstlView   JSTL-       @Bean public ViewResolver viewResolver(){ InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setViewClass(JstlView.class); return resolver; } //    ,      -, //         @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } // ,          @Bean(name = "validator") public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean(); validatorFactoryBean.setValidationMessageSource(messageSource); return validatorFactoryBean; } //        Spring MVC @Override public Validator getValidator() { return validator(); } //  ,      (    //        ) @Bean public CookieLocaleResolver localeResolver() { CookieLocaleResolver localeResolver = new CookieLocaleResolver(); localeResolver.setDefaultLocale(Locale.forLanguageTag("en")); return localeResolver; } //  ,    -       @Override public void addInterceptors(InterceptorRegistry registry) { //         LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName("language"); registry.addInterceptor(localeChangeInterceptor); //  OSIV  OpenSessionInViewInterceptor openSessionInViewInterceptor = new OpenSessionInViewInterceptor(); openSessionInViewInterceptor.setSessionFactory(sessionFactory); registry.addWebRequestInterceptor(openSessionInViewInterceptor); //      ApiInterceptor apiInterceptor = new ApiInterceptor(); registry.addInterceptor(apiInterceptor).addPathPatterns("/api/**"); } } 

Using the abstract WebMvcConfigurerAdapter class facilitates customization by providing a set of basic configurable parameters, but this class is not required. You can use a lower-level WebMvcConfigurationSupport , you can use almost basic abstract AbstractAnnotationConfigDispatcherServletInitializer to initialize the context, or you can build a configuration on bins, making a lot of unnecessary work.

This code is analogous to the following xml configuration:
springmvc-servlet.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:resources mapping="/javascript/share/**" location="/javascript/share/" order="-1"/> <mvc:default-servlet-handler/> <mvc:annotation-driven validator="validator"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:interceptors> <bean class="org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="language"/> </bean> <mvc:interceptor> <mvc:mapping path="/api/**"/> <bean class="com.idvsbruck.test.common.ApiInterceptor"/> </mvc:interceptor> </mvc:interceptors> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="defaultLocale" value="en"/> </bean> </beans> 

Context Spring Security SecutityContext . This publication does not address issues related to protection mechanisms, role-based management, etc. - all that provides a wonderful framework Spring Security . However, the configuration will look incomplete if you ignore the configuration of the Spring Security context. Therefore, it will be given, but the details of interaction with the framework and the nuances of using the client with its own router will not be revealed.
SecurityContext.java
 @Configuration //   debug=true,      -    @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityContext extends WebSecurityConfigurerAdapter { //  web based security   http- @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/**").permitAll(); http.formLogin().loginPage("/signin"); http.logout().invalidateHttpSession(true).logoutSuccessUrl("/").logoutUrl("/signout"); http.anonymous().authorities("USER_ANONYMOUS").principal("guest").key("foobar"); } //    @Override public void configure(WebSecurity web) { web.ignoring().antMatchers("/javascript/share/**"); } //    (   - InMemory,    ,     .. //        @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } //  ,     ;   @Bean @Override public AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } //    @Bean(name = "authenticationProvider") public AuthenticationProvider authenticationProvider() { return new CustomAuthenticationProvider(); } //   UserDetailsService @Bean(name = "userDetailsService") public UserDetailsService userDetailsService() { return new CustomUserDetailsManager(); } //   ;   deprecated org.springframework.security.authentication.encoding.PasswordEncoder //      org.springframework.security.crypto.password.PasswordEncoder //   BCryptPasswordEncoder   - BCrypt,  StandartPasswordEncoder,  //   SHA-256  NoOpPasswordEncoder    (   ) @Bean(name = "passwordEncoder") public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } 

This code is analogous to the following xml configuration:
application-security.xml
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <security:http pattern="/javascript/share/**" security="none"/> <security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/> <security:http auto-config="false" use-expressions="true" disable-url-rewriting="true" entry-point-ref="authEntryPoint"> <security:intercept-url pattern="/**" access="permitAll"/> <security:form-login login-page="/signin"/> <security:logout invalidate-session="true" logout-success-url="/" logout-url="/signout"/> <security:anonymous enabled="true" username="guest" granted-authority="USER_ANONYMOUS" key="foobar"/> </security:http> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="customAuthenticationProvider"/> </security:authentication-manager> <bean id="customAuthenticationProvider" class="com.idvsbruck.test.security.CustomAuthenticationProvider"> <property name="userDetailsService" ref="userDetailsService"/> </bean> <bean id="userDetailsService" class="com.idvsbruck.test.security.CustomUserDetailsManager"/> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> </beans> 

Configuration embedded Jetty server


The embedded server startup scheme is standard: it is necessary to determine the web context, servlets, filters and start the server immediately. When defining the class responsible for the web context (for Jetty, this is WebAppContext ), he has an idea of ​​the WEB-INF folder for Java web projects, where he usually successfully finds the web.xml deployment descriptor, and launches the web project: servlets, listeners, application context, security context, etc. Since the goal is to abandon the xml configuration, our project does not have a standard web.xml deployment descriptor, so we have to do all the necessary settings by directly specifying this in the launch class (a simplified configuration, so to speak, with the necessary configuration).
Main.java
 public class Main { public static void main(String... args) throws Exception { Properties properties = new Properties(); //       Properties InputStream stream = Main.class.getResourceAsStream("/WEB-INF/application.properties"); properties.load(stream); stream.close(); //      log4j      PropertyConfigurator.configure(properties); //  -    Java-based  WebContext AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext(); webContext.register(WebContext.class); //         webContext.getEnvironment().getPropertySources().addLast(new PropertiesPropertySource("applicationEnvironment", properties)); //    Spring MVC ServletHolder servletHolder = new ServletHolder("test-dispatcher", new DispatcherServlet(webContext)); servletHolder.setAsyncSupported(true); servletHolder.setInitOrder(1); //    Spring Security FilterHolder filterHolder = new FilterHolder(new DelegatingFilterProxy("springSecurityFilterChain")); filterHolder.setAsyncSupported(true); //  - Jetty WebAppContext webAppContext = new WebAppContext(); //     webAppContext.setInitParameter("contextConfigLocation", ApplicationContext.class.getName()); //   ,   WEB-INF webAppContext.setResourceBase("resource"); //   , Context Path,     webAppContext.addEventListener(new ContextLoaderListener(webContext)); webAppContext.setContextPath(properties.getProperty("base.url")); webAppContext.addServlet(servletHolder, "/"); webAppContext.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)); //   Server server = new Server(Integer.parseInt(properties.getProperty("base.port"))); server.setHandler(webAppContext); server.start(); server.join(); } } 

If you directly populate the Environment environment with parameters from the project configuration, you can refuse to reload the properties in the application context configurator (the @PropertySource annotation and the PropertySourcesPlaceholderConfigurer bin of the AppProperty () ) are redundant code. Also loading anywhere in the application of the configuration file, and with it, using the @ Value annotation for the parameters from this configuration, becomes unnecessary - you just need to specify @ Autowired private Environment env , and you can use the configuration parameters. Personally, I recently came across this innovation (?), And it seemed to me extremely convenient.

There is also no need to specify other contexts or context configurators directly, since the @ComponentScan annotation in ApplicationContext indicates to the configurator the package that should be scanned for the presence of Spring components. Faced with @Configuration , Spring itself picks up the necessary configurations. , , «» Spring — , (, , @ComponentScan @ Import — , ). Jetty WebContext ApplicationContext , .

Jetty — Spring — WebApplicationInitializer . Spring Spring — onStartup AbstractSecurityWebApplicationInitializer . Jetty -, . Jetty :
Main.java
 public class Main { public static void main(String... args) throws Exception { Properties properties = new Properties(); InputStream stream = Main.class.getResourceAsStream("/WEB-INF/application.properties"); properties.load(stream); stream.close(); PropertyConfigurator.configure(properties); WebAppContext webAppContext = new WebAppContext(); webAppContext.setResourceBase("resource"); webAppContext.setContextPath(properties.getProperty("base.url")); webAppContext.setConfigurations(new Configuration[] { new WebXmlConfiguration(), new AnnotationConfiguration() { @Override public void preConfigure(WebAppContext context) { ClassInheritanceMap map = new ClassInheritanceMap(); map.put(WebApplicationInitializer.class.getName(), new ConcurrentHashSet<String>() {{ add(WebInitializer.class.getName()); add(SecurityWebInitializer.class.getName()); }}); context.setAttribute(CLASS_INHERITANCE_MAP, map); _classInheritanceHandler = new ClassInheritanceHandler(map); } } }); Server server = new Server(Integer.parseInt(properties.getProperty("base.port"))); server.setHandler(webAppContext); server.start(); server.join(); } } 

, , , . , , , ( ), @PropertySource , .
new WebXmlConfiguration() Jetty , : webdefault.xml jetty , .
The overridden preConfigure method specifies a collection of initializers that must be launched before starting the server. I would like to pay particular attention to the SecurityWebInitilizer class :
SecurityWebInitializer.java
 public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer { } 

Everything.The purpose of this class is the definition of the DelegatingFilterProxy filter named "springSecurityFilterChain" . This is a direct analogue of the following fragment from web.xml :
web.xml
 ... <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> ... 

AbstractSecurityWebApplicationInitializer WebApplicationInitializer , onStartup final , . , : , Spring MVC , Spring Security , , Spring — 2 , MVC' Security ( , ). SecurityWebInitializer , «» … WebInitializer .

WebInitializer :
WebInitializer.java
 public class WebInitializer extends AbstractSecurityWebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); applicationContext.register(ApplicationContext.class); servletContext.addListener(new ContextLoaderListener(applicationContext)); ServletRegistration.Dynamic dispatcher = servletContext.addServlet("test-dispatcher", new DispatcherServlet(applicationContext)); dispatcher.setLoadOnStartup(1); dispatcher.setAsyncSupported(true); dispatcher.addMapping("/"); } } 

, , , Spring MVC 3.1, , , 3.2 AbstractAnnotationConfigDispatcherServletInitializer , . , WebInitializer :
WebInitializer.java
 public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] {ApplicationContext.class}; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] {"/"}; } @Override protected String getServletName() { return "test-dispatcher"; } } 

This class takes all the work of creating contexts and servlets.

Here, in principle, the entire basic configuration of the embedded Jetty + Spring MVC + Spring Security . We start the Main class , the test page is available at localhost: 8080 / test .

Source: https://habr.com/ru/post/255773/


All Articles