Spring + Web MVC: dispatcher-servlet.xml vs. appli

2020-01-26 12:25发布

问题:

What is the correct way to use the two contexts: dispatcher-servlet.xml and applicationContext.xml? What goes where?

I want to write a fairly typical app deployed in a servlet container. It has some controllers with JSP views. It also has some nontrivial logic on the back-end. Do I really need both contexts? How are they related to each other? How can I decide what to put in which?

Also, I want to use Spring-security for my application. I may want to use its features (like declarative security with annotations) in web controllers as well as in deeper layers. How should I configure security to work in this case? Should it be in one of those files (which?), or both?

回答1:

The dispatcher-servlet.xml file contains all of your configuration for Spring MVC. So in it you will find beans such as ViewHandlerResolvers, ConverterFactories, Interceptors and so forth. All of these beans are part of Spring MVC which is a framework that structures how you handle web requests, providing useful features such as databinding, view resolution and request mapping.

The application-context.xml can optionally be included when using Spring MVC or any other framework for that matter. This gives you a container that may be used to configure other types of spring beans that provide support for things like data persistence. Basically, in this configuration file is where you pull in all of the other goodies Spring offers.

These configuration files are configured in the web.xml file as shown:

Dispatcher Config

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Application Config

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/application-context.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

To configure controllers, annotate them with @Controller then include the following in the dispatcher-context.xml file:

<mvc:annotation-driven/>
<context:component-scan base-package="package.with.controllers.**" />


回答2:

To add to Kevin's answer, I find that in practice nearly all of your non-trivial Spring MVC applications will require an application context (as opposed to only the spring MVC dispatcher servlet context). It is in the application context that you should configure all non-web related concerns such as:

  • Security
  • Persistence
  • Scheduled Tasks
  • Others?

To make this a bit more concrete, here's an example of the Spring configuration I've used when setting up a modern (Spring version 4.1.2) Spring MVC application. Personally, I prefer to still use a WEB-INF/web.xml file but that's really the only xml configuration in sight.

WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">

  <filter>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
  </filter>

  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.company.config.WebConfig</param-value>
    </init-param>
  </servlet>

  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.company.config.AppConfig</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>

  <jsp-config>
    <jsp-property-group>
      <url-pattern>*.jsp</url-pattern>
      <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>
  </jsp-config>

</web-app>


WebConfig.java

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.company.controller")
public class WebConfig {

  @Bean
  public InternalResourceViewResolver getInternalResourceViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
  }
}


AppConfig.java

@Configuration
@ComponentScan(basePackages = "com.company")
@Import(value = {SecurityConfig.class, PersistenceConfig.class, ScheduleConfig.class})
public class AppConfig {
  // application domain @Beans here...
}


Security.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired
  private LdapUserDetailsMapper ldapUserDetailsMapper;

  @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
      .antMatchers("/").permitAll()
      .antMatchers("/**/js/**").permitAll()
      .antMatchers("/**/images/**").permitAll()
      .antMatchers("/**").access("hasRole('ROLE_ADMIN')")
      .and().formLogin();

    http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
    }

  @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth.ldapAuthentication()
      .userSearchBase("OU=App Users")
      .userSearchFilter("sAMAccountName={0}")
      .groupSearchBase("OU=Development")
      .groupSearchFilter("member={0}")
      .userDetailsContextMapper(ldapUserDetailsMapper)
      .contextSource(getLdapContextSource());
    }

  private LdapContextSource getLdapContextSource() {
    LdapContextSource cs = new LdapContextSource();
    cs.setUrl("ldaps://ldapServer:636");
    cs.setBase("DC=COMPANY,DC=COM");
    cs.setUserDn("CN=administrator,CN=Users,DC=COMPANY,DC=COM");
    cs.setPassword("password");
    cs.afterPropertiesSet();
    return cs;
  }
}


PersistenceConfig.java

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(transactionManagerRef = "getTransactionManager", entityManagerFactoryRef = "getEntityManagerFactory", basePackages = "com.company")
public class PersistenceConfig {

  @Bean
  public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
    lef.setDataSource(dataSource);
    lef.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
    lef.setPackagesToScan("com.company");
    return lef;
  }

  private HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() {
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    hibernateJpaVendorAdapter.setDatabase(Database.ORACLE);
    hibernateJpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
    hibernateJpaVendorAdapter.setShowSql(false);
    hibernateJpaVendorAdapter.setGenerateDdl(false);
    return hibernateJpaVendorAdapter;
  }

  @Bean
  public JndiObjectFactoryBean getDataSource() {
    JndiObjectFactoryBean jndiFactoryBean = new JndiObjectFactoryBean();
    jndiFactoryBean.setJndiName("java:comp/env/jdbc/AppDS");
    return jndiFactoryBean;
  }

  @Bean
  public JpaTransactionManager getTransactionManager(DataSource dataSource) {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(getEntityManagerFactory(dataSource).getObject());
    jpaTransactionManager.setDataSource(dataSource);
    return jpaTransactionManager;
  }
}


ScheduleConfig.java

@Configuration
@EnableScheduling
public class ScheduleConfig {
  @Autowired
  private EmployeeSynchronizer employeeSynchronizer;

  // cron pattern: sec, min, hr, day-of-month, month, day-of-week, year (optional)
  @Scheduled(cron="0 0 0 * * *")
  public void employeeSync() {
    employeeSynchronizer.syncEmployees();
  }
}

As you can see, the web configuration is only a small part of the overall spring web application configuration. Most web applications I've worked with have many concerns that lie outside of the dispatcher servlet configuration that require a full-blown application context bootstrapped via the org.springframework.web.context.ContextLoaderListener in the web.xml.



回答3:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  http://www.springframework.org/schema/cache 
        http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

        <mvc:annotation-driven/>
        <context:component-scan base-package="com.testpoc.controller"/>

        <bean id="ViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="ViewClass" value="org.springframework.web.servlet.view.JstlView"></property>
            <property name="prefix">
                <value>/WEB-INF/pages/</value>
            </property>
            <property name="suffix">
                <value>.jsp</value>
            </property>
        </bean>

</beans>


回答4:

<mvc:annotation-driven />
<mvc:default-servlet-handler />
<mvc:resources mapping="/resources/**" location="/resources/" />

<context:component-scan base-package="com.tridenthyundai.ains" />

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

<bean id="messageSource" 
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="/WEB-INF/messages" />
</bean>

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">

    <property name="prefix">
        <value>/WEB-INF/pages/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>



回答5:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd ">

    <mvc:annotation-driven />
    <mvc:default-servlet-handler />
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <context:component-scan base-package="com.sapta.hr" />

    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

    <bean id="messageSource" 
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/messages" />
    </bean>

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">

        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

</beans>


回答6:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>TestPOC</display-name>

  <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
    </context-param>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>