0
# Integration & Support
1
2
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.
3
4
## Maven Dependencies
5
6
```xml
7
<!-- Spring Aspects (AspectJ integration) -->
8
<dependency>
9
<groupId>org.springframework</groupId>
10
<artifactId>spring-aspects</artifactId>
11
<version>5.3.39</version>
12
</dependency>
13
14
<!-- Spring Instrument (Load-time weaving) -->
15
<dependency>
16
<groupId>org.springframework</groupId>
17
<artifactId>spring-instrument</artifactId>
18
<version>5.3.39</version>
19
</dependency>
20
21
<!-- Spring Context Support (Additional context implementations) -->
22
<dependency>
23
<groupId>org.springframework</groupId>
24
<artifactId>spring-context-support</artifactId>
25
<version>5.3.39</version>
26
</dependency>
27
28
<!-- Spring WebSocket -->
29
<dependency>
30
<groupId>org.springframework</groupId>
31
<artifactId>spring-websocket</artifactId>
32
<version>5.3.39</version>
33
</dependency>
34
35
<!-- Spring OXM (Object/XML Mapping) -->
36
<dependency>
37
<groupId>org.springframework</groupId>
38
<artifactId>spring-oxm</artifactId>
39
<version>5.3.39</version>
40
</dependency>
41
42
<!-- AspectJ Runtime -->
43
<dependency>
44
<groupId>org.aspectj</groupId>
45
<artifactId>aspectjrt</artifactId>
46
<version>1.9.7</version>
47
</dependency>
48
49
<!-- AspectJ Weaver -->
50
<dependency>
51
<groupId>org.aspectj</groupId>
52
<artifactId>aspectjweaver</artifactId>
53
<version>1.9.7</version>
54
</dependency>
55
```
56
57
## Core Imports
58
59
```java { .api }
60
// AspectJ Integration
61
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
62
import org.springframework.context.annotation.EnableLoadTimeWeaving;
63
import org.springframework.context.annotation.LoadTimeWeavingConfigurer;
64
import org.springframework.instrument.classloading.LoadTimeWeaver;
65
66
// Scheduling Support
67
import org.springframework.scheduling.annotation.Async;
68
import org.springframework.scheduling.annotation.EnableAsync;
69
import org.springframework.scheduling.annotation.Scheduled;
70
import org.springframework.scheduling.annotation.EnableScheduling;
71
import org.springframework.scheduling.TaskScheduler;
72
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
73
74
// Caching Support
75
import org.springframework.cache.annotation.Cacheable;
76
import org.springframework.cache.annotation.CacheEvict;
77
import org.springframework.cache.annotation.EnableCaching;
78
import org.springframework.cache.CacheManager;
79
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
80
81
// WebSocket Support
82
import org.springframework.web.socket.WebSocketHandler;
83
import org.springframework.web.socket.WebSocketSession;
84
import org.springframework.web.socket.config.annotation.EnableWebSocket;
85
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
86
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
87
88
// STOMP Support
89
import org.springframework.messaging.handler.annotation.MessageMapping;
90
import org.springframework.messaging.simp.annotation.EnableWebSocketMessageBroker;
91
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
92
93
// OXM (Object/XML Mapping)
94
import org.springframework.oxm.Marshaller;
95
import org.springframework.oxm.Unmarshaller;
96
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
97
98
// Validation Support
99
import org.springframework.validation.annotation.Validated;
100
import javax.validation.Valid;
101
import javax.validation.constraints.*;
102
```
103
104
## AspectJ Integration
105
106
### Load-Time Weaving
107
108
```java { .api }
109
// Enables load-time weaving support
110
@Target(ElementType.TYPE)
111
@Retention(RetentionPolicy.RUNTIME)
112
@Documented
113
@Import(LoadTimeWeavingConfiguration.class)
114
public @interface EnableLoadTimeWeaving {
115
116
AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;
117
118
enum AspectJWeaving {
119
ENABLED, DISABLED, AUTODETECT
120
}
121
}
122
123
// Configuration interface for load-time weaving
124
public interface LoadTimeWeavingConfigurer {
125
126
LoadTimeWeaver getLoadTimeWeaver();
127
}
128
129
// Load-time weaver interface
130
public interface LoadTimeWeaver {
131
132
void addTransformer(ClassFileTransformer transformer);
133
134
ClassLoader getInstrumentableClassLoader();
135
136
ClassLoader getThrowawayClassLoader();
137
}
138
139
// Configuration for AspectJ load-time weaving
140
@Configuration
141
@EnableLoadTimeWeaving
142
public class AspectJConfig implements LoadTimeWeavingConfigurer {
143
144
@Override
145
public LoadTimeWeaver getLoadTimeWeaver() {
146
return new InstrumentationLoadTimeWeaver();
147
}
148
149
@Bean
150
public AnnotationBeanConfigurerAspect beanConfigurerAspect() {
151
return AnnotationBeanConfigurerAspect.aspectOf();
152
}
153
}
154
```
155
156
### Dependency Injection in Domain Objects
157
158
```java { .api }
159
// Aspect for configuring Spring beans
160
public aspect AnnotationBeanConfigurerAspect {
161
162
// Configures objects with @Configurable annotation
163
public pointcut beanConstruction(Object bean) :
164
initialization((@Configurable *).new(..)) && this(bean);
165
166
after(Object bean) returning : beanConstruction(bean) {
167
configureBean(bean);
168
}
169
170
public void configureBean(Object bean) {
171
// Configure the bean using Spring's dependency injection
172
}
173
}
174
175
// Annotation for domain objects that should be configured by Spring
176
@Target(ElementType.TYPE)
177
@Retention(RetentionPolicy.RUNTIME)
178
@Documented
179
public @interface Configurable {
180
181
String value() default "";
182
183
Autowire autowire() default Autowire.NO;
184
185
boolean dependencyCheck() default false;
186
187
boolean preConstruction() default false;
188
}
189
190
// Usage example - domain object with dependency injection
191
@Entity
192
@Configurable
193
public class Order {
194
195
@Transient
196
@Autowired
197
private OrderValidator orderValidator;
198
199
@Transient
200
@Autowired
201
private PricingService pricingService;
202
203
private Long id;
204
private BigDecimal amount;
205
206
public void validate() {
207
orderValidator.validate(this);
208
}
209
210
public BigDecimal calculateTotalPrice() {
211
return pricingService.calculatePrice(this);
212
}
213
}
214
```
215
216
## Scheduling Support
217
218
### Async Processing
219
220
```java { .api }
221
// Enables async method execution
222
@Target(ElementType.TYPE)
223
@Retention(RetentionPolicy.RUNTIME)
224
@Documented
225
@Import(AsyncConfigurationSelector.class)
226
public @interface EnableAsync {
227
228
Class<? extends Annotation> annotation() default Async.class;
229
230
boolean proxyTargetClass() default false;
231
232
AdviceMode mode() default AdviceMode.PROXY;
233
234
int order() default Ordered.LOWEST_PRECEDENCE;
235
}
236
237
// Annotation to mark methods for async execution
238
@Target({ElementType.TYPE, ElementType.METHOD})
239
@Retention(RetentionPolicy.RUNTIME)
240
@Documented
241
public @interface Async {
242
243
String value() default "";
244
}
245
246
// Configuration for async processing
247
@Configuration
248
@EnableAsync
249
public class AsyncConfig implements AsyncConfigurer {
250
251
@Override
252
public Executor getAsyncExecutor() {
253
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
254
executor.setCorePoolSize(5);
255
executor.setMaxPoolSize(20);
256
executor.setQueueCapacity(100);
257
executor.setThreadNamePrefix("async-");
258
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
259
executor.initialize();
260
return executor;
261
}
262
263
@Override
264
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
265
return new SimpleAsyncUncaughtExceptionHandler();
266
}
267
}
268
269
// Service with async methods
270
@Service
271
public class NotificationService {
272
273
@Async
274
public CompletableFuture<String> sendEmailAsync(String to, String subject, String body) {
275
// Simulate email sending delay
276
try {
277
Thread.sleep(1000);
278
} catch (InterruptedException e) {
279
Thread.currentThread().interrupt();
280
}
281
282
String result = "Email sent to " + to;
283
return CompletableFuture.completedFuture(result);
284
}
285
286
@Async("customExecutor")
287
public void processInBackground(ProcessingTask task) {
288
// Long-running background processing
289
task.process();
290
}
291
292
@Bean("customExecutor")
293
public TaskExecutor customTaskExecutor() {
294
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
295
executor.setCorePoolSize(2);
296
executor.setMaxPoolSize(10);
297
executor.setThreadNamePrefix("custom-");
298
executor.initialize();
299
return executor;
300
}
301
}
302
```
303
304
### Task Scheduling
305
306
```java { .api }
307
// Enables scheduled task execution
308
@Target(ElementType.TYPE)
309
@Retention(RetentionPolicy.RUNTIME)
310
@Documented
311
@Import(SchedulingConfiguration.class)
312
public @interface EnableScheduling {
313
}
314
315
// Annotation for scheduling methods
316
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
317
@Retention(RetentionPolicy.RUNTIME)
318
@Documented
319
@Repeatable(Schedules.class)
320
public @interface Scheduled {
321
322
String CRON_DISABLED = "-";
323
324
String cron() default "";
325
326
String zone() default "";
327
328
long fixedDelay() default -1;
329
330
String fixedDelayString() default "";
331
332
long fixedRate() default -1;
333
334
String fixedRateString() default "";
335
336
long initialDelay() default -1;
337
338
String initialDelayString() default "";
339
}
340
341
// Configuration for scheduling
342
@Configuration
343
@EnableScheduling
344
public class SchedulingConfig implements SchedulingConfigurer {
345
346
@Override
347
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
348
taskRegistrar.setScheduler(taskScheduler());
349
}
350
351
@Bean
352
public TaskScheduler taskScheduler() {
353
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
354
scheduler.setPoolSize(5);
355
scheduler.setThreadNamePrefix("scheduled-");
356
scheduler.setWaitForTasksToCompleteOnShutdown(true);
357
scheduler.setAwaitTerminationSeconds(30);
358
scheduler.initialize();
359
return scheduler;
360
}
361
}
362
363
// Service with scheduled methods
364
@Service
365
public class ScheduledTaskService {
366
367
private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskService.class);
368
369
// Fixed rate execution (every 5 seconds)
370
@Scheduled(fixedRate = 5000)
371
public void performPeriodicTask() {
372
logger.info("Executing periodic task at {}", LocalDateTime.now());
373
}
374
375
// Fixed delay execution (5 seconds after previous completion)
376
@Scheduled(fixedDelay = 5000)
377
public void performTaskWithDelay() {
378
logger.info("Executing task with delay at {}", LocalDateTime.now());
379
// Simulate processing time
380
try {
381
Thread.sleep(2000);
382
} catch (InterruptedException e) {
383
Thread.currentThread().interrupt();
384
}
385
}
386
387
// Cron-based scheduling (every minute)
388
@Scheduled(cron = "0 * * * * *")
389
public void performCronTask() {
390
logger.info("Executing cron task at {}", LocalDateTime.now());
391
}
392
393
// Daily cleanup at 2 AM
394
@Scheduled(cron = "0 0 2 * * *", zone = "America/New_York")
395
public void dailyCleanup() {
396
logger.info("Performing daily cleanup at {}", LocalDateTime.now());
397
cleanupOldData();
398
}
399
400
// Conditional scheduling based on properties
401
@Scheduled(fixedRate = 10000)
402
@ConditionalOnProperty(name = "app.monitoring.enabled", havingValue = "true")
403
public void monitoringTask() {
404
logger.info("Performing monitoring task");
405
performHealthCheck();
406
}
407
408
// Initial delay before first execution
409
@Scheduled(fixedRate = 30000, initialDelay = 10000)
410
public void taskWithInitialDelay() {
411
logger.info("Task with initial delay executed");
412
}
413
414
private void cleanupOldData() {
415
// Cleanup logic
416
}
417
418
private void performHealthCheck() {
419
// Health check logic
420
}
421
}
422
```
423
424
## Caching Support
425
426
### Caching Annotations
427
428
```java { .api }
429
// Enables caching support
430
@Target(ElementType.TYPE)
431
@Retention(RetentionPolicy.RUNTIME)
432
@Documented
433
@Import(CachingConfigurationSelector.class)
434
public @interface EnableCaching {
435
436
boolean proxyTargetClass() default false;
437
438
AdviceMode mode() default AdviceMode.PROXY;
439
440
int order() default Ordered.LOWEST_PRECEDENCE;
441
}
442
443
// Annotation to cache method results
444
@Target({ElementType.TYPE, ElementType.METHOD})
445
@Retention(RetentionPolicy.RUNTIME)
446
@Inherited
447
@Documented
448
public @interface Cacheable {
449
450
@AliasFor("cacheNames")
451
String[] value() default {};
452
453
@AliasFor("value")
454
String[] cacheNames() default {};
455
456
String key() default "";
457
458
String keyGenerator() default "";
459
460
String cacheManager() default "";
461
462
String cacheResolver() default "";
463
464
String condition() default "";
465
466
String unless() default "";
467
468
boolean sync() default false;
469
}
470
471
// Annotation to evict cache entries
472
@Target({ElementType.TYPE, ElementType.METHOD})
473
@Retention(RetentionPolicy.RUNTIME)
474
@Inherited
475
@Documented
476
public @interface CacheEvict {
477
478
@AliasFor("cacheNames")
479
String[] value() default {};
480
481
@AliasFor("value")
482
String[] cacheNames() default {};
483
484
String key() default "";
485
486
String keyGenerator() default "";
487
488
String cacheManager() default "";
489
490
String cacheResolver() default "";
491
492
String condition() default "";
493
494
boolean allEntries() default false;
495
496
boolean beforeInvocation() default false;
497
}
498
499
// Annotation to update cache entries
500
@Target({ElementType.TYPE, ElementType.METHOD})
501
@Retention(RetentionPolicy.RUNTIME)
502
@Inherited
503
@Documented
504
public @interface CachePut {
505
506
@AliasFor("cacheNames")
507
String[] value() default {};
508
509
@AliasFor("value")
510
String[] cacheNames() default {};
511
512
String key() default "";
513
514
String keyGenerator() default "";
515
516
String cacheManager() default "";
517
518
String cacheResolver() default "";
519
520
String condition() default "";
521
522
String unless() default "";
523
}
524
```
525
526
### Cache Configuration
527
528
```java { .api }
529
// Cache configuration
530
@Configuration
531
@EnableCaching
532
public class CacheConfig {
533
534
@Bean
535
public CacheManager cacheManager() {
536
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
537
cacheManager.setCacheNames(Arrays.asList(
538
"users", "products", "orders", "settings"
539
));
540
cacheManager.setAllowNullValues(false);
541
return cacheManager;
542
}
543
544
// Custom key generator
545
@Bean
546
public KeyGenerator customKeyGenerator() {
547
return (target, method, params) -> {
548
StringBuilder key = new StringBuilder();
549
key.append(target.getClass().getSimpleName());
550
key.append(".").append(method.getName());
551
for (Object param : params) {
552
key.append(".").append(param != null ? param.toString() : "null");
553
}
554
return key.toString();
555
};
556
}
557
558
// Redis cache manager (if using Redis)
559
@Bean
560
@ConditionalOnClass(RedisTemplate.class)
561
public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
562
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
563
.serializeKeysWith(RedisSerializationContext.SerializationPair
564
.fromSerializer(new StringRedisSerializer()))
565
.serializeValuesWith(RedisSerializationContext.SerializationPair
566
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
567
.entryTtl(Duration.ofHours(1));
568
569
return RedisCacheManager.builder(connectionFactory)
570
.cacheDefaults(config)
571
.build();
572
}
573
}
574
575
// Service with caching
576
@Service
577
public class UserService {
578
579
private final UserRepository userRepository;
580
581
public UserService(UserRepository userRepository) {
582
this.userRepository = userRepository;
583
}
584
585
@Cacheable(value = "users", key = "#id")
586
public User findById(Long id) {
587
System.out.println("Loading user from database: " + id);
588
return userRepository.findById(id).orElse(null);
589
}
590
591
@Cacheable(value = "users", key = "#username")
592
public User findByUsername(String username) {
593
System.out.println("Loading user by username: " + username);
594
return userRepository.findByUsername(username).orElse(null);
595
}
596
597
@CachePut(value = "users", key = "#result.id")
598
public User save(User user) {
599
return userRepository.save(user);
600
}
601
602
@CacheEvict(value = "users", key = "#id")
603
public void deleteById(Long id) {
604
userRepository.deleteById(id);
605
}
606
607
@CacheEvict(value = "users", allEntries = true)
608
public void clearAllUsers() {
609
// This will clear all entries from the "users" cache
610
}
611
612
// Conditional caching
613
@Cacheable(value = "users", condition = "#id > 0", unless = "#result == null")
614
public User findByIdConditional(Long id) {
615
return userRepository.findById(id).orElse(null);
616
}
617
618
// Custom key generator
619
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
620
public List<User> findByAgeRange(int minAge, int maxAge) {
621
return userRepository.findByAgeBetween(minAge, maxAge);
622
}
623
}
624
```
625
626
## WebSocket Support
627
628
### WebSocket Configuration
629
630
```java { .api }
631
// Enables WebSocket support
632
@Target(ElementType.TYPE)
633
@Retention(RetentionPolicy.RUNTIME)
634
@Documented
635
@Import(DelegatingWebSocketConfiguration.class)
636
public @interface EnableWebSocket {
637
}
638
639
// Configuration interface for WebSocket
640
public interface WebSocketConfigurer {
641
642
void registerWebSocketHandlers(WebSocketHandlerRegistry registry);
643
}
644
645
// WebSocket handler interface
646
public interface WebSocketHandler {
647
648
void afterConnectionEstablished(WebSocketSession session) throws Exception;
649
650
void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
651
652
void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
653
654
void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;
655
656
boolean supportsPartialMessages();
657
}
658
659
// WebSocket session interface
660
public interface WebSocketSession extends Closeable {
661
662
String getId();
663
664
URI getUri();
665
666
HttpHeaders getHandshakeHeaders();
667
668
Map<String, Object> getAttributes();
669
670
Principal getPrincipal();
671
672
InetSocketAddress getLocalAddress();
673
674
InetSocketAddress getRemoteAddress();
675
676
String getAcceptedProtocol();
677
678
void sendMessage(WebSocketMessage<?> message) throws IOException;
679
680
boolean isOpen();
681
682
void close() throws IOException;
683
684
void close(CloseStatus status) throws IOException;
685
}
686
687
// WebSocket configuration
688
@Configuration
689
@EnableWebSocket
690
public class WebSocketConfig implements WebSocketConfigurer {
691
692
@Override
693
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
694
registry.addHandler(new ChatWebSocketHandler(), "/chat")
695
.addInterceptors(new HttpSessionHandshakeInterceptor())
696
.setAllowedOrigins("*");
697
698
registry.addHandler(new EchoWebSocketHandler(), "/echo")
699
.withSockJS(); // Enable SockJS fallback
700
}
701
702
@Bean
703
public WebSocketHandler chatWebSocketHandler() {
704
return new ChatWebSocketHandler();
705
}
706
}
707
708
// WebSocket handler implementation
709
public class ChatWebSocketHandler extends TextWebSocketHandler {
710
711
private static final Logger logger = LoggerFactory.getLogger(ChatWebSocketHandler.class);
712
private final Set<WebSocketSession> sessions = new CopyOnWriteArraySet<>();
713
714
@Override
715
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
716
sessions.add(session);
717
logger.info("WebSocket connection established: {}", session.getId());
718
719
// Send welcome message
720
String welcomeMessage = "Welcome to chat! Session ID: " + session.getId();
721
session.sendMessage(new TextMessage(welcomeMessage));
722
}
723
724
@Override
725
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
726
logger.info("Received message from {}: {}", session.getId(), message.getPayload());
727
728
// Broadcast message to all connected sessions
729
String broadcastMessage = session.getId() + ": " + message.getPayload();
730
for (WebSocketSession webSocketSession : sessions) {
731
if (webSocketSession.isOpen()) {
732
webSocketSession.sendMessage(new TextMessage(broadcastMessage));
733
}
734
}
735
}
736
737
@Override
738
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
739
logger.error("Transport error for session {}: {}", session.getId(), exception.getMessage());
740
sessions.remove(session);
741
}
742
743
@Override
744
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
745
sessions.remove(session);
746
logger.info("WebSocket connection closed: {} with status {}", session.getId(), closeStatus);
747
}
748
}
749
```
750
751
### STOMP Messaging Support
752
753
```java { .api }
754
// Enables WebSocket message broker
755
@Target(ElementType.TYPE)
756
@Retention(RetentionPolicy.RUNTIME)
757
@Documented
758
@Import(DelegatingWebSocketMessageBrokerConfiguration.class)
759
public @interface EnableWebSocketMessageBroker {
760
}
761
762
// Configuration interface for WebSocket message broker
763
public interface WebSocketMessageBrokerConfigurer {
764
765
default void configureMessageBroker(MessageBrokerRegistry registry) {
766
}
767
768
default void registerStompEndpoints(StompEndpointRegistry registry) {
769
}
770
771
default void configureWebSocketTransport(WebSocketTransportRegistration registry) {
772
}
773
774
default void configureClientInboundChannel(ChannelRegistration registration) {
775
}
776
777
default void configureClientOutboundChannel(ChannelRegistration registration) {
778
}
779
}
780
781
// STOMP configuration
782
@Configuration
783
@EnableWebSocketMessageBroker
784
public class WebSocketMessageConfig implements WebSocketMessageBrokerConfigurer {
785
786
@Override
787
public void configureMessageBroker(MessageBrokerRegistry registry) {
788
// Enable simple in-memory broker
789
registry.enableSimpleBroker("/topic", "/queue");
790
791
// Set application destination prefix
792
registry.setApplicationDestinationPrefixes("/app");
793
794
// Configure user destination prefix
795
registry.setUserDestinationPrefix("/user");
796
}
797
798
@Override
799
public void registerStompEndpoints(StompEndpointRegistry registry) {
800
// Register STOMP endpoint
801
registry.addEndpoint("/ws")
802
.setAllowedOrigins("*")
803
.withSockJS();
804
}
805
806
@Override
807
public void configureClientInboundChannel(ChannelRegistration registration) {
808
registration.interceptors(new ChannelInterceptor() {
809
@Override
810
public Message<?> preSend(Message<?> message, MessageChannel channel) {
811
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
812
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
813
// Authentication logic here
814
}
815
return message;
816
}
817
});
818
}
819
}
820
821
// STOMP message controller
822
@Controller
823
public class WebSocketMessageController {
824
825
private final SimpMessagingTemplate messagingTemplate;
826
827
public WebSocketMessageController(SimpMessagingTemplate messagingTemplate) {
828
this.messagingTemplate = messagingTemplate;
829
}
830
831
@MessageMapping("/chat.send")
832
@SendTo("/topic/public")
833
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
834
return chatMessage;
835
}
836
837
@MessageMapping("/chat.addUser")
838
@SendTo("/topic/public")
839
public ChatMessage addUser(@Payload ChatMessage chatMessage,
840
SimpMessageHeaderAccessor headerAccessor) {
841
// Add username in web socket session
842
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
843
return chatMessage;
844
}
845
846
// Send message to specific user
847
@MessageMapping("/private")
848
public void sendToUser(@Payload PrivateMessage message, Principal principal) {
849
messagingTemplate.convertAndSendToUser(
850
message.getRecipient(),
851
"/queue/messages",
852
message
853
);
854
}
855
856
// Scheduled message broadcasting
857
@Scheduled(fixedRate = 30000)
858
public void sendPeriodicMessage() {
859
ServerMessage message = ServerMessage.builder()
860
.content("Server time: " + LocalDateTime.now())
861
.timestamp(LocalDateTime.now())
862
.build();
863
864
messagingTemplate.convertAndSend("/topic/server", message);
865
}
866
}
867
868
// WebSocket event listener
869
@Component
870
public class WebSocketEventListener {
871
872
private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class);
873
874
@Autowired
875
private SimpMessagingTemplate messagingTemplate;
876
877
@EventListener
878
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
879
logger.info("Received a new web socket connection");
880
}
881
882
@EventListener
883
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
884
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
885
886
String username = (String) headerAccessor.getSessionAttributes().get("username");
887
if (username != null) {
888
logger.info("User disconnected: {}", username);
889
890
ChatMessage chatMessage = ChatMessage.builder()
891
.type(MessageType.LEAVE)
892
.sender(username)
893
.build();
894
895
messagingTemplate.convertAndSend("/topic/public", chatMessage);
896
}
897
}
898
}
899
```
900
901
## Object/XML Mapping (OXM)
902
903
### Marshalling Support
904
905
```java { .api }
906
// Interface for marshalling objects to XML
907
public interface Marshaller {
908
909
boolean supports(Class<?> clazz);
910
911
void marshal(Object graph, Result result) throws IOException, XmlMappingException;
912
}
913
914
// Interface for unmarshalling XML to objects
915
public interface Unmarshaller {
916
917
boolean supports(Class<?> clazz);
918
919
Object unmarshal(Source source) throws IOException, XmlMappingException;
920
}
921
922
// JAXB marshaller implementation
923
public class Jaxb2Marshaller implements Marshaller, Unmarshaller, InitializingBean {
924
925
public void setContextPath(String contextPath);
926
public void setClassesToBeBound(Class<?>... classesToBeBound);
927
public void setPackagesToScan(String... packagesToScan);
928
public void setSchema(Resource schema);
929
public void setMarshallerProperties(Map<String, ?> properties);
930
public void setUnmarshallerProperties(Map<String, ?> properties);
931
public void setValidationEventHandler(ValidationEventHandler validationEventHandler);
932
933
@Override
934
public boolean supports(Class<?> clazz);
935
936
@Override
937
public void marshal(Object graph, Result result) throws XmlMappingException;
938
939
@Override
940
public Object unmarshal(Source source) throws XmlMappingException;
941
}
942
943
// OXM configuration
944
@Configuration
945
public class OxmConfig {
946
947
@Bean
948
public Jaxb2Marshaller marshaller() {
949
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
950
marshaller.setPackagesToScan("com.example.xml.model");
951
952
Map<String, Object> properties = new HashMap<>();
953
properties.put(Marshaller.JAXB_FORMATTED_OUTPUT, true);
954
properties.put(Marshaller.JAXB_ENCODING, "UTF-8");
955
marshaller.setMarshallerProperties(properties);
956
957
return marshaller;
958
}
959
960
@Bean
961
public MarshallingService marshallingService(Jaxb2Marshaller marshaller) {
962
return new MarshallingService(marshaller, marshaller);
963
}
964
}
965
966
// XML model with JAXB annotations
967
@XmlRootElement(name = "user")
968
@XmlAccessorType(XmlAccessType.FIELD)
969
public class UserXml {
970
971
@XmlElement
972
private Long id;
973
974
@XmlElement
975
private String username;
976
977
@XmlElement
978
private String email;
979
980
@XmlElement(name = "created_date")
981
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
982
private LocalDateTime createdDate;
983
984
@XmlElementWrapper(name = "orders")
985
@XmlElement(name = "order")
986
private List<OrderXml> orders;
987
988
// Constructors, getters, setters
989
}
990
991
// Marshalling service
992
@Service
993
public class MarshallingService {
994
995
private final Marshaller marshaller;
996
private final Unmarshaller unmarshaller;
997
998
public MarshallingService(Marshaller marshaller, Unmarshaller unmarshaller) {
999
this.marshaller = marshaller;
1000
this.unmarshaller = unmarshaller;
1001
}
1002
1003
public String marshalToString(Object object) {
1004
try (StringWriter writer = new StringWriter()) {
1005
marshaller.marshal(object, new StreamResult(writer));
1006
return writer.toString();
1007
} catch (Exception e) {
1008
throw new XmlProcessingException("Failed to marshal object", e);
1009
}
1010
}
1011
1012
public void marshalToFile(Object object, String filename) {
1013
try (FileOutputStream fos = new FileOutputStream(filename)) {
1014
marshaller.marshal(object, new StreamResult(fos));
1015
} catch (Exception e) {
1016
throw new XmlProcessingException("Failed to marshal to file", e);
1017
}
1018
}
1019
1020
@SuppressWarnings("unchecked")
1021
public <T> T unmarshalFromString(String xml, Class<T> clazz) {
1022
try (StringReader reader = new StringReader(xml)) {
1023
return (T) unmarshaller.unmarshal(new StreamSource(reader));
1024
} catch (Exception e) {
1025
throw new XmlProcessingException("Failed to unmarshal from string", e);
1026
}
1027
}
1028
1029
@SuppressWarnings("unchecked")
1030
public <T> T unmarshalFromFile(String filename, Class<T> clazz) {
1031
try (FileInputStream fis = new FileInputStream(filename)) {
1032
return (T) unmarshaller.unmarshal(new StreamSource(fis));
1033
} catch (Exception e) {
1034
throw new XmlProcessingException("Failed to unmarshal from file", e);
1035
}
1036
}
1037
}
1038
```
1039
1040
## Email Support
1041
1042
### JavaMail Integration
1043
1044
```java { .api }
1045
// Mail configuration
1046
@Configuration
1047
@ConditionalOnProperty(name = "spring.mail.host")
1048
public class MailConfig {
1049
1050
@Bean
1051
public JavaMailSender mailSender(
1052
@Value("${spring.mail.host}") String host,
1053
@Value("${spring.mail.port:587}") int port,
1054
@Value("${spring.mail.username}") String username,
1055
@Value("${spring.mail.password}") String password) {
1056
1057
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
1058
mailSender.setHost(host);
1059
mailSender.setPort(port);
1060
mailSender.setUsername(username);
1061
mailSender.setPassword(password);
1062
1063
Properties props = mailSender.getJavaMailProperties();
1064
props.put("mail.transport.protocol", "smtp");
1065
props.put("mail.smtp.auth", "true");
1066
props.put("mail.smtp.starttls.enable", "true");
1067
props.put("mail.debug", "false");
1068
1069
return mailSender;
1070
}
1071
1072
@Bean
1073
public TemplateEngine templateEngine() {
1074
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
1075
templateEngine.addTemplateResolver(emailTemplateResolver());
1076
return templateEngine;
1077
}
1078
1079
private ITemplateResolver emailTemplateResolver() {
1080
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
1081
templateResolver.setPrefix("/mail-templates/");
1082
templateResolver.setSuffix(".html");
1083
templateResolver.setTemplateMode(TemplateMode.HTML);
1084
templateResolver.setCharacterEncoding("UTF-8");
1085
templateResolver.setCacheable(false);
1086
return templateResolver;
1087
}
1088
}
1089
1090
// Email service
1091
@Service
1092
public class EmailService {
1093
1094
private final JavaMailSender mailSender;
1095
private final TemplateEngine templateEngine;
1096
1097
@Value("${app.mail.from}")
1098
private String fromEmail;
1099
1100
public EmailService(JavaMailSender mailSender, TemplateEngine templateEngine) {
1101
this.mailSender = mailSender;
1102
this.templateEngine = templateEngine;
1103
}
1104
1105
public void sendSimpleEmail(String to, String subject, String text) {
1106
SimpleMailMessage message = new SimpleMailMessage();
1107
message.setFrom(fromEmail);
1108
message.setTo(to);
1109
message.setSubject(subject);
1110
message.setText(text);
1111
1112
mailSender.send(message);
1113
}
1114
1115
public void sendHtmlEmail(String to, String subject, String templateName, Map<String, Object> variables) {
1116
try {
1117
MimeMessage mimeMessage = mailSender.createMimeMessage();
1118
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
1119
1120
helper.setFrom(fromEmail);
1121
helper.setTo(to);
1122
helper.setSubject(subject);
1123
1124
Context context = new Context();
1125
context.setVariables(variables);
1126
String htmlContent = templateEngine.process(templateName, context);
1127
1128
helper.setText(htmlContent, true);
1129
1130
mailSender.send(mimeMessage);
1131
} catch (MessagingException e) {
1132
throw new EmailException("Failed to send HTML email", e);
1133
}
1134
}
1135
1136
public void sendEmailWithAttachment(String to, String subject, String text, String attachmentPath) {
1137
try {
1138
MimeMessage mimeMessage = mailSender.createMimeMessage();
1139
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
1140
1141
helper.setFrom(fromEmail);
1142
helper.setTo(to);
1143
helper.setSubject(subject);
1144
helper.setText(text);
1145
1146
File attachment = new File(attachmentPath);
1147
helper.addAttachment(attachment.getName(), attachment);
1148
1149
mailSender.send(mimeMessage);
1150
} catch (MessagingException e) {
1151
throw new EmailException("Failed to send email with attachment", e);
1152
}
1153
}
1154
1155
@Async
1156
public CompletableFuture<Void> sendEmailAsync(String to, String subject, String templateName, Map<String, Object> variables) {
1157
sendHtmlEmail(to, subject, templateName, variables);
1158
return CompletableFuture.completedFuture(null);
1159
}
1160
}
1161
```
1162
1163
## Validation Support
1164
1165
### Bean Validation Integration
1166
1167
```java { .api }
1168
// Validation configuration
1169
@Configuration
1170
public class ValidationConfig {
1171
1172
@Bean
1173
public LocalValidatorFactoryBean validator() {
1174
return new LocalValidatorFactoryBean();
1175
}
1176
1177
@Bean
1178
public MethodValidationPostProcessor methodValidationPostProcessor(LocalValidatorFactoryBean validator) {
1179
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
1180
processor.setValidator(validator);
1181
return processor;
1182
}
1183
}
1184
1185
// Service with method-level validation
1186
@Service
1187
@Validated
1188
public class UserValidationService {
1189
1190
public User createUser(@Valid CreateUserRequest request) {
1191
// Method parameter validation
1192
return new User(request.getUsername(), request.getEmail());
1193
}
1194
1195
public List<User> getUsers(
1196
@Min(0) @Max(100) int page,
1197
@Min(1) @Max(50) int size) {
1198
// Parameter validation with constraints
1199
return findUsers(page, size);
1200
}
1201
1202
public void updateUser(
1203
@NotNull @Positive Long id,
1204
@Valid @NotNull UpdateUserRequest request) {
1205
// Multiple parameter validation
1206
updateUserById(id, request);
1207
}
1208
1209
@NotNull
1210
public User findUserById(@NotNull @Positive Long id) {
1211
// Return value validation
1212
return getUserById(id);
1213
}
1214
1215
private User getUserById(Long id) {
1216
// Implementation
1217
return new User();
1218
}
1219
1220
private List<User> findUsers(int page, int size) {
1221
// Implementation
1222
return new ArrayList<>();
1223
}
1224
1225
private void updateUserById(Long id, UpdateUserRequest request) {
1226
// Implementation
1227
}
1228
}
1229
1230
// Custom validator
1231
@Component
1232
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
1233
1234
private final UserRepository userRepository;
1235
1236
public UniqueUsernameValidator(UserRepository userRepository) {
1237
this.userRepository = userRepository;
1238
}
1239
1240
@Override
1241
public boolean isValid(String username, ConstraintValidatorContext context) {
1242
if (username == null) {
1243
return true; // Let @NotNull handle null values
1244
}
1245
1246
return !userRepository.existsByUsername(username);
1247
}
1248
}
1249
1250
// Custom validation annotation
1251
@Target({ElementType.FIELD, ElementType.PARAMETER})
1252
@Retention(RetentionPolicy.RUNTIME)
1253
@Constraint(validatedBy = UniqueUsernameValidator.class)
1254
@Documented
1255
public @interface UniqueUsername {
1256
1257
String message() default "Username already exists";
1258
1259
Class<?>[] groups() default {};
1260
1261
Class<? extends Payload>[] payload() default {};
1262
}
1263
1264
// DTO with validation annotations
1265
public class CreateUserRequest {
1266
1267
@NotBlank(message = "Username is required")
1268
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
1269
@UniqueUsername
1270
private String username;
1271
1272
@NotBlank(message = "Email is required")
1273
@Email(message = "Email should be valid")
1274
private String email;
1275
1276
@NotBlank(message = "First name is required")
1277
@Size(max = 50)
1278
private String firstName;
1279
1280
@NotBlank(message = "Last name is required")
1281
@Size(max = 50)
1282
private String lastName;
1283
1284
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$", message = "Invalid phone number")
1285
private String phoneNumber;
1286
1287
@Past(message = "Birth date must be in the past")
1288
private LocalDate birthDate;
1289
1290
// Constructors, getters, setters
1291
}
1292
```
1293
1294
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.