Matter AI | Code Reviewer Documentation home pagelight logodark logo
  • Contact
  • Github
  • Sign in
  • Sign in
  • Documentation
  • Blog
  • Discord
  • Github
  • Introduction
    • What is Matter AI?
    Getting Started
    • QuickStart
    Product
    • Security Analysis
    • Code Quality
    • Agentic Chat
    • RuleSets
    • Memories
    • Analytics
    • Command List
    • Configurations
    Patterns
    • Languages
      • Supported Languages
      • Python
      • Java
      • JavaScript
      • TypeScript
      • Node.js
      • React
      • Fastify
      • Next.js
      • Terraform
      • C#
      • C++
      • C
      • Go
      • Rust
      • Swift
      • React Native
      • Spring Boot
      • Kotlin
      • Flutter
      • Ruby
      • PHP
      • Scala
      • Perl
      • R
      • Dart
      • Elixir
      • Erlang
      • Haskell
      • Lua
      • Julia
      • Clojure
      • Groovy
      • Fortran
      • COBOL
      • Pascal
      • Assembly
      • Bash
      • PowerShell
      • SQL
      • PL/SQL
      • T-SQL
      • MATLAB
      • Objective-C
      • VBA
      • ABAP
      • Apex
      • Apache Camel
      • Crystal
      • D
      • Delphi
      • Elm
      • F#
      • Hack
      • Lisp
      • OCaml
      • Prolog
      • Racket
      • Scheme
      • Solidity
      • Verilog
      • VHDL
      • Zig
      • MongoDB
      • ClickHouse
      • MySQL
      • GraphQL
      • Redis
      • Cassandra
      • Elasticsearch
    • Security
    • Performance
    Integrations
    • Code Repositories
    • Team Messengers
    • Ticketing
    Enterprise
    • Enterprise Deployment Overview
    • Enterprise Configurations
    • Observability and Fallbacks
    • Create Your Own GitHub App
    • Self-Hosting Options
    • RBAC
    Patterns
    Languages

    Spring Boot

    Spring Boot is an open-source Java-based framework used to create microservices and standalone Spring applications with minimal configuration. It simplifies the bootstrapping and development of new Spring applications.

    Spring Boot, while designed to simplify Java application development, can still be misused. Here are the most important anti-patterns to avoid when developing Spring Boot applications.

    Copy
    // Anti-pattern: Field injection
    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepository;
        
        @Autowired
        private EmailService emailService;
        
        // Methods using injected dependencies
    }
    
    // Better approach: Constructor injection
    @Service
    public class UserService {
        private final UserRepository userRepository;
        private final EmailService emailService;
        
        @Autowired // Optional in newer Spring versions
        public UserService(UserRepository userRepository, EmailService emailService) {
            this.userRepository = userRepository;
            this.emailService = emailService;
        }
        
        // Methods using injected dependencies
    }
    

    Field injection makes your code harder to test and hides dependencies. Use constructor injection to make dependencies explicit and enable easier unit testing.

    Copy
    // Anti-pattern: Hardcoded configuration values
    @Service
    public class EmailService {
        private final String smtpServer = "smtp.example.com";
        private final int port = 587;
        private final String username = "user";
        private final String password = "password";
        
        // Service methods
    }
    
    // Better approach: Use @ConfigurationProperties
    @Configuration
    @ConfigurationProperties(prefix = "mail")
    public class EmailProperties {
        private String smtpServer;
        private int port;
        private String username;
        private String password;
        
        // Getters and setters
    }
    
    @Service
    public class EmailService {
        private final EmailProperties emailProperties;
        
        public EmailService(EmailProperties emailProperties) {
            this.emailProperties = emailProperties;
        }
        
        // Service methods using emailProperties
    }
    

    Hardcoded configuration values make your application inflexible and difficult to deploy in different environments. Use @ConfigurationProperties to externalize configuration.

    Copy
    // Anti-pattern: Misusing @Transactional
    @Service
    public class OrderService {
        private final OrderRepository orderRepository;
        private final EmailService emailService;
        
        @Autowired
        public OrderService(OrderRepository orderRepository, EmailService emailService) {
            this.orderRepository = orderRepository;
            this.emailService = emailService;
        }
        
        @Transactional
        public void processOrder(Order order) {
            orderRepository.save(order);
            emailService.sendOrderConfirmation(order); // Might fail and roll back DB transaction
        }
    }
    
    // Better approach: Separate transactional boundaries
    @Service
    public class OrderService {
        private final OrderRepository orderRepository;
        private final EmailService emailService;
        
        @Autowired
        public OrderService(OrderRepository orderRepository, EmailService emailService) {
            this.orderRepository = orderRepository;
            this.emailService = emailService;
        }
        
        public void processOrder(Order order) {
            saveOrder(order);
            try {
                emailService.sendOrderConfirmation(order);
            } catch (Exception e) {
                // Log error but don't roll back transaction
                log.error("Failed to send confirmation email", e);
            }
        }
        
        @Transactional
        private void saveOrder(Order order) {
            orderRepository.save(order);
        }
    }
    

    Misusing @Transactional can lead to unexpected rollbacks or transaction leaks. Be mindful of transaction boundaries and separate transactional operations from non-transactional ones.

    Copy
    <!-- Anti-pattern: Manually managing dependencies -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- Many more dependencies -->
    </dependencies>
    
    <!-- Better approach: Use Spring Boot starters -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    

    Manually managing dependencies can lead to version conflicts and missing transitive dependencies. Use Spring Boot starters to get curated, compatible dependency sets.

    Copy
    // Anti-pattern: Manual JDBC or JPA code
    @Repository
    public class UserRepositoryImpl implements UserRepository {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Override
        public User findById(Long id) {
            return jdbcTemplate.queryForObject(
                "SELECT * FROM users WHERE id = ?",
                new Object[]{id},
                (rs, rowNum) -> new User(
                    rs.getLong("id"),
                    rs.getString("username"),
                    rs.getString("email")
                )
            );
        }
        
        // Many more manual implementations
    }
    
    // Better approach: Use Spring Data repositories
    public interface UserRepository extends JpaRepository<User, Long> {
        // All basic CRUD operations are automatically implemented
        
        // Custom query methods
        Optional<User> findByEmail(String email);
        List<User> findByLastNameOrderByFirstNameAsc(String lastName);
        
        @Query("SELECT u FROM User u WHERE u.status = :status")
        List<User> findByStatus(@Param("status") UserStatus status);
    }
    

    Writing manual data access code is error-prone and time-consuming. Use Spring Data repositories to automatically implement common data access patterns.

    Copy
    // Anti-pattern: Poor exception handling
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @Autowired
        private UserService userService;
        
        @GetMapping("/{id}")
        public User getUser(@PathVariable Long id) {
            return userService.findById(id); // Might throw exception
        }
    }
    
    // Better approach: Proper exception handling
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        private final UserService userService;
        
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
        
        @GetMapping("/{id}")
        public ResponseEntity<User> getUser(@PathVariable Long id) {
            return userService.findById(id)
                .map(user -> ResponseEntity.ok(user))
                .orElseThrow(() -> new ResourceNotFoundException("User not found with id " + id));
        }
    }
    
    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(ResourceNotFoundException.class)
        public ResponseEntity<?> handleResourceNotFoundException(ResourceNotFoundException ex) {
            ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
            return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
        }
        
        // Other exception handlers
    }
    

    Not handling exceptions properly leads to poor user experience and security issues. Use @ControllerAdvice and @ExceptionHandler to handle exceptions globally.

    Copy
    // Anti-pattern: Environment-specific configuration in code
    @Configuration
    public class DatabaseConfig {
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            if (System.getProperty("env").equals("dev")) {
                dataSource.setUrl("jdbc:h2:mem:testdb");
                // Dev configuration
            } else {
                dataSource.setUrl("jdbc:mysql://production-db:3306/app");
                // Production configuration
            }
            return dataSource;
        }
    }
    
    // Better approach: Use Spring profiles
    @Configuration
    @Profile("dev")
    public class DevDatabaseConfig {
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:h2:mem:testdb");
            // Dev configuration
            return dataSource;
        }
    }
    
    @Configuration
    @Profile("prod")
    public class ProdDatabaseConfig {
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:mysql://production-db:3306/app");
            // Production configuration
            return dataSource;
        }
    }
    

    Hardcoding environment-specific configuration makes deployment difficult. Use Spring profiles to separate configuration for different environments.

    Copy
    // Anti-pattern: Custom health checks and metrics
    @RestController
    @RequestMapping("/system")
    public class SystemController {
        @Autowired
        private DataSource dataSource;
        
        @GetMapping("/health")
        public Map<String, Object> health() {
            Map<String, Object> health = new HashMap<>();
            try (Connection conn = dataSource.getConnection()) {
                health.put("database", "UP");
            } catch (SQLException e) {
                health.put("database", "DOWN");
            }
            // More manual health checks
            return health;
        }
    }
    
    // Better approach: Use Spring Boot Actuator
    // In pom.xml or build.gradle
    // <dependency>
    //     <groupId>org.springframework.boot</groupId>
    //     <artifactId>spring-boot-starter-actuator</artifactId>
    // </dependency>
    
    // In application.properties or application.yml
    // management.endpoints.web.exposure.include=health,info,metrics
    // management.endpoint.health.show-details=always
    
    // Custom health indicator if needed
    @Component
    public class CustomHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // Check health logic
            if (isHealthy()) {
                return Health.up().withDetail("details", "Service is running smoothly").build();
            }
            return Health.down().withDetail("details", "Service is experiencing issues").build();
        }
    }
    

    Implementing custom monitoring endpoints is unnecessary and lacks features. Use Spring Boot Actuator for production-ready features like health checks, metrics, and monitoring.

    Copy
    // Anti-pattern: Custom security implementation
    @Component
    public class CustomAuthFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            String token = httpRequest.getHeader("Authorization");
            
            // Manual token validation
            if (token != null && validateToken(token)) {
                chain.doFilter(request, response);
            } else {
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }
        
        private boolean validateToken(String token) {
            // Custom token validation logic
            return true;
        }
    }
    
    // Better approach: Use Spring Security
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private JwtTokenProvider tokenProvider;
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .antMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .apply(new JwtConfigurer(tokenProvider));
        }
    }
    

    Custom security implementations are prone to vulnerabilities. Use Spring Security for robust, well-tested security features.

    Copy
    // Anti-pattern: Manual test setup
    public class UserServiceTest {
        private UserRepository userRepository;
        private EmailService emailService;
        private UserService userService;
        
        @Before
        public void setup() {
            userRepository = mock(UserRepository.class);
            emailService = mock(EmailService.class);
            userService = new UserService(userRepository, emailService);
        }
        
        @Test
        public void testFindById() {
            // Test implementation
        }
    }
    
    // Better approach: Use Spring Boot Test
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class UserServiceIntegrationTest {
        @Autowired
        private UserService userService;
        
        @MockBean
        private EmailService emailService;
        
        @Test
        public void testFindById() {
            // Test implementation with Spring Boot test features
        }
    }
    
    // Or for slices of the application
    @RunWith(SpringRunner.class)
    @WebMvcTest(UserController.class)
    public class UserControllerTest {
        @Autowired
        private MockMvc mockMvc;
        
        @MockBean
        private UserService userService;
        
        @Test
        public void testGetUser() throws Exception {
            // Test with MockMvc
            mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1));
        }
    }
    

    Manual test setup is verbose and error-prone. Use Spring Boot’s testing features like @SpringBootTest, @WebMvcTest, and @DataJpaTest for more effective testing.

    Copy
    <!-- Add to your pom.xml -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
    
    Copy
    # application.properties for development
    spring.devtools.restart.enabled=true
    spring.devtools.livereload.enabled=true
    

    Not using Spring Boot DevTools during development leads to slower development cycles. DevTools provides automatic restart, live reload, and other development-time features that improve productivity.

    Copy
    // Anti-pattern: Manual caching
    @Service
    public class ProductService {
        private final ProductRepository productRepository;
        private final Map<Long, Product> productCache = new ConcurrentHashMap<>();
        
        @Autowired
        public ProductService(ProductRepository productRepository) {
            this.productRepository = productRepository;
        }
        
        public Product getProductById(Long id) {
            if (productCache.containsKey(id)) {
                return productCache.get(id);
            }
            
            Product product = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
            productCache.put(id, product);
            return product;
        }
        
        public void updateProduct(Product product) {
            productRepository.save(product);
            productCache.put(product.getId(), product);
        }
    }
    
    // Better approach: Use Spring Boot Caching
    @Configuration
    @EnableCaching
    public class CachingConfig {
        @Bean
        public CacheManager cacheManager() {
            return new ConcurrentMapCacheManager("products");
        }
    }
    
    @Service
    public class ProductService {
        private final ProductRepository productRepository;
        
        @Autowired
        public ProductService(ProductRepository productRepository) {
            this.productRepository = productRepository;
        }
        
        @Cacheable("products")
        public Product getProductById(Long id) {
            return productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
        }
        
        @CachePut(value = "products", key = "#product.id")
        public Product updateProduct(Product product) {
            return productRepository.save(product);
        }
        
        @CacheEvict(value = "products", key = "#id")
        public void deleteProduct(Long id) {
            productRepository.deleteById(id);
        }
    }
    

    Manual caching is error-prone and difficult to maintain. Use Spring Boot’s caching abstraction with annotations like @Cacheable, @CachePut, and @CacheEvict for declarative caching.

    Copy
    // Anti-pattern: Manual validation
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @PostMapping
        public ResponseEntity<User> createUser(@RequestBody UserDto userDto) {
            // Manual validation
            if (userDto.getEmail() == null || !userDto.getEmail().contains("@")) {
                throw new BadRequestException("Invalid email");
            }
            if (userDto.getPassword() == null || userDto.getPassword().length() < 8) {
                throw new BadRequestException("Password must be at least 8 characters");
            }
            // More validations...
            
            // Process valid user
            User user = userService.createUser(userDto);
            return ResponseEntity.ok(user);
        }
    }
    
    // Better approach: Use Bean Validation
    public class UserDto {
        @NotBlank(message = "Name is required")
        private String name;
        
        @Email(message = "Invalid email format")
        @NotBlank(message = "Email is required")
        private String email;
        
        @Size(min = 8, message = "Password must be at least 8 characters")
        private String password;
        
        // Getters and setters
    }
    
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @PostMapping
        public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
            // Validation is handled automatically
            User user = userService.createUser(userDto);
            return ResponseEntity.ok(user);
        }
    }
    
    // Global validation error handler
    @ControllerAdvice
    public class ValidationExceptionHandler {
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
            Map<String, String> errors = new HashMap<>();
            ex.getBindingResult().getAllErrors().forEach(error -> {
                String fieldName = ((FieldError) error).getField();
                String errorMessage = error.getDefaultMessage();
                errors.put(fieldName, errorMessage);
            });
            return ResponseEntity.badRequest().body(errors);
        }
    }
    

    Manual validation is error-prone and verbose. Use Bean Validation (JSR-380) with annotations like @Valid, @NotNull, @Size, etc., for declarative validation.

    Copy
    // Anti-pattern: Custom health check endpoints
    @RestController
    @RequestMapping("/health")
    public class HealthCheckController {
        @Autowired
        private DataSource dataSource;
        
        @GetMapping
        public Map<String, Object> checkHealth() {
            Map<String, Object> health = new HashMap<>();
            health.put("status", "UP");
            
            try (Connection conn = dataSource.getConnection()) {
                health.put("database", "UP");
            } catch (Exception e) {
                health.put("database", "DOWN");
                health.put("status", "DOWN");
            }
            
            // More health checks
            return health;
        }
    }
    
    // Better approach: Use Spring Boot Actuator
    // In application.properties
    // management.endpoint.health.show-details=always
    // management.endpoints.web.exposure.include=health,info,metrics
    
    // Custom health indicator
    @Component
    public class DatabaseHealthIndicator implements HealthIndicator {
        private final DataSource dataSource;
        
        @Autowired
        public DatabaseHealthIndicator(DataSource dataSource) {
            this.dataSource = dataSource;
        }
        
        @Override
        public Health health() {
            try (Connection conn = dataSource.getConnection()) {
                // Execute a simple query
                try (Statement stmt = conn.createStatement()) {
                    stmt.execute("SELECT 1");
                }
                return Health.up().withDetail("database", "Available").build();
            } catch (Exception e) {
                return Health.down()
                    .withDetail("database", "Unavailable")
                    .withException(e)
                    .build();
            }
        }
    }
    

    Custom health check endpoints lack standardization and features. Use Spring Boot Actuator’s health endpoints and custom health indicators for comprehensive health monitoring.

    React NativeKotlin
    websitexgithublinkedin
    Powered by Mintlify
    Assistant
    Responses are generated using AI and may contain mistakes.