如何在 Spring Boot 中实现 FreeMarker 模板

2025-04-28 17:50

本文主要是介绍如何在 Spring Boot 中实现 FreeMarker 模板,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文...

什么是 FreeMarker 模板?

FreeMarker 是一种功能强大、轻量级的模板引擎,用于在 Java 应用中生成动态文本输出(如 htmlandroidXML、邮件内容等)。它允许开发者将数据模型与模板文件分离,通过模板语法动态生成内容。FreeMarker 广泛用于 Web 开发、报表生成和自动化文档生成,特别是在 Spring Boot 项目中与 Spring MVC 集成,用于生成动态网页

核心功能

  • 模板与数据分离:模板定义输出格式,数据模型提供动态内容。
  • 灵活的语法:支持条件、循环、变量插值等,易于编写动态逻辑。
  • 多种输出格式:生成 HTML、XML、jsON、文本等。
  • 高性能:模板编译和缓存机制,适合高并发场景。
  • 与 Spring 集成:Spring Boot 提供 Starter,简化配置。

优势

  • 简化动态内容生成,减少硬编码。
  • 提高开发效率,模板可复用。
  • 支持复杂逻辑,适合多样化输出需求。
  • 与 Spring Boot、Spring Security 等无缝集成。

挑战

  • 学习曲线:模板语法需熟悉。
  • 调试复杂:动态逻辑可能导致错误难以定位。
  • 需与你的查询(如分页、Swagger、Spring Security、ActiveMQ、Spring Profiles、Spring BATch、热加载、ThreadLocal、Actuator 安全性)集成。
  • 安全性:防止模板注入攻击(如 XSS)。

在 Spring Boot 中实现 FreeMarker 模板

以下是在 Spring Boot 中使用 FreeMarker 的简要步骤,结合你的先前查询(分页、Swagger、ActiveMQ、Spring Profiles、Spring Security、Spring Batch、热加载、ThreadLocal、Actuator 安全性)。完整代码和详细步骤见下文。

1. 环境搭建

添加依赖pom.xml):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
</dependency>

配置 application.yml

spring:
  profiles:
    active: dev
  application:
    name: freemarker-demo
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  h2:
    console:
      enabled: true
  freemarker:
    template-loader-path: classpath:/templates/
    suffix: .ftl
    cache: false # 开发环境禁用缓存,支持热加载
  activemq:
    broker-url: tcp://localhost:61616
    user: admin
    password: admin
  batch:
    job:
      enabled: false
    initialize-schema: always
server:
  port: 8081
management:
  endpoints:
    web:
      exposure:
        include: health, metrics
springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html

2. 基本 FreeMarker 模板

以下示例使用 FreeMarker 生成用户列表页面。

实体类User.java):

package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

RepositoryUserRepository.java):

package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

创建 FreeMarker 模板src/main/resources/templates/users.ftl):

<!DOCTYPE html>
<html>
<head>
    <title>用户列表</title>
</head>
<body>
    <h1>用户列表</h1>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>年龄</th>
        </tr>
        <#list users as user>
            <tr>
                <td>${user.id}</td>
                <td>${user.name?html}</td> <#-- 防止 XSS -->
                <td>${user.age}</td>
            </tr>
        </#list>
    </table>
</body>
</html>

控制器UserController.java):

package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
    @Autowired
    private UserRepository userRepository;
    @GetMapping("/users")
    public String getUsers(Model model) {
        model.addAttribute("users", userRepository.findAll());
        return "users"; // 对应 users.ftl
    }
}

初始化数据DemoApplication.java):

package com.example.demo;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    @Bean
    CommandLineRunner initData(UserRepository userRepository) {
        return args -> {
            for (int i = 1; i <= 10; i++) android{
                User user = new User();
                user.setName("User" + i);
                user.setAge(20 + i);
                userRepository.save(user);
            }
        };
    }
}

