Sprng MVC + mybatis fully annotated configuration, zero XML file

Time:2021-10-22

Write in front

Since the spring boot, a web project has become very simple,
This article mainly explains how to use spring MVC to build a web project without using the automatic configuration of spring boot
Web.xml can be discarded after servlet 3.0, so this article will not have any XML files, all in the form of 'Java config'

Create project

First, use idea to create a maven project called spring web

Sprng MVC + mybatis fully annotated configuration, zero XML file

Use spring MVC + mybatis + freemaker to build the project
spring.version=4.3.14.RELEASE
The dependent jar and the complete POM file are as follows:
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.8</version>
        </dependency>

        <!-- freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!-- database -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

    </dependencies>

Using java configuration

After servlet 3.0, when the servlet container starts, it will call the class that implements the servletcontainerinitializer interface,
Spring MVC’s springservletcontainerinitializer implements this interface. In the source code, you can see that the implementation class of webapplicationinitializer will be called circularly in the implementation

Spring MVC provides the abstractannotationconfigdispatcherservletinitializer abstract class to simplify Java configuration, although the class name is very long

Create a new configuration class called webappinitializer, which inherits the long name class above ⬆️
It seems that this is equivalent to the previous web.xml. If you are familiar with the XML configuration of spring web project, you can easily find that rootconfig and ServletConfig are very similar to the previous two spring XML configuration files
If you need to configure other servlets, you can override onstartup()

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //Rootconfig wants to be the same as the previous application-context.xml
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringContextConfig.class};
    }

    //ServletConfig is equivalent to the previous mvc-servlet.xml
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    //Dispatchservlet path
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/*"};
    }

    //Configure filters
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding(String.valueOf(StandardCharsets.UTF_8));
        encodingFilter.setForceEncoding(true);
        return new Filter[]{encodingFilter};
    }
}

SpringContextConfig

Here you can configure the data source, transaction manager, enable annotation scanning, enable AOP, and integrate with other frameworks (usually ORM framework, where we use mybatis)
Do you feel like the configuration in the previous application-context.xml

PS: if the springmvcconfig configuration class is scanned by the package configured by springcontextconfig, you need to exclude the springmvcconfig class, otherwise the springmvcconfig class will be initialized twice

@EnableAspectJAutoProxy
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
@ComponentScan(basePackages = {"cat.spring.web"},
        excludeFilters = {@ComponentScan.Filter(classes = Controller.class), @ComponentScan.Filter(classes = EnableWebMvc.class)})
public class SpringContextConfig {

    @Bean
    public DataSource dataSource(Environment env) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setJdbcUrl(env.getProperty("db.url"));
        dataSource.setDriverClass(env.getProperty("db.driver"));
        dataSource.setUser(env.getProperty("db.user"));
        dataSource.setPassword(env.getProperty("db.password"));
        dataSource.setMinPoolSize(Integer.valueOf(env.getProperty("pool.minPoolSize")));
        dataSource.setMaxPoolSize(Integer.valueOf(env.getProperty("pool.maxPoolSize")));
        dataSource.setAutoCommitOnClose(false);
        dataSource.setCheckoutTimeout(Integer.valueOf(env.getProperty("pool.checkoutTimeout")));
        dataSource.setAcquireRetryAttempts(2);
        return dataSource;
    }

    /**
     *Configure transaction manager
     *
     * @param dataSource
     * @retur
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     *Mybatis configuration
     *
     * @param dataSource
     * @return
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {

        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setTypeAliasesPackage("cat.spring.web.entity");
        sessionFactoryBean.setMapperLocations(new ClassPathResource[]{new ClassPathResource("/mapper/**/*.xml")});

        Configuration configuration = new Configuration();
        configuration.setUseGeneratedKeys(true);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setUseColumnLabel(true);
        sessionFactoryBean.setConfiguration(configuration);

        return sessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        configurer.setBasePackage("cat.spring.web.mapper");

        return configurer;
    }
    
}

Give the configuration of the application.properties file

db.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&allowMultiQueries=true
db.driver=com.mysql.jdbc.Driver
db.user=root
db.password=123456

#Database connection pool configuration
#Minimum number of connections reserved in the connection pool
pool.minPoolSize=5
#Maximum number of connections reserved in the connection pool
pool.maxPoolSize=30
#Get connection timeout
pool.checkoutTimeout=1000

freemarker.request-context-attribute=rc
freemarker.expose-request-attributes=true
freemarker.expose-session-attributes=true
freemarker.prefer-file-system-access=false

SpringMvcConfig

Spring mvcconfig mainly configures spring MVC interceptors, views such as JSP or freemark, or other spring MVC configurations

@EnableWebMvc
@PropertySource("classpath:application.properties")
@ComponentScan(basePackages = "cat.spring.web.controller", includeFilters = @ComponentScan.Filter(classes = Controller.class), useDefaultFilters = false)
public class SpringMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("index");
    }

    /**
     *Configure freemaker
     * @return FreeMarkerConfigurer
     */
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer(Environment env) {
        FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
        freeMarkerConfigurer.setTemplateLoaderPath("classpath:/templates/");
        freeMarkerConfigurer.setPreferFileSystemAccess(env.getProperty("freemarker.prefer-file-system-access", boolean.class, false));
        freeMarkerConfigurer.setDefaultEncoding(String.valueOf(StandardCharsets.UTF_8));

        return freeMarkerConfigurer;
    }

    @Bean
    public ViewResolver freemarkerViewResolver(Environment env) {

        FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver("", ".ftl");
        viewResolver.setViewClass(FreeMarkerView.class);
        viewResolver.setCache(false);
        viewResolver.setContentType("text/html;charset=utf-8");
        viewResolver.setRequestContextAttribute(env.getProperty("freemarker.request-context-attribute", "rc"));
        viewResolver.setExposeRequestAttributes(env.getProperty("freemarker.expose-request-attributes", boolean.class, true));
        viewResolver.setExposeSessionAttributes(env.getProperty("freemarker.expose-session-attributes", boolean.class, true));

        return viewResolver;
    }
}

function

At this point, the project configuration section is finished, and we can run it using Maven’s tomecat plug-in

    <build>
        <finalName>spring-web</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <username>tomcat</username>
                    <password>123456</password>
                    <uriEncoding>utf-8</uriEncoding>
                    <path>/cat</path>
                    <port>8080</port>
                    <update>false</update>
                    <!--<warSourceDirectory>${basedir}/src/main/webapp</warSourceDirectory>-->
                </configuration>
            </plugin>
        </plugins>
    </build>

Execute command: MVN tomcat7: run