Spring boot + Vue + elementui to build a traditional simple management system with internationalization front and back

This project can download the source code on GitHub, welcome to give your advice. Thank you
GitHub address: Click to enter

Developing IED using IntelliJ IDEA
At present, we only analyze how to use the framework at the architecture application layer. Later, we will analyze the underlying principle technology of all architectures used after the completion of this project. In sum, we prefer to learn how to use it simply first, and then analyze the learning mode of its underlying principle.


Article catalog

1 project construction

To build a new maven project, let's not talk about it here. Let's see the structure of our project:

config: mainly used to store configuration file resources. I'm relatively simple here, just one application.properties, which can also be the application.yml recommended by springboot
i18n: it mainly stores international language files. Here, you need to specify the file path and location in application.properties
Namely:



# i18n
spring.messages.encoding=utf-8
spring.messages.basename=i18n/index

META-INF: not needed at present. I want to write some jsp files in the future. So it is also supported here. The configured jsp files will be in the folder here

views is mainly the front-end code, such as html, js, etc

Let's look at the back end first
First, the introduction of jar package in pom.xml:
springboot project, if necessary, must inherit its parent project

<!--Support full plank Web Development, including Tomcat and spring-webmvc-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- spring boot Integrate spring security-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!--Support regular test dependencies, including JUnit,Hamcrest,Mockito as well as spring-test Modular-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

<!-- Support JDBC data base -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
	<version>2.0.4.RELEASE</version>
</dependency>


<!--mybatis-spring Adapter-->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.0</version>
</dependency>

<!--mybatis-->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.0</version>
</dependency>

<!--mysql Connect database driver-->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<!-- dbcp Common mainstream database connection pool tools -->
<dependency>
	<groupId>commons-dbcp</groupId>
	<artifactId>commons-dbcp</artifactId>
	<version>1.4</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->
<dependency>
	<groupId>commons-pool</groupId>
	<artifactId>commons-pool</artifactId>
	<version>1.6</version>
</dependency>

<!-- aop Faceted support -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!--Support Thymeleaf Template engine, including Spring Integration of-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
	<groupId>net.sourceforge.nekohtml</groupId>
	<artifactId>nekohtml</artifactId>
	<version>1.9.22</version>
</dependency>

<!-- standard Java The library cannot provide enough methods to handle its core classes. Apache Commons Lang These additional methods are provided.-->
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-lang3</artifactId>
	<version>3.7</version>
</dependency>

<!-- XML operation jar package-->
<dependency>
	<groupId>dom4j</groupId>
	<artifactId>dom4j</artifactId>
	<version>1.6.1</version>
</dependency>

The backend uses mybatis to add, delete, modify and query the database, and the Thymeleaf template engine to render the page. The project uses mysql as the database support

It is also necessary to solve the problem that the IDE tool does not compile. xml files and other resource files (fonts, etc.) when compiling java files. You need to set:

<resources>
    <!---Solve font problems-->
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
        <excludes>
            <exclude>**/fonts/**</exclude>
            <exclude>**/**.ico</exclude>
        </excludes>
    </resource>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>false</filtering>
        <includes>
            <include>**/fonts/**</include>
            <include>**/**.ico</include>
        </includes>
    </resource>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
    </resource>
</resources>

Look at application.properties:

# service port
server.port=8080
# close favicon
spring.mvc.favicon.enabled=false
# database
default.datasource.driverClassName=com.mysql.cj.jdbc.Driver
default.datasource.url=jdbc:mysql://localhost:3306/xttl?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&useSSL=false
default.datasource.username=root
default.datasource.password=soft01
# thymeleaf
spring.thymeleaf.prefix=classpath:/views/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.cache=false
# jsp
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp
# i18n
spring.messages.encoding=utf-8
spring.messages.basename=i18n/index

The default connection database of mysql 8.0 will use useSSL. We need to disable it here. Otherwise, the warning of Establishing SSL connection without server's identity verification is not recommended will appear. At the same time, we need to set the time zone, that is, serverTimezone. Otherwise, could not get JDBC connection will appear; Nested exception is java.sql.sqlexception: the server time zone value 'XXX' is unrecognized or representatives more than one time zone. You must configure either the server or jdbc driver (via the serverTimezone configuration property) to use a more specific time zone value if you want to use time zone support. At present, you can set a time zone at will (serverTimezone=GMT, which means the central area time zone of Greenwich). In the future, there are still problems. Please see the subsequent decomposition. No more details here.
Thymeleaf template engine configuration: due to the use of VUE+ElementUI in the previous section, there will be some unconventional html specifications on html tags, which will lead to template parsing errors. Therefore, the JAR package nekohtml is introduced here to solve this problem. At the same time, the configuration of spring.thymeleaf.mode needs to be changed to LEGACYHTML5. The spring boot default template is placed in the templates under resource. You can see that our project structure is not like this, so we need to specify the template path spring.thymeleaf.prefix=classpath:/views/

2. Spring boot integrates spring security

Inherit org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter, override the configure method, and paste the entire class file of the building owner directly here

package com.tllg.sysset.mvcconfig.security;

import com.tllg.sysset.mvcconfig.auth.MyAuthProvider;
import com.tllg.sysset.mvcconfig.user.MyUserDetailsService;
import com.tllg.sysset.mvcconfig.handler.MyFailHandler;
import com.tllg.sysset.mvcconfig.handler.MySuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author lgli
 * Security information configuration
 * Basic configuration information of the project, mainly including login interception and static resource access
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private MyAuthProvider myAuthProvider;

    @Override
    public void configure(WebSecurity web){
        //Define the static resources of the login page (including js)
        web.ignoring().antMatchers("/login/**",//Login interface and resources
                "/login.html",//Login interface and resources
                "/base/js/**",//Main js,jquery and vue
                "/*.ico"//Icon
        );
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .formLogin()//Jump to login page when user login is required
            .loginPage("/login.html")//Define login page
            .usernameParameter("username").passwordParameter("password")//Define login interface parameters
            .loginProcessingUrl("/login_to_sys")//Login interface, which is executed by spring security;
            .permitAll()//It means that no interception is needed, all pass
            .failureHandler(new MyFailHandler())//Login failure handling handler
            .successHandler(new MySuccessHandler())//Login successfully processed handler
            .and().headers().frameOptions().sameOrigin()//Ensure that the same source iframe can be accessed without blocking
            .and().authorizeRequests().anyRequest().authenticated()
            .and().csrf().disable()
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
        auth.authenticationProvider(myAuthProvider);
//        auth.eraseCredentials(true);
    }
}

Web. Designing (). Ant matchers filter some resource files that do not need to be intercepted, such as all html and js files used for login.
The configure(HttpSecurity http) method is mainly used to intercept settings. The main meaning of the code is also clearly commented. frameOptions().sameOrigin() is set mainly because the front end of the project accesses the back-end requests through iframe nesting, otherwise the requests will access exceptions.
In addition, we need to implement the loadUserByUsername method of org.springframework.security.core.userdetails.UserDetailsService to customize the authentication information, which can be ignored by default. I have implemented the change method here, and assembled some resource information after the successful login verification. The core code is as follows: "other related codes involved, see GitHub"

if(UtilBase.STRING.isBlank(name)){
	throw new UsernameNotFoundException("Please pass in the user name");
}
logger.info("The user name passed in is{}Attempting to log in to",name);
UserBaseInfo user = userMapper.selectUserByUserUniqueSign(name, "0");
if(user == null){
	throw new UsernameNotFoundException("Invalid user");
}
UserBaseSys userBaseSys = new UserBaseSys(user);
//Get user role
userBaseSys.setUserResource((List<ResourceInfo>)dealWithCascade(
		userMapper.selectUserResourcesByUserUniqueSign(name, StaticEnum.ZERO.getValue()),ResourceInfo.class
));
//Get user resources
userBaseSys.setUserRoles((List<RoleBaseInfo >)dealWithCascade(
		userMapper.selectUserRolesByUserUniqueSign(name),RoleBaseInfo.class
));
logger.info("Get user data, return userDetail");
return userBaseSys;

The configure method sets the handler for login verification failure or success. Here, you need to implement your own interface
Failed. You need to implement the onAuthenticationFailure method of org.springframework.security.web.authentication.AuthenticationFailureHandler. Here you can simply return to the login page:

package com.tllg.sysset.mvcconfig.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * @author lgli
 * Login failure handling handler
 *
 */
public class MyFailHandler implements AuthenticationFailureHandler {

    private Logger logger = LoggerFactory.getLogger(MyFailHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException e) throws IOException {
        logger.error("Authentication exception in this operation:"+new Date());
        response.sendRedirect("/login.html?error="+e.getMessage());//Return to the login page and log in again
    }
}

To succeed, you need to implement the onAuthenticationSuccess method of org.springframework.security.web.authentication.AuthenticationSuccessHandler. Here you can log in successfully and enter the system homepage directly

package com.tllg.sysset.mvcconfig.handler;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author lgli
 * Implement user-defined login successfully
 * Login successfully processed handler
 *
 */
public class MySuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        response.sendRedirect("home/entryHomeIndex");
    }
}

3.SpringBoot integrates Mybatis

Since I haven't reviewed the principle of mybatis for a long time, I use @ Configuration mode to review mybatis step by step. Here, I only do step-by-step analysis instead of bottom-level principle interpretation.

package com.tllg.sysset.datasource;

import com.tllg.util.PropertyUtil;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import java.io.IOException;


/**
 * @author lgli
 * Initialize master data source
 * Data source connection pool--> Session factory sqlSessionFactoryBean--> Auto scan object relation mapping--> transaction management
 */
@Configuration
@EnableTransactionManagement//Support things
public class MainSource implements TransactionManagementConfigurer {

    private static final Logger log = LoggerFactory.getLogger(MainSource.class);

    @Autowired
    private PropertyUtil propertyUtil;

    private PooledDataSource datasource;