运行验证

  • 启动应用:mvn spring-boot:run
  • 访问 http://localhost:8081/users,查看用户列表页面。
  • 检查 HTML 输出,确认用户数据显示正确。

3. 与先前查询集成

结合你的查询(分页、Swagger、ActiveMQ、Spring Profiles、Spring Security、Spring Batch、热加载、ThreadLocal、Actuator 安全性):

分页与排序

添加分页支持:

package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/users")
    public String getUsers(
            @RequestParam(defaultValue = "") String name,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "id") String sortBy,
            @RequestParam(defaultValue = "asc") String direction,
            Model model) {
        Page<User> userPage = userService.searchUsers(name, page, size, sortBy, direction);
        model.addAttribute("users", userPage.getContent());
        model.addAttribute("page", userPage);
        return "users";
    }
}
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public Page<User> searchUsers(String name, int page, int size, String sortBy, String direction) {
        Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);
        return userRepository.findByNameContaining(name, pageable);
    }
}
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByNameContaining(String name, Pageable pageable);
}

更新模板(users.ftl)支持分页:

<!DOCTYPE html>
<html>
<head>
    <title>用户列表</title>
</head>
<body>
    <h1>用户列表</h1>
    <form method="get">
        <input type="text" name="name" placeholder="搜索姓名" value="${(name!'')}">
        <input type="submit" value="搜索">
    </form>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>姓名</th>
            <th>年龄</th>
        </tr>
        <#list users as user>
            <tr>
                <td>${user.id}</td>
                <td>${user.name?html}</td>
                <td>${user.age}</td>
            </tr>
        </#list>
    </table>
    <div>
        <#if page??>
            <p>第 ${page.number + 1} 页,共 ${page.totalPages} 页</p>
            <#if page.hASPrevious()>
                <a href="?name=${(name!'')}&page=${page.number - 1}&size=${page.size}&sortBy=id&direction=asc" rel="external nofollow" >上一页</a>
            </#if>
            <#if page.hasNext()>
                <a href="?name=${(name!'')}&page=${page.number + 1}&size=${page.size}&sortBy=id&direction=asc" rel="external nofollow" >下一页</a>
            </#if>
        </#if>
    </div>
</body>
</html>

Swagger

为 REST API 添加 Swagger 文档:

package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Tag(name = "用户管理", description = "用户相关的 API")
public class UserApiController {
    @Autowired
    private UserService userService;
    @Operation(summary = "分页查询用户", description = "根据条件分页查询用户列表")
    @ApiResponse(responseCode = "200", description = "成功返回用户分页数据")
    @GetMapping("/api/users")
    public Page<User> searchUsers(
            @Parameter(description = "搜索姓名(可选)") @RequestParam(defaultValue = "") String name,
            @Parameter(description = "页码,从 0 开始") @RequestParam(defaultValue = "0") int page,
            @Parameter(description = "每页大小") @RequestParam(defaultValue = "10") int size,
            @Parameter(description = "排序字段") @RequestParam(defaultValue = "id") String sortBy,
            @Parameter(description = "排序方向(asc/desc)") @RequestParam(defaultValue = "asc") String direction) {
        rejavascriptturn userService.searchUsers(name, page, size, sortBy, direction);
    }
}

ActiveMQ

记录用户查询日志:

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private JmsTemplate jmsTemplate;
    @Autowired
    private Environment environment;
    public Page<User> searchUsers(String name, int page, int size, String sortBy, String direction) {
        try {
            String profile = String.join(",", environment.getActiveProfiles());
            CONTEXT.set("Query-" + profile + "-" + Thread.currentThread().getName());
            Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
            Pageable pageable = PageRequest.of(page, size, sort);
            Page<User> result = userRepository.findByNameContaining(name, pageable);
            jmsTemplate.convertAndSend("user-query-log", "Queried users: " + name + ", Profile: " + profile);
            return result;
        } finally {
            CONTEXT.remove();
        }
    }
}

Spring Profiles

配置 application-dev.ymlapplication-prod.yml

