Comprehensive application framework and inversion of control container for the Java platform providing dependency injection, AOP, data access, transaction management, and web framework capabilities
Spring provides additional modules for integration with various enterprise technologies, cross-cutting concerns, and advanced features. This includes spring-aspects, spring-instrument, spring-context-support, spring-websocket, and spring-oxm modules.
<!-- Spring Aspects (AspectJ integration) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.39</version>
</dependency>
<!-- Spring Instrument (Load-time weaving) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.3.39</version>
</dependency>
<!-- Spring Context Support (Additional context implementations) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.39</version>
</dependency>
<!-- Spring WebSocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.3.39</version>
</dependency>
<!-- Spring OXM (Object/XML Mapping) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>5.3.39</version>
</dependency>
<!-- AspectJ Runtime -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<!-- AspectJ Weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>// AspectJ Integration
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
import org.springframework.context.annotation.LoadTimeWeavingConfigurer;
import org.springframework.instrument.classloading.LoadTimeWeaver;
// Scheduling Support
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
// Caching Support
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
// WebSocket Support
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
// STOMP Support
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.annotation.EnableWebSocketMessageBroker;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
// OXM (Object/XML Mapping)
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
// Validation Support
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import javax.validation.constraints.*;// Enables load-time weaving support
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LoadTimeWeavingConfiguration.class)
public @interface EnableLoadTimeWeaving {
AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;
enum AspectJWeaving {
ENABLED, DISABLED, AUTODETECT
}
}
// Configuration interface for load-time weaving
public interface LoadTimeWeavingConfigurer {
LoadTimeWeaver getLoadTimeWeaver();
}
// Load-time weaver interface
public interface LoadTimeWeaver {
void addTransformer(ClassFileTransformer transformer);
ClassLoader getInstrumentableClassLoader();
ClassLoader getThrowawayClassLoader();
}
// Configuration for AspectJ load-time weaving
@Configuration
@EnableLoadTimeWeaving
public class AspectJConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
@Bean
public AnnotationBeanConfigurerAspect beanConfigurerAspect() {
return AnnotationBeanConfigurerAspect.aspectOf();
}
}// Aspect for configuring Spring beans
public aspect AnnotationBeanConfigurerAspect {
// Configures objects with @Configurable annotation
public pointcut beanConstruction(Object bean) :
initialization((@Configurable *).new(..)) && this(bean);
after(Object bean) returning : beanConstruction(bean) {
configureBean(bean);
}
public void configureBean(Object bean) {
// Configure the bean using Spring's dependency injection
}
}
// Annotation for domain objects that should be configured by Spring
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Configurable {
String value() default "";
Autowire autowire() default Autowire.NO;
boolean dependencyCheck() default false;
boolean preConstruction() default false;
}
// Usage example - domain object with dependency injection
@Entity
@Configurable
public class Order {
@Transient
@Autowired
private OrderValidator orderValidator;
@Transient
@Autowired
private PricingService pricingService;
private Long id;
private BigDecimal amount;
public void validate() {
orderValidator.validate(this);
}
public BigDecimal calculateTotalPrice() {
return pricingService.calculatePrice(this);
}
}// Enables async method execution
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Async.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
// Annotation to mark methods for async execution
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
// Configuration for async processing
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
// Service with async methods
@Service
public class NotificationService {
@Async
public CompletableFuture<String> sendEmailAsync(String to, String subject, String body) {
// Simulate email sending delay
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
String result = "Email sent to " + to;
return CompletableFuture.completedFuture(result);
}
@Async("customExecutor")
public void processInBackground(ProcessingTask task) {
// Long-running background processing
task.process();
}
@Bean("customExecutor")
public TaskExecutor customTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("custom-");
executor.initialize();
return executor;
}
}// Enables scheduled task execution
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(SchedulingConfiguration.class)
public @interface EnableScheduling {
}
// Annotation for scheduling methods
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String CRON_DISABLED = "-";
String cron() default "";
String zone() default "";
long fixedDelay() default -1;
String fixedDelayString() default "";
long fixedRate() default -1;
String fixedRateString() default "";
long initialDelay() default -1;
String initialDelayString() default "";
}
// Configuration for scheduling
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
}
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("scheduled-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
scheduler.initialize();
return scheduler;
}
}
// Service with scheduled methods
@Service
public class ScheduledTaskService {
private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskService.class);
// Fixed rate execution (every 5 seconds)
@Scheduled(fixedRate = 5000)
public void performPeriodicTask() {
logger.info("Executing periodic task at {}", LocalDateTime.now());
}
// Fixed delay execution (5 seconds after previous completion)
@Scheduled(fixedDelay = 5000)
public void performTaskWithDelay() {
logger.info("Executing task with delay at {}", LocalDateTime.now());
// Simulate processing time
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Cron-based scheduling (every minute)
@Scheduled(cron = "0 * * * * *")
public void performCronTask() {
logger.info("Executing cron task at {}", LocalDateTime.now());
}
// Daily cleanup at 2 AM
@Scheduled(cron = "0 0 2 * * *", zone = "America/New_York")
public void dailyCleanup() {
logger.info("Performing daily cleanup at {}", LocalDateTime.now());
cleanupOldData();
}
// Conditional scheduling based on properties
@Scheduled(fixedRate = 10000)
@ConditionalOnProperty(name = "app.monitoring.enabled", havingValue = "true")
public void monitoringTask() {
logger.info("Performing monitoring task");
performHealthCheck();
}
// Initial delay before first execution
@Scheduled(fixedRate = 30000, initialDelay = 10000)
public void taskWithInitialDelay() {
logger.info("Task with initial delay executed");
}
private void cleanupOldData() {
// Cleanup logic
}
private void performHealthCheck() {
// Health check logic
}
}// Enables caching support
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
// Annotation to cache method results
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
}
// Annotation to evict cache entries
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
boolean allEntries() default false;
boolean beforeInvocation() default false;
}
// Annotation to update cache entries
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
}// Cache configuration
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
cacheManager.setCacheNames(Arrays.asList(
"users", "products", "orders", "settings"
));
cacheManager.setAllowNullValues(false);
return cacheManager;
}
// Custom key generator
@Bean
public KeyGenerator customKeyGenerator() {
return (target, method, params) -> {
StringBuilder key = new StringBuilder();
key.append(target.getClass().getSimpleName());
key.append(".").append(method.getName());
for (Object param : params) {
key.append(".").append(param != null ? param.toString() : "null");
}
return key.toString();
};
}
// Redis cache manager (if using Redis)
@Bean
@ConditionalOnClass(RedisTemplate.class)
public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofHours(1));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
// Service with caching
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
System.out.println("Loading user from database: " + id);
return userRepository.findById(id).orElse(null);
}
@Cacheable(value = "users", key = "#username")
public User findByUsername(String username) {
System.out.println("Loading user by username: " + username);
return userRepository.findByUsername(username).orElse(null);
}
@CachePut(value = "users", key = "#result.id")
public User save(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteById(Long id) {
userRepository.deleteById(id);
}
@CacheEvict(value = "users", allEntries = true)
public void clearAllUsers() {
// This will clear all entries from the "users" cache
}
// Conditional caching
@Cacheable(value = "users", condition = "#id > 0", unless = "#result == null")
public User findByIdConditional(Long id) {
return userRepository.findById(id).orElse(null);
}
// Custom key generator
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public List<User> findByAgeRange(int minAge, int maxAge) {
return userRepository.findByAgeBetween(minAge, maxAge);
}
}// Enables WebSocket support
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DelegatingWebSocketConfiguration.class)
public @interface EnableWebSocket {
}
// Configuration interface for WebSocket
public interface WebSocketConfigurer {
void registerWebSocketHandlers(WebSocketHandlerRegistry registry);
}
// WebSocket handler interface
public interface WebSocketHandler {
void afterConnectionEstablished(WebSocketSession session) throws Exception;
void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;
boolean supportsPartialMessages();
}
// WebSocket session interface
public interface WebSocketSession extends Closeable {
String getId();
URI getUri();
HttpHeaders getHandshakeHeaders();
Map<String, Object> getAttributes();
Principal getPrincipal();
InetSocketAddress getLocalAddress();
InetSocketAddress getRemoteAddress();
String getAcceptedProtocol();
void sendMessage(WebSocketMessage<?> message) throws IOException;
boolean isOpen();
void close() throws IOException;
void close(CloseStatus status) throws IOException;
}
// WebSocket configuration
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/chat")
.addInterceptors(new HttpSessionHandshakeInterceptor())
.setAllowedOrigins("*");
registry.addHandler(new EchoWebSocketHandler(), "/echo")
.withSockJS(); // Enable SockJS fallback
}
@Bean
public WebSocketHandler chatWebSocketHandler() {
return new ChatWebSocketHandler();
}
}
// WebSocket handler implementation
public class ChatWebSocketHandler extends TextWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(ChatWebSocketHandler.class);
private final Set<WebSocketSession> sessions = new CopyOnWriteArraySet<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
logger.info("WebSocket connection established: {}", session.getId());
// Send welcome message
String welcomeMessage = "Welcome to chat! Session ID: " + session.getId();
session.sendMessage(new TextMessage(welcomeMessage));
}
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.info("Received message from {}: {}", session.getId(), message.getPayload());
// Broadcast message to all connected sessions
String broadcastMessage = session.getId() + ": " + message.getPayload();
for (WebSocketSession webSocketSession : sessions) {
if (webSocketSession.isOpen()) {
webSocketSession.sendMessage(new TextMessage(broadcastMessage));
}
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
logger.error("Transport error for session {}: {}", session.getId(), exception.getMessage());
sessions.remove(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
sessions.remove(session);
logger.info("WebSocket connection closed: {} with status {}", session.getId(), closeStatus);
}
}// Enables WebSocket message broker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DelegatingWebSocketMessageBrokerConfiguration.class)
public @interface EnableWebSocketMessageBroker {
}
// Configuration interface for WebSocket message broker
public interface WebSocketMessageBrokerConfigurer {
default void configureMessageBroker(MessageBrokerRegistry registry) {
}
default void registerStompEndpoints(StompEndpointRegistry registry) {
}
default void configureWebSocketTransport(WebSocketTransportRegistration registry) {
}
default void configureClientInboundChannel(ChannelRegistration registration) {
}
default void configureClientOutboundChannel(ChannelRegistration registration) {
}
}
// STOMP configuration
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// Enable simple in-memory broker
registry.enableSimpleBroker("/topic", "/queue");
// Set application destination prefix
registry.setApplicationDestinationPrefixes("/app");
// Configure user destination prefix
registry.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// Register STOMP endpoint
registry.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// Authentication logic here
}
return message;
}
});
}
}
// STOMP message controller
@Controller
public class WebSocketMessageController {
private final SimpMessagingTemplate messagingTemplate;
public WebSocketMessageController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
return chatMessage;
}
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
public ChatMessage addUser(@Payload ChatMessage chatMessage,
SimpMessageHeaderAccessor headerAccessor) {
// Add username in web socket session
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
return chatMessage;
}
// Send message to specific user
@MessageMapping("/private")
public void sendToUser(@Payload PrivateMessage message, Principal principal) {
messagingTemplate.convertAndSendToUser(
message.getRecipient(),
"/queue/messages",
message
);
}
// Scheduled message broadcasting
@Scheduled(fixedRate = 30000)
public void sendPeriodicMessage() {
ServerMessage message = ServerMessage.builder()
.content("Server time: " + LocalDateTime.now())
.timestamp(LocalDateTime.now())
.build();
messagingTemplate.convertAndSend("/topic/server", message);
}
}
// WebSocket event listener
@Component
public class WebSocketEventListener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class);
@Autowired
private SimpMessagingTemplate messagingTemplate;
@EventListener
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
logger.info("Received a new web socket connection");
}
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
String username = (String) headerAccessor.getSessionAttributes().get("username");
if (username != null) {
logger.info("User disconnected: {}", username);
ChatMessage chatMessage = ChatMessage.builder()
.type(MessageType.LEAVE)
.sender(username)
.build();
messagingTemplate.convertAndSend("/topic/public", chatMessage);
}
}
}// Interface for marshalling objects to XML
public interface Marshaller {
boolean supports(Class<?> clazz);
void marshal(Object graph, Result result) throws IOException, XmlMappingException;
}
// Interface for unmarshalling XML to objects
public interface Unmarshaller {
boolean supports(Class<?> clazz);
Object unmarshal(Source source) throws IOException, XmlMappingException;
}
// JAXB marshaller implementation
public class Jaxb2Marshaller implements Marshaller, Unmarshaller, InitializingBean {
public void setContextPath(String contextPath);
public void setClassesToBeBound(Class<?>... classesToBeBound);
public void setPackagesToScan(String... packagesToScan);
public void setSchema(Resource schema);
public void setMarshallerProperties(Map<String, ?> properties);
public void setUnmarshallerProperties(Map<String, ?> properties);
public void setValidationEventHandler(ValidationEventHandler validationEventHandler);
@Override
public boolean supports(Class<?> clazz);
@Override
public void marshal(Object graph, Result result) throws XmlMappingException;
@Override
public Object unmarshal(Source source) throws XmlMappingException;
}
// OXM configuration
@Configuration
public class OxmConfig {
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.example.xml.model");
Map<String, Object> properties = new HashMap<>();
properties.put(Marshaller.JAXB_FORMATTED_OUTPUT, true);
properties.put(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.setMarshallerProperties(properties);
return marshaller;
}
@Bean
public MarshallingService marshallingService(Jaxb2Marshaller marshaller) {
return new MarshallingService(marshaller, marshaller);
}
}
// XML model with JAXB annotations
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserXml {
@XmlElement
private Long id;
@XmlElement
private String username;
@XmlElement
private String email;
@XmlElement(name = "created_date")
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime createdDate;
@XmlElementWrapper(name = "orders")
@XmlElement(name = "order")
private List<OrderXml> orders;
// Constructors, getters, setters
}
// Marshalling service
@Service
public class MarshallingService {
private final Marshaller marshaller;
private final Unmarshaller unmarshaller;
public MarshallingService(Marshaller marshaller, Unmarshaller unmarshaller) {
this.marshaller = marshaller;
this.unmarshaller = unmarshaller;
}
public String marshalToString(Object object) {
try (StringWriter writer = new StringWriter()) {
marshaller.marshal(object, new StreamResult(writer));
return writer.toString();
} catch (Exception e) {
throw new XmlProcessingException("Failed to marshal object", e);
}
}
public void marshalToFile(Object object, String filename) {
try (FileOutputStream fos = new FileOutputStream(filename)) {
marshaller.marshal(object, new StreamResult(fos));
} catch (Exception e) {
throw new XmlProcessingException("Failed to marshal to file", e);
}
}
@SuppressWarnings("unchecked")
public <T> T unmarshalFromString(String xml, Class<T> clazz) {
try (StringReader reader = new StringReader(xml)) {
return (T) unmarshaller.unmarshal(new StreamSource(reader));
} catch (Exception e) {
throw new XmlProcessingException("Failed to unmarshal from string", e);
}
}
@SuppressWarnings("unchecked")
public <T> T unmarshalFromFile(String filename, Class<T> clazz) {
try (FileInputStream fis = new FileInputStream(filename)) {
return (T) unmarshaller.unmarshal(new StreamSource(fis));
} catch (Exception e) {
throw new XmlProcessingException("Failed to unmarshal from file", e);
}
}
}// Mail configuration
@Configuration
@ConditionalOnProperty(name = "spring.mail.host")
public class MailConfig {
@Bean
public JavaMailSender mailSender(
@Value("${spring.mail.host}") String host,
@Value("${spring.mail.port:587}") int port,
@Value("${spring.mail.username}") String username,
@Value("${spring.mail.password}") String password) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(host);
mailSender.setPort(port);
mailSender.setUsername(username);
mailSender.setPassword(password);
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.debug", "false");
return mailSender;
}
@Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(emailTemplateResolver());
return templateEngine;
}
private ITemplateResolver emailTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("/mail-templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false);
return templateResolver;
}
}
// Email service
@Service
public class EmailService {
private final JavaMailSender mailSender;
private final TemplateEngine templateEngine;
@Value("${app.mail.from}")
private String fromEmail;
public EmailService(JavaMailSender mailSender, TemplateEngine templateEngine) {
this.mailSender = mailSender;
this.templateEngine = templateEngine;
}
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(fromEmail);
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}
public void sendHtmlEmail(String to, String subject, String templateName, Map<String, Object> variables) {
try {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
helper.setFrom(fromEmail);
helper.setTo(to);
helper.setSubject(subject);
Context context = new Context();
context.setVariables(variables);
String htmlContent = templateEngine.process(templateName, context);
helper.setText(htmlContent, true);
mailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new EmailException("Failed to send HTML email", e);
}
}
public void sendEmailWithAttachment(String to, String subject, String text, String attachmentPath) {
try {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(fromEmail);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text);
File attachment = new File(attachmentPath);
helper.addAttachment(attachment.getName(), attachment);
mailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new EmailException("Failed to send email with attachment", e);
}
}
@Async
public CompletableFuture<Void> sendEmailAsync(String to, String subject, String templateName, Map<String, Object> variables) {
sendHtmlEmail(to, subject, templateName, variables);
return CompletableFuture.completedFuture(null);
}
}// Validation configuration
@Configuration
public class ValidationConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(LocalValidatorFactoryBean validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
}
}
// Service with method-level validation
@Service
@Validated
public class UserValidationService {
public User createUser(@Valid CreateUserRequest request) {
// Method parameter validation
return new User(request.getUsername(), request.getEmail());
}
public List<User> getUsers(
@Min(0) @Max(100) int page,
@Min(1) @Max(50) int size) {
// Parameter validation with constraints
return findUsers(page, size);
}
public void updateUser(
@NotNull @Positive Long id,
@Valid @NotNull UpdateUserRequest request) {
// Multiple parameter validation
updateUserById(id, request);
}
@NotNull
public User findUserById(@NotNull @Positive Long id) {
// Return value validation
return getUserById(id);
}
private User getUserById(Long id) {
// Implementation
return new User();
}
private List<User> findUsers(int page, int size) {
// Implementation
return new ArrayList<>();
}
private void updateUserById(Long id, UpdateUserRequest request) {
// Implementation
}
}
// Custom validator
@Component
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
private final UserRepository userRepository;
public UniqueUsernameValidator(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
if (username == null) {
return true; // Let @NotNull handle null values
}
return !userRepository.existsByUsername(username);
}
}
// Custom validation annotation
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
@Documented
public @interface UniqueUsername {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// DTO with validation annotations
public class CreateUserRequest {
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
@UniqueUsername
private String username;
@NotBlank(message = "Email is required")
@Email(message = "Email should be valid")
private String email;
@NotBlank(message = "First name is required")
@Size(max = 50)
private String firstName;
@NotBlank(message = "Last name is required")
@Size(max = 50)
private String lastName;
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$", message = "Invalid phone number")
private String phoneNumber;
@Past(message = "Birth date must be in the past")
private LocalDate birthDate;
// Constructors, getters, setters
}Spring's integration and support modules provide essential capabilities for building enterprise applications with advanced features like AspectJ integration, caching, scheduling, WebSocket communication, XML processing, email support, and comprehensive validation.
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring