Backend Development 11 min read

SpringBoot + Thymeleaf Supplier Management System: Design, Implementation, and Redis Optimization

This article details the end‑to‑end development of a supplier management system using SpringBoot, Thymeleaf, Mybatis‑Plus, MySQL and Redis, covering project planning, code implementation, file upload handling, caching optimization, and deployment considerations.

Java Captain
Java Captain
Java Captain
SpringBoot + Thymeleaf Supplier Management System: Design, Implementation, and Redis Optimization

The author describes the complete workflow of building a supplier management system, starting from design, documentation, coding, and delivery, and notes that the entire project was completed by a single developer within two days.

Technology stack includes SpringBoot, Thymeleaf, Mybatis‑Plus, MySQL, PageHelper, Lombok, and Redis for later page‑level optimization.

The business flow covers login, user and role management, news announcements, product and order modules, and permission checks using Thymeleaf expressions.

Project setup begins with creating a Maven project and adding required dependencies, followed by configuring application.yml :

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/supplier?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    username: root
    password: root
  # thymeleaf configuration
  thymeleaf:
    # disable cache
    cache: false
    prefix: classpath:/templates/

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml

Key infrastructure such as exception handling and interceptors are implemented:

@ControllerAdvice
public class ExceptionHandler {
    private final org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());
    @org.springframework.web.bind.annotation.ExceptionHandler(Exception.class)
    public ModelAndView exception(HttpServletRequest request, Exception e) throws Exception {
        logger.error("Request URL:{},Exception:{}", request.getRequestURL(), e);
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        ModelAndView mv = new ModelAndView();
        mv.addObject("url", request.getRequestURL());
        mv.addObject("exception", e);
        mv.setViewName("error/error");
        return mv;
    }
}
public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getSession().getAttribute("user") == null) {
            response.sendRedirect("/api");
            return false;
        }
        return true;
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api", "/api/doLogin");
    }
}

File upload functionality is provided through a controller:

// Navigate to upload page
@RequestMapping("/file-upload")
public String userList(){
    return "file-upload";
}

@RequestMapping("/doAddForUser")
public String doAdd(User user, @RequestParam("file") MultipartFile files, HttpServletRequest request) throws IOException {
    if (files != null && !files.isEmpty()) {
        String name = UUID.randomUUID().toString().replace("-", "");
        String ext = FilenameUtils.getExtension(files.getOriginalFilename());
        String url = request.getSession().getServletContext().getRealPath("/upload/");
        File file = new File(url);
        if (!file.exists()) { file.mkdir(); }
        files.transferTo(new File(url + "/" + name + "." + ext));
        user.setAvatar(request.getContextPath() + "/upload/" + name + "." + ext);
    }
    user.setId(UUID.randomUUID().toString());
    String salt = PasswordUtils.getSalt();
    String encode = PasswordUtils.encode(user.getPassword(), salt);
    user.setSalt(salt);
    user.setPassword(encode);
    user.setCreateTime(new Date());
    userService.save(user);
    return "redirect:/api/users";
}

A multi‑file upload method is also shown (though not used in the final project):

private void commons(Object obj, @RequestParam("file") CommonsMultipartFile[] files, HttpServletRequest request) throws IOException {
    for (int i = 0; i < files.length; i++) {
        if (files[i] != null && !files[i].isEmpty()) {
            String name = UUID.randomUUID().toString().replace("-", "");
            String ext = FilenameUtils.getExtension(files[i].getOriginalFilename());
            String url = request.getSession().getServletContext().getRealPath("/upload/");
            File file = new File(url);
            if (!file.exists()) { file.mkdir(); }
            files[i].transferTo(new File(url + "/" + name + "." + ext));
            if (i == 0) obj.setUrl1(request.getContextPath() + "/upload/" + name + "." + ext);
            if (i == 1) obj.setUrl2(request.getContextPath() + "/upload/" + name + "." + ext);
            if (i == 2) obj.setUrl3(request.getContextPath() + "/upload/" + name + "." + ext);
            if (i == 3) obj.setUrl4(request.getContextPath() + "/upload/" + name + "." + ext);
            if (i == 4) obj.setUrl5(request.getContextPath() + "/upload/" + name + "." + ext);
        }
    }
}

To improve performance, the author adds Redis caching for page content. Required Maven dependencies:

org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2

Redis configuration in application.yml :

## Redis configuration
redis:
  host: localhost
  port: 6379
  database: 0
  connect-timeout: 10000ms
  lettuce:
    pool:
      max-active: 8
      max-wait: 10000ms
      max-idle: 200
      min-idle: 5

RedisTemplate bean for serialization:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate
redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate
redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}

Controller method that caches the rendered HTML of a news list page in Redis for 60 seconds:

@Autowired
private NewsService newsService;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ThymeleafViewResolver viewResolver;

@RequestMapping(value = "/news", produces = "text/html;charset=utf-8")
@ResponseBody
public String roles(Model model, @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
                    @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
                    HttpServletRequest request, HttpServletResponse response) {
    ValueOperations valueOperations = redisTemplate.opsForValue();
    String html = (String) valueOperations.get("news-list");
    if (!StringUtils.isEmpty(html)) {
        return html;
    }
    PageHelper.startPage(pageNo, pageSize);
    List
list = newsService.list();
    PageInfo
pageInfo = new PageInfo<>(list);
    model.addAttribute("news", list);
    model.addAttribute("pageInfo", pageInfo);
    WebContext context = new WebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap());
    html = viewResolver.getTemplateEngine().process("news-list", context);
    if (!StringUtils.isEmpty(html)) {
        valueOperations.set("news-list", html, 60, TimeUnit.SECONDS);
    }
    return html;
}

Additional notes clarify the difference between @Controller (template rendering) and @RestController (JSON response), and remind developers to add @ResponseBody when caching full pages as JSON strings.

The complete source code is available on Gitee at https://gitee.com/gao-wumao/supplier , and readers are encouraged to clone the repository.

JavaBackend DevelopmentRedisMySQLSpringBootThymeleaf
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.