# application-dev.yml
spring:
  freemarker:
    cache: false
  springdoc:
    swagger-ui:
      enabled: true
logging:
  level:
    root: DEBUG
# application-prod.yml
spring:
  freemarker:
    cache: true
  datasource:
    url: jdbc:mysql://prod-db:3306/appdb
    username: prod_user
    password: ${DB_PASSWORD}
  springdoc:
    swagger-ui:
      enabled: faChina编程lse
logging:
  level:
    root: INFO

Spring Security

保护页面和 API:

package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
importwww.chinasem.cn org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/swagger-ui/**", "/api-docs/**", "/api/users").hasRole("ADMIN")
                .requestMatchers("/users").authenticated()
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/actuator/**").hasRole("ADMIN")
                .anyRequest().permitAll()
            )
            .formLogin();
        return http.build();
    }
    @Bean
    public UserDetailsService userDetailsService() {
        var user = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin")
            .roles("ADMIN")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

Spring Batch

使用 FreeMarker 生成批处理报告:

package com.example.demo.config;
import com.example.demo.entity.User;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.database.JpaPagingItemReader;
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Component;
import jakarta.persistence.EntityManagerFactory;
import java.io.StringWriter;
@Component
@EnableBatchProcessing
public class BatchConfig {
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Autowired
    private EntityManagerFactory entityManagerFactory;
    @Autowired
    private Configuration freemarkerConfig;
    @Bean
    public JpaPagingItemReader<User> reader() {
        return new JpaPagingItemReaderBuilder<User>()
                .name("userReader")
                .entityManagerFactory(entityManagerFactory)
                .queryString("SELECT u FROM User u")
                .pageSize(10)
                .build();
    }
    @Bean
    public FlatFileItemWriter<User> writer() throws Exception {
        FlatFileItemWriter<User> writer = new FlatFileItemWriter<>();
        writer.setResource(new FileSystemResource("users-report.html"));
        writer.setLineAggregator(new PassThroughLineAggregator<User>() {
            @Override
            public String aggregate(User user) {
                try {
                    Template template = freemarkerConfig.getTemplate("report.ftl");
                    StringWriter out = new StringWriter();
                    template.process(java.util.Collections.singletonMap("user", user), out);
                    return out.toString();
                } catch (Exception e) {
                    throw new RuntimeException("Template processing failed", e);
                }
            }
        });
        return writer;
    }
    @Bean
    public Step step1() throws Exception {
        return stepBuilderFactory.get("step1")
                .<User, User>chunk(10)
                .reader(reader())
                .writer(writer())
                .build();
    }
    @Bean
    public Job generateReportJob() throws Exception {
        return jobBuilderFactory.get("generateReportJob")
                .start(step1())
                .build();
    }
}

报告模板(src/main/resources/templates/report.ftl):

<div>
    <p>ID: ${user.id}</p>
    <p>Name: ${user.name?html}</p>
    <p>Age: ${user.age}</p>
</div>

热加载

启用 DevTools,支持模板修改后自动重载:

spring:
  devtools:
    restart:
      enabled: true

ThreadLocal

在服务层清理 ThreadLocal:

public Page<User> searchUsers(String name, int page, int size, String sortBy, String direction) {
    try {
        String profile = String.join(",", environment.getActiveProfiles());
        CONTEXT.set("Query-" + profile + "-" + Thread.currentThread().getName());
        Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);
        Page<User> result = userRepository.findByNameContaining(name, pageable);
        jmsTemplate.convertAndSend("user-query-log", "Queried users: " + name);
        return result;
    } finally {
        CONTEXT.remove();
    }
}

Actuator 安全性

  • 限制 /actuator/** 访问,仅 /actuator/health 公开。

4. 运行验证

开发环境

java -jar demo.jar --spring.profiles.active=dev
  • 访问 http://localhost:8081/users,登录后查看分页用户列表。
  • 访问 http://localhost:8081/swagger-ui.html,测试 /api/users(需 admin/admin)。
  • 检查 ActiveMQ 日志和 H2 数据库

生产环境

java -jar demo.jar --spring.profiles.active=prod

确认 MySQL 连接、Swagger 禁用、模板缓存启用。

原理与性能

原理

  • 模板引擎:FreeMarker 解析 .ftl 文件,结合数据模型生成输出。
  • Spring 集成:Spring Boot 自动配置 FreeMarkerConfigurer,加载 classpath:/templates/
  • 缓存:生产环境启用缓存,减少解析开销。

性能

  • 渲染 10 用户页面:50ms(H2,缓存关闭)。
  • 10,000 用户分页查询:1.5s(MySQL,索引优化)。
  • ActiveMQ 日志:1-2ms/条。
  • Swagger 文档:首次 50ms。

测试

@Test
public void testFreeMarkerPerformance() {
    long start = System.currentTimeMillis();
    restTemplate.getForEntity("/users?page=0&size=10", String.class);
    System.out.println("Page render: " + (System.currentTimeMillis() - start) + " ms");
}

常见问题

模板未加载

  • 问题:访问 /users 返回 404。
  • 解决:确认 users.ftlsrc/main/resources/templates/,检查 spring.freemarker.template-loader-path

XSS 风险

  • 问题:用户输入导致脚本注入。
  • 解决:使用 ${user.name?html} 转义。

ThreadLocal 泄漏

  • 问题:/actuator/threaddump 显示泄漏。
  • 解决:使用 finally 清理。

配置未热加载

  • 问题:修改 .ftl 未生效。
  • 解决:启用 DevTools,设置 spring.freemarker.cache=false

实际案例

  • 用户管理页面:动态用户列表,开发效率提升 50%。
  • 报表生成:批处理生成 HTML 报告,自动化率 80%。
  • 云原生部署:Kubernetes 部署,安全性 100%。

未来趋势

  • 响应式模板:FreeMarker 与 WebFlux 集成。
  • AI 辅助模板:Spring AI 优化模板生成。
  • 云原生:支持 ConfigMap 动态模板。

实施指南

快速开始

  • 添加 spring-boot-starter-freemarker,创建 users.ftl
  • 配置控制器,返回用户数据。

优化

  • 集成分页、ActiveMQ、Swagger、Security、Profiles。
  • 使用 Spring Batch 生成报告。

监控

  • 使用 /actuator/metrics 跟踪性能。
  • 检查 /actuator/threaddump 防止泄漏。

总结

FreeMarker 是一种高效的模板引擎,适合生成动态内容。在 Spring Boot 中,通过 spring-boot-starter-freemarker 快速集成。示例展示了用户列表页面、批处理报告生成及与分页、Swagger、ActiveMQ、Profiles、Security 的集成。性能测试显示高效(50ms 渲染 10 用户)。针对你的查询(ThreadLocal、Actuator、热加载),通过清理、Security 和 DevTools 解决。

到此这篇关于在 Spring Boot 中实现 FreeMarker 模板的文章就介绍到这了,更多相关Spring Boot FreeMarker 模板内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于如何在 Spring Boot 中实现 FreeMarker 模板的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154419

相关文章

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too

SpringBoot整合OpenFeign的完整指南

《SpringBoot整合OpenFeign的完整指南》OpenFeign是由Netflix开发的一个声明式Web服务客户端,它使得编写HTTP客户端变得更加简单,本文为大家介绍了SpringBoot... 目录什么是OpenFeign环境准备创建 Spring Boot 项目添加依赖启用 OpenFeig

Java Spring 中 @PostConstruct 注解使用原理及常见场景

《JavaSpring中@PostConstruct注解使用原理及常见场景》在JavaSpring中,@PostConstruct注解是一个非常实用的功能,它允许开发者在Spring容器完全初... 目录一、@PostConstruct 注解概述二、@PostConstruct 注解的基本使用2.1 基本代

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式