    private SqlSessionFactory sessionFactory;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        initDataSource();
        return new DataSourceTransactionManager(datasource);
    }

    /**
     * Initialize data source and its connection
     */
    @Bean(name="mainDatasource")
    public PooledDataSource initDataSource(){
        try {
            PooledDataSource datasource = new PooledDataSource();
            //Set connection driver - database type
            datasource.setDriver(propertyUtil.getValueByKey("default.datasource.driverClassName"));
            //Set URL
            datasource.setUrl(propertyUtil.getValueByKey("default.datasource.url"));
            //Set user name
            datasource.setUsername(propertyUtil.getValueByKey("default.datasource.username"));
            //Set password
            datasource.setPassword(propertyUtil.getValueByKey("default.datasource.password"));
            //Connection pool additional information properties in the default pooledatasource
            this.datasource = datasource;
            return datasource;
        }catch (Exception e){
            log.error("Failed to initialize the primary data source connection pool,"+e,e);
            throw new RuntimeException("Failed to initialize the primary data source connection pool exception"+e);
        }
    }


    /**
     * Register sqlSessionFactoryBean
     *
     */
    @Bean(name="mainSqlSessionFactory")
    public SqlSessionFactory registerSqlSessionFactory(){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(this.datasource);//set up data sources
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            //Specify xml profile path
            sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:com/tllg/**/mapper/xml/*.xml"));
            this.sessionFactory = sqlSessionFactoryBean.getObject();
            return this.sessionFactory;
        }catch (IOException ioe){
            log.error("Master data source configuration Mapper mapping file does not exist“+ioe,ioe);
            throw new RuntimeException("Configuration Mapper mapping file does not exist“+ioe);
        }catch (Exception ex){
            log.error("Master data source register sqlSessionFactoryBean exception“+ex,ex);
            throw new RuntimeException("Master data source register sqlSessionFactoryBean exception“+ex);
        }
    }

    /**
     * Register sqlSessionTemplate
     * @param factory SqlSessionFactory
     * @return
     */
    @Bean(name="mainSqlSessionTemplate")
    public SqlSessionTemplate setSqlSessionTemplate(SqlSessionFactory factory){
        return new SqlSessionTemplate(factory);
    }
}

-------------------------------------------------


package com.tllg.sysset.datasource;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@AutoConfigureAfter(MainSource.class)
public class MainMapper {
    /**
     * Register Mapper mapping file information
     */
    @Bean(name="mainMapperScannerConfigurer")
    public MapperScannerConfigurer registerMapperConfig(){
        MapperScannerConfigurer mapper = new MapperScannerConfigurer();
        //Specify sqlSessionFactory
        mapper.setSqlSessionFactoryBeanName("mainSqlSessionFactory");
        //Specify dao interface profile path
        mapper.setBasePackage("com.tllg.**.mapper");
        return mapper;
    }
}

Code Description:
The main action track of mybatis is to connect to the database - > sqlsessionfactorybuilder - > sqlsessionfactory - > sqlsession - > do something -- > return result
By reading the configuration file information, mybatis gets the database connection. By connecting the configuration object, it gets the session factory SqlSessionFactoryBuilder object of the current database connection. By SqlSessionFactoryBuilder object, it gets the SqlSessionFactory instance. By SqlSessionFactory, it gets the SqlSession instance. SqlSession can execute our sql. All object instances, using singleton mode. Of course, in general, the singleton mode must be used, otherwise the database connection will be full if there are too many creation.
The registerSqlSessionFactory() method is mainly used for some configuration information of SqlSessionFactory, such as data source and its sql file.
The setSqlSessionTemplate() method obtains the SqlSession instance through the proxy execution, which encapsulates its transaction information internally. We do not need to call SqlSessionFactory manually to obtain SqlSession to execute sql. That is to say, the follow-up process described above will be handled by SqlSessionTemplate.



There are two @ Configuration configuration files here. The first one is mainly the process coding of mybatis action track. The second one is mainly to set the mapping relationship file. Note the @ AutoConfigureAfter annotation, which means that we need to execute other Configuration files and set the mapping relationship before changing the Configuration file. We need to get SqlSessionFactory, so the second Configuration file is executed in the first place After Configuration files.

4 Resolver integration

We need to override the addResourceHandlers method of org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter to access static resource files

package com.tllg.sysset.mvcconfig.resolver;


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author lgli
 * Configuration information of resource path
 * Specify access resource path
 */
@Component
public class SourceResolverConfig extends WebMvcConfigurerAdapter {

    /**
     * Add directory configuration
     * Default configuration/**
     * You can use addResourceLocations multiple times to add directories
     * Priority added first is higher than that added later
     * @param registry registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/views/");
        super.addResourceHandlers(registry);
    }
}

At this time, the basic background configuration is almost complete, and the project can also run.
So far, in the next section, we will continue to introduce the front-end code.
Study a little every day to make progress
Friends who love can watch my official account and update some learning experiences regularly. It's a personal summary. I hope you can point out the incorrect points and learn together.

The way of code is long, because I like it, I insist on it.




Tags: Spring Mybatis Thymeleaf Database

Posted on Tue, 05 May 2020 15:45:30 -0400 by gregsmith