# Spring Boot Thymeleaf Integration Guide

## Dependency Configuration

### Maven Dependencies

```xml
<dependencies>
    <!-- Core Thymeleaf starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Layout dialect for template inheritance -->
    <dependency>
        <groupId>nz.net.ultraq.thymeleaf</groupId>
        <artifactId>thymeleaf-layout-dialect</artifactId>
    </dependency>

    <!-- Security integration -->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity6</artifactId>
    </dependency>

    <!-- Java 8 time utilities -->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-java8time</artifactId>
    </dependency>
</dependencies>
```

### Gradle Dependencies

```gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
    implementation 'org.thymeleaf.extras:thymeleaf-extras-java8time'
}
```

## Auto-Configuration

### Default Configuration

Spring Boot automatically configures:

- `TemplateResolver` - Classpath-based template resolution
- `TemplateEngine` - Spring-integrated template engine
- `ViewResolver` - Thymeleaf view resolution
- `ResourceResolver` - Static resource handling

### Template Locations

```
src/main/resources/
├── templates/          # Thymeleaf templates
│   ├── admin/         # Admin templates
│   ├── user/           # User templates
│   ├── fragments/      # Reusable fragments
│   └── layout/         # Layout templates
├── static/             # Static resources
│   ├── css/           # Stylesheets
│   ├── js/            # JavaScript files
│   ├── images/         # Images
│   └── fonts/         # Font files
└── messages/           # i18n properties
    ├── messages.properties
    ├── messages_fr.properties
    └── messages_es.properties
```

## Configuration Properties

### Application Properties

```properties
# Template Configuration
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.themyleaf.template-resolver-order=classpath

# Caching Configuration
spring.thymeleaf.cache=true  # Set to false in development
spring.thymeleaf.check-template-location=true

# Encoding Configuration
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html

# Static Resource Configuration
spring.web.resources.static-locations=classpath:/static/
spring.mvc.static-path-pattern=/static/**
```

### YAML Configuration

```yaml
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML
    template-resolver-order: classpath
    cache: true
    check-template-location: true
    encoding: UTF-8
    servlet:
      content-type: text/html
  web:
    resources:
      static-locations: classpath:/static/
  mvc:
    static-path-pattern: /static/**
```

## Controller Integration

### Basic Controller

```java
@Controller
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public String listUsers(Model model) {
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        model.addAttribute("title", "User List");
        return "user/list";
    }

    @GetMapping("/{id}")
    public String viewUser(@PathVariable Long id, Model model) {
        User user = userService.findById(id);
        model.addAttribute("user", user);
        model.addAttribute("title", "User Details");
        return "user/view";
    }

    @GetMapping("/new")
    public String createUserForm(Model model) {
        model.addAttribute("user", new User());
        model.addAttribute("title", "Create User");
        model.addAttribute("roles", userService.getAvailableRoles());
        return "user/form";
    }

    @PostMapping
    public String saveUser(@ModelAttribute @Valid User user,
                        BindingResult result, Model model) {
        if (result.hasErrors()) {
            model.addAttribute("title", "Create User");
            model.addAttribute("roles", userService.getAvailableRoles());
            return "user/form";
        }

        userService.save(user);
        return "redirect:/users";
    }
}
```

### REST Controller with Thymeleaf

```java
@Controller
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/dashboard")
    public String dashboard(Model model) {
        DashboardData data = dashboardService.getData();
        model.addAttribute("data", data);
        return "api/dashboard";
    }

    @PostMapping("/process")
    @ResponseBody
    public ResponseEntity<?> processData(@RequestBody RequestData data) {
        ProcessResult result = service.process(data);
        return ResponseEntity.ok(result);
    }
}
```

## Template Engine Configuration

### Custom Template Engine

```java
@Configuration
public class ThymeleafConfig {

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);

        // Add additional dialects
        templateEngine.addDialect(new LayoutDialect());
        templateEngine.addDialect(new SpringSecurityDialect());

        return templateEngine;
    }

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("classpath:/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCacheable(true);
        resolver.setCharacterEncoding("UTF-8");
        return resolver;
    }
}
```

### Development Template Resolver

```java
@Configuration
@Profile("dev")
public class DevThymeleafConfig {

    @Bean
    public ITemplateResolver templateResolver() {
        FileTemplateResolver resolver = new FileTemplateResolver();
        resolver.setPrefix("src/main/resources/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCacheable(false);
        resolver.setCheckExistence(true);
        return resolver;
    }
}
```

## Error Handling

### Custom Error Pages

```java
@Controller
public class ErrorController {

    @GetMapping("/error")
    public String handleError(HttpServletRequest request, Model model) {
        int statusCode = getStatusCode(request);
        model.addAttribute("statusCode", statusCode);
        model.addAttribute("errorMessage", getErrorMessage(request));

        switch (statusCode) {
            case 404:
                return "error/404";
            case 500:
                return "error/500";
            default:
                return "error/default";
        }
    }

    private int getStatusCode(HttpServletRequest request) {
        return (Integer) request.getAttribute("javax.servlet.error.status_code");
    }

    private String getErrorMessage(HttpServletRequest request) {
        return (String) request.getAttribute("javax.servlet.error.message");
    }
}
```

### Error Templates

```html
<!-- error/404.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head th:replace="~{layout/base :: head}">
    <title th:text="#{error.404.title}">Page Not Found</title>
  </head>
  <body th:replace="~{layout/base :: layout(~{::content})}">
    <main th:fragment="content">
      <div class="error-page">
        <h1 class="error-code" th:text="${statusCode}">404</h1>
        <h2 class="error-message" th:text="#{error.404.message}">
          Page not found
        </h2>
        <p th:text="${errorMessage}">The requested page could not be found.</p>
        <a th:href="@{/}" th:text="#{error.home}">Go Home</a>
      </div>
    </main>
  </body>
</html>
```

## Internationalization

### Message Source Configuration

```java
@Configuration
public class I18nConfig {

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages/messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(3600);
        return messageSource;
    }

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.ENGLISH);
        return resolver;
    }
}
```

### Locale Interceptor

```java
@Component
public class LocaleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler) {
        String lang = request.getParameter("lang");
        if (lang != null && !lang.isEmpty()) {
            Locale locale = Locale.forLanguageTag(lang);
            LocaleContextHolder.setLocale(locale);
        }
        return true;
    }
}
```

## Performance Optimization

### Caching Strategy

```properties
# Development
spring.thymeleaf.cache=false

# Production
spring.thymeleaf.cache=true
spring.thymeleaf.cache-ttl-ms=3600000

# Template resolver optimization
spring.thymeleaf.template-resolver-order=classpath,filesystem
```

### Template Precompilation

```java
@Configuration
public class PrecompiledTemplateConfig {

    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        if (isProductionProfile()) {
            precompileTemplates();
        }
    }

    private void precompileTemplates() {
        // Precompile frequently used templates
        String[] templates = {"user/list", "user/form", "layout/base"};

        for (String template : templates) {
            try {
                templateEngine.process(template, new Context());
            } catch (Exception e) {
                log.warn("Failed to precompile template: " + template, e);
            }
        }
    }
}
```

## Security Integration

### Security Dialect Setup

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SpringSecurityDialect securityDialect() {
        return new SpringSecurityDialect();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/static/**").permitAll()
                .requestMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .defaultSuccessUrl("/dashboard")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .permitAll()
            );

        return http.build();
    }
}
```

### Security Context in Templates

```html
<!-- Authentication status -->
<div sec:authorize="isAuthenticated()">
  <span th:text="${#authentication.name}">User</span>
  <a th:href="@{/logout}" th:text="#{nav.logout}">Logout</a>
</div>

<div sec:authorize="!isAuthenticated()">
  <a th:href="@{/login}" th:text="#{nav.login}">Login</a>
  <a th:href="@{/register}" th:text="#{nav.register}">Register</a>
</div>

<!-- Role-based access -->
<div sec:authorize="hasRole('ADMIN')">
  <a th:href="@{/admin/dashboard}" th:text="#{nav.admin}">Admin Panel</a>
</div>

<!-- Permission-based access -->
<button
  sec:authorize="hasAuthority('USER_WRITE')"
  th:href="@{/users/{id}/edit(id=${user.id})}"
  th:text="#{action.edit}"
>
  Edit
</button>
```

## Testing Integration

### Template Testing

```java
@SpringBootTest
@AutoConfigureTestDatabase
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldShowUserList() throws Exception {
        mockMvc.perform(get("/users"))
               .andExpect(status().isOk())
               .andExpect(view().name("user/list"))
               .andExpect(model().attributeExists("users"))
               .andExpect(xpath("//table/tbody/tr").nodeCount(3));
    }

    @Test
    void shouldShowUserDetails() throws Exception {
        mockMvc.perform(get("/users/1"))
               .andExpect(status().isOk())
               .andExpect(view().name("user/view"))
               .andExpect(model().attributeExists("user"))
               .andExpect(xpath("//h1").string("John Doe"));
    }

    @Test
    void shouldCreateUser() throws Exception {
        mockMvc.perform(post("/users")
               .param("name", "New User")
               .param("email", "new@example.com")
               .with(csrf()))
               .andExpect(status().is3xxRedirection())
               .andExpect(redirectedUrl("/users"));
    }
}
```

### Template Integration Test

```java
@Test
void testTemplateRendering() {
    Context context = new Context();
    context.setVariable("user", createUser());

    String result = templateEngine.process("user/view", context);

    assertThat(result).contains("John Doe");
    assertThat(result).contains("john@example.com");
    assertThat(result).contains("<!DOCTYPE html>");
}
```

## Common Patterns

### Data Transfer Objects

```java
public class UserDTO {
    private String name;
    private String email;
    private List<String> roles;

    // Getters and setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    // Validation annotations
    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
    public String getName() { return name; }

    @Email(message = "Invalid email format")
    public String getEmail() { return email; }
}
```

### Service Layer Integration

```java
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private MessageSource messageSource;

    public Page<User> findAll(Pageable pageable) {
        return userRepository.findAll(pageable);
    }

    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException(
                    messageSource.getMessage("user.not.found", new Object[]{id},
                    LocaleContextHolder.getLocale())));
    }

    public User save(User user) {
        validateUser(user);
        return userRepository.save(user);
    }

    private void validateUser(User user) {
        if (user.getEmail() != null && userRepository.existsByEmail(user.getEmail())) {
            throw new EmailAlreadyExistsException(
                messageSource.getMessage("user.email.exists",
                new Object[]{user.getEmail()},
                LocaleContextHolder.getLocale()));
        }
    }
}
```

## Best Practices

### Template Organization

1. **Consistent naming** - Use lowercase, descriptive names
2. **Logical grouping** - Group related templates in directories
3. **Fragment reuse** - Extract common components
4. **Layout inheritance** - Use base layouts for consistency
5. **Separation of concerns** - Keep business logic in controllers

### Performance Considerations

1. **Enable caching in production** - Set `spring.thymeleaf.cache=true`
2. **Minimize template complexity** - Avoid deep nesting
3. **Use fragment composition** - Reuse common elements
4. **Optimize static resources** - Use CDN for libraries
5. **Monitor template performance** - Track rendering times

### Security Practices

1. **Always escape output** - Use `th:text` instead of unescaped content
2. **Validate input** - Use Spring Validation
3. **Authorize access** - Use Security dialect
4. **CSRF protection** - Include CSRF tokens in forms
5. **Secure static resources** - Proper access controls

### Maintainability

1. **Document fragments** - Clear fragment purposes
2. **Consistent styling** - Use CSS classes consistently
3. **Internationalization** - Externalize all text
4. **Error handling** - Graceful error pages
5. **Testing coverage** - Test template rendering
