常用maven 包
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> webjars-locator</ artifactId>
< version> 0.34</ version>
</ dependency>
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> sockjs-client</ artifactId>
< version> 1.1.2</ version>
</ dependency>
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> stomp-websocket</ artifactId>
< version> 2.3.3</ version>
</ dependency>
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> js-cookie</ artifactId>
< version> 2.1.4</ version>
</ dependency>
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> jquery</ artifactId>
< version> 3.2.1</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-data-redis</ artifactId>
</ dependency>
< dependency>
< groupId> org.apache.commons</ groupId>
< artifactId> commons-pool2</ artifactId>
< version> 2.7.0</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< scope> test</ scope>
< exclusions>
< exclusion>
< groupId> org.junit.vintage</ groupId>
< artifactId> junit-vintage-engine</ artifactId>
</ exclusion>
</ exclusions>
</ dependency>
< dependency>
< groupId> io.netty</ groupId>
< artifactId> netty-all</ artifactId>
< version> ${netty-all.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-websocket</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-amqp</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-reactor-netty</ artifactId>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< scope> runtime</ scope>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-jdbc</ artifactId>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> druid-spring-boot-starter</ artifactId>
< version> ${druid.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-data-jpa</ artifactId>
</ dependency>
< dependency>
< groupId> com.h2database</ groupId>
< artifactId> h2</ artifactId>
< scope> runtime</ scope>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-web</ artifactId>
< version> 5.2.3.RELEASE</ version>
< scope> compile</ scope>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-thymeleaf</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-devtools</ artifactId>
< optional> true</ optional>
</ dependency>
< dependency>
< groupId> org.projectlombok</ groupId>
< artifactId> lombok</ artifactId>
< version> ${lombok.version}</ version>
< scope> provided</ scope>
</ dependency>
< dependency>
< groupId> io.jsonwebtoken</ groupId>
< artifactId> jjwt</ artifactId>
< version> 0.9.1</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-security</ artifactId>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> fastjson</ artifactId>
< version> 1.2.60</ version>
</ dependency>
< dependency>
< groupId> cn.hutool</ groupId>
< artifactId> hutool-core</ artifactId>
< version> ${hutool.version}</ version>
</ dependency>
< dependency>
< groupId> cn.hutool</ groupId>
< artifactId> hutool-http</ artifactId>
< version> ${hutool.version}</ version>
</ dependency>
< dependency>
< groupId> cn.hutool</ groupId>
< artifactId> hutool-crypto</ artifactId>
< version> ${hutool.version}</ version>
</ dependency>
< dependency>
< groupId> cn.hutool</ groupId>
< artifactId> hutool-extra</ artifactId>
< version> ${hutool.version}</ version>
</ dependency>
< dependency>
< groupId> cn.hutool</ groupId>
< artifactId> hutool-cron</ artifactId>
< version> ${hutool.version}</ version>
</ dependency>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-context</ artifactId>
< version> 5.2.3.RELEASE</ version>
< scope> compile</ scope>
</ dependency>
< dependency>
< groupId> io.springfox</ groupId>
< artifactId> springfox-swagger2</ artifactId>
< version> ${swagger.version}</ version>
</ dependency>
< dependency>
< groupId> io.springfox</ groupId>
< artifactId> springfox-swagger-ui</ artifactId>
< version> ${swagger.version}</ version>
</ dependency>
yml
server :
port : 3000
undertow :
io-threads : 2
worker-threads : 36
buffer-size : 1024
directBuffers : true
servlet :
session :
timeout : 86400s
logging :
file : ./logs/log.log
spring :
datasource :
type : com.alibaba.druid.pool.DruidDataSource
url : jdbc: mysql: //localhost: 3306/theatre? characterEncoding=utf8&useSSL =false&serverTimezone =Asia/Shanghai
driver-class-name : com.mysql.cj.jdbc.Driver
username : root
password : root
druid :
validation-query : SELECT 1
initial-size : 1
min-idle : 1
max-active : 20
filters : stat
max-wait : 60000
time-between-eviction-runs-millis : 60000
min-evictable-idle-time-millis : 3000000
test-while-idle : true
test-on-borrow : false
test-on-return : false
pool-prepared-statements : true
max-open-prepared-statements : 20
redis :
host : 127.0.0.1
password :
port : 6379
timeout : 12000
database : 0
lettuce :
pool :
max-active : 300
max-idle : 100
min-idle : 8
max-wait : -1
thymeleaf :
mode : HTML
prefix : classpath: /templates/
suffix : .html
encoding : UTF- 8
servlet :
content-type : text/html
cache : false
enabled : true
messages :
basename : i18n/messages
encoding : UTF- 8
jpa :
hibernate :
show-sql : true
ddl-auto : update
swagger :
base
mvc 配置
package com. devin. config;
import com. devin. web. interceptor. LocaleInterceptor;
import com. devin. web. interceptor. LoginInterceptor;
import org. apache. catalina. filters. RequestFilter;
import org. springframework. beans. factory. annotation. Autowired;
import org. springframework. boot. SpringBootConfiguration;
import org. springframework. boot. web. servlet. FilterRegistrationBean;
import org. springframework. context. annotation. Bean;
import org. springframework. web. servlet. LocaleResolver;
import org. springframework. web. servlet. config. annotation. InterceptorRegistry;
import org. springframework. web. servlet. config. annotation. ResourceHandlerRegistry;
import org. springframework. web. servlet. config. annotation. WebMvcConfigurer;
import org. springframework. web. servlet. i18n. LocaleChangeInterceptor;
import org. springframework. web. servlet. i18n. SessionLocaleResolver;
import org. springframework. web. servlet. resource. PathResourceResolver;
import org. springframework. web. servlet. resource. WebJarsResourceResolver;
import java. util. Locale;
@SpringBootConfiguration
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private LocaleInterceptor localeInterceptor;
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors ( InterceptorRegistry registry) {
registry. addInterceptor ( loginInterceptor)
. addPathPatterns ( "/user.*" )
. addPathPatterns ( "/user/**" )
. excludePathPatterns ( "/user/login" )
. excludePathPatterns ( "/user/getLogin" )
. excludePathPatterns ( "/static/**" ) ;
registry. addInterceptor ( localeInterceptor)
. addPathPatterns ( "/user.*" )
. addPathPatterns ( "/user/**" )
. addPathPatterns ( "/install" ) ;
registry. addInterceptor ( localeChangeInterceptor ( ) )
. addPathPatterns ( "/install" ) ;
}
@Override
public void addResourceHandlers ( ResourceHandlerRegistry registry) {
registry. addResourceHandler ( "/static/**" )
. addResourceLocations ( "classpath:/static/" ) ;
registry. addResourceHandler ( "/**" )
. addResourceLocations ( "classpath:/templates/**" ) ;
registry. addResourceHandler ( "swagger-ui.html" )
. addResourceLocations ( "classpath:/META-INF/resources/" ) ;
registry. addResourceHandler ( "/webjars/**" )
. addResourceLocations ( "/webjars/" ) . resourceChain ( false )
. addResolver ( new WebJarsResourceResolver ( ) )
. addResolver ( new PathResourceResolver ( ) ) ; ;
}
@Bean
public LocaleResolver localeResolver ( ) {
final SessionLocaleResolver slr = new SessionLocaleResolver ( ) ;
slr. setDefaultLocale ( Locale. CHINA) ;
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor ( ) {
final LocaleChangeInterceptor lci = new LocaleChangeInterceptor ( ) ;
lci. setParamName ( "lang" ) ;
return lci;
}
}
swagger
package com. devin. config;
import org. springframework. boot. SpringBootConfiguration;
import org. springframework. context. annotation. Bean;
import springfox. documentation. builders. ApiInfoBuilder;
import springfox. documentation. builders. PathSelectors;
import springfox. documentation. builders. RequestHandlerSelectors;
import springfox. documentation. service. ApiInfo;
import springfox. documentation. service. Contact;
import springfox. documentation. spi. DocumentationType;
import springfox. documentation. spring. web. plugins. Docket;
import springfox. documentation. swagger2. annotations. EnableSwagger2;
@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket apiConfig ( ) {
return new Docket ( DocumentationType. SWAGGER_2)
. apiInfo ( apiInfo ( ) )
. select ( )
. apis ( RequestHandlerSelectors. basePackage ( "com.devin.web.controller" ) )
. paths ( PathSelectors. any ( ) )
. build ( ) ;
}
public ApiInfo apiInfo ( ) {
return new ApiInfoBuilder ( )
. contact ( new Contact ( "devin" , "simple-le.top" , "[email protected] " ) )
. title ( "剧场接口文档" )
. version ( "v1.0.0" )
. build ( ) ;
}
}
bean 配置
package com. devin. config;
import com. devin. util. JwtUtil;
import com. fasterxml. jackson. annotation. JsonAutoDetect;
import com. fasterxml. jackson. annotation. JsonTypeInfo;
import com. fasterxml. jackson. annotation. PropertyAccessor;
import com. fasterxml. jackson. databind. ObjectMapper;
import com. fasterxml. jackson. databind. jsontype. impl. LaissezFaireSubTypeValidator;
import org. springframework. context. annotation. Bean;
import org. springframework. context. annotation. Lazy;
import org. springframework. data. redis. connection. lettuce. LettuceConnectionFactory;
import org. springframework. data. redis. core. RedisTemplate;
import org. springframework. data. redis. serializer. Jackson2JsonRedisSerializer;
import org. springframework. data. redis. serializer. RedisSerializer;
import org. springframework. data. redis. serializer. StringRedisSerializer;
import org. springframework. stereotype. Component;
@Component
public class BeanConfig {
@Bean
@Lazy
public JwtUtil jjwtUtil ( ) {
return JwtUtil. getInstance ( ) ;
}
@Bean
public RedisTemplate< String, Object> redisTemplate ( LettuceConnectionFactory lettuceConnectionFactory) {
Jackson2JsonRedisSerializer< Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer < > (
Object. class ) ;
ObjectMapper om = new ObjectMapper ( ) ;
om. setVisibility ( PropertyAccessor. ALL, JsonAutoDetect. Visibility. ANY) ;
om. activateDefaultTyping ( LaissezFaireSubTypeValidator. instance, ObjectMapper. DefaultTyping. NON_FINAL, JsonTypeInfo. As. PROPERTY) ;
jackson2JsonRedisSerializer. setObjectMapper ( om) ;
RedisTemplate< String, Object> redisTemplate = new RedisTemplate < > ( ) ;
redisTemplate. setConnectionFactory ( lettuceConnectionFactory) ;
RedisSerializer< ? > stringSerializer = new StringRedisSerializer ( ) ;
redisTemplate. setKeySerializer ( stringSerializer) ;
redisTemplate. setValueSerializer ( jackson2JsonRedisSerializer) ;
redisTemplate. setHashKeySerializer ( stringSerializer) ;
redisTemplate. setHashValueSerializer ( jackson2JsonRedisSerializer) ;
redisTemplate. afterPropertiesSet ( ) ;
return redisTemplate;
}
}
websocket
package com. devin. config;
import org. springframework. context. annotation. Configuration;
import org. springframework. messaging. simp. config. MessageBrokerRegistry;
import org. springframework. web. socket. config. annotation. AbstractWebSocketMessageBrokerConfigurer;
import org. springframework. web. socket. config. annotation. EnableWebSocketMessageBroker;
import org. springframework. web. socket. config. annotation. StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints ( StompEndpointRegistry registry) {
registry. addEndpoint ( "/ws" ) . withSockJS ( ) ;
}
@Override
public void configureMessageBroker ( MessageBrokerRegistry registry) {
registry. setApplicationDestinationPrefixes ( "/app" ) ;
registry. enableSimpleBroker ( "/channel" ) ;
}
}
用户实体类
package com. devin. model. domain;
import lombok. Data;
import javax. persistence. *;
import java. util. Date;
@Data
@Entity
@Table ( name = "theatre_movie" )
public class Movie {
private static final long serialVersionUID = - 5144055068797033748 L;
@Id
@GeneratedValue ( strategy = GenerationType. IDENTITY)
private long id;
private String kind;
private String title;
private Date createDate;
private String url;
private String faceUrl;
private String createName;
}
DTO
public class ChatMessage {
public enum MessageType {
CHAT, JOIN, LEAVE
}
private MessageType messageType;
private String content;
private String sender;
public MessageType getType ( ) {
return messageType;
}
public void setType ( MessageType messageType) {
this . messageType = messageType;
}
public String getContent ( ) {
return content;
}
public void setContent ( String content) {
this . content = content;
}
public String getSender ( ) {
return sender;
}
public void setSender ( String sender) {
this . sender = sender;
}
}
enum
package com. devin. model. enums;
public enum PostTypeEnum {
POST_TYPE_POST ( "post" ) ,
POST_TYPE_PAGE ( "page" ) ;
private String desc;
PostTypeEnum ( String desc) {
this . desc = desc;
}
public String getDesc ( ) {
return desc;
}
}
base repository
package com. devin. repository. base;
import org. springframework. data. domain. Sort;
import org. springframework. data. jpa. repository. JpaRepository;
import org. springframework. data. jpa. repository. JpaSpecificationExecutor;
import org. springframework. data. repository. NoRepositoryBean;
import org. springframework. lang. NonNull;
import java. util. List;
@NoRepositoryBean
public interface BaseRepository < DOMAIN, ID> extends JpaRepository < DOMAIN, ID> {
@NonNull
List< DOMAIN> findAllByIdIn ( @NonNull Iterable< ID> ids, @NonNull Sort sort) ;
long deleteByIdIn ( @NonNull Iterable< ID> ids) ;
}
impl repository
需要在启动类加 否则实体管理器无法启动 @EnableJpaAuditing @EnableJpaRepositories(basePackages = "com.devin.repository", repositoryBaseClass = BaseRepositoryImpl.class)
package com. devin. repository. base;
import lombok. extern. slf4j. Slf4j;
import org. springframework. data. domain. Sort;
import org. springframework. data. jpa. domain. Specification;
import org. springframework. data. jpa. repository. support. JpaEntityInformation;
import org. springframework. data. jpa. repository. support. SimpleJpaRepository;
import org. springframework. lang. Nullable;
import org. springframework. util. Assert;
import javax. persistence. EntityManager;
import javax. persistence. TypedQuery;
import javax. persistence. criteria. *;
import java. util. ArrayList;
import java. util. Collections;
import java. util. List;
@Slf4j
public class BaseRepositoryImpl < DOMAIN, ID> extends SimpleJpaRepository < DOMAIN, ID> implements BaseRepository < DOMAIN, ID> {
private final JpaEntityInformation< DOMAIN, ID> entityInformation;
private final EntityManager entityManager;
public BaseRepositoryImpl ( JpaEntityInformation< DOMAIN, ID> entityInformation, EntityManager entityManager) {
super ( entityInformation, entityManager) ;
this . entityInformation = entityInformation;
this . entityManager = entityManager;
}
@Override
public List< DOMAIN> findAllByIdIn ( Iterable< ID> ids, Sort sort) {
Assert. notNull ( ids, "The given Iterable of Id's must not be null!" ) ;
log. debug ( "Customized findAllById method was invoked" ) ;
if ( ! ids. iterator ( ) . hasNext ( ) ) {
return Collections. emptyList ( ) ;
}
if ( ! this . entityInformation. hasCompositeId ( ) ) {
ByIdsSpecification< DOMAIN> specification = new ByIdsSpecification < > ( this . entityInformation) ;
TypedQuery< DOMAIN> query = super . getQuery ( specification, sort) ;
return query. setParameter ( specification. parameter, ids) . getResultList ( ) ;
} else {
List< DOMAIN> results = new ArrayList < > ( ) ;
ids. forEach ( id - > super . findById ( id) . ifPresent ( results: : add) ) ;
return results;
}
}
@Override
public long deleteByIdIn ( Iterable< ID> ids) {
log. debug ( "Customized deleteByIdIn method was invoked" ) ;
List< DOMAIN> domains = findAllById ( ids) ;
deleteInBatch ( domains) ;
return domains. size ( ) ;
}
private static final class ByIdsSpecification < T> implements Specification < T> {
private static final long serialVersionUID = 1 L;
private final JpaEntityInformation< T, ? > entityInformation;
@Nullable
ParameterExpression< Iterable> parameter;
ByIdsSpecification ( JpaEntityInformation< T, ? > entityInformation) {
this . entityInformation = entityInformation;
}
@Override
public Predicate toPredicate ( Root< T> root, CriteriaQuery< ? > query, CriteriaBuilder cb) {
Path< ? > path = root. get ( this . entityInformation. getIdAttribute ( ) ) ;
this . parameter = cb. parameter ( Iterable. class ) ;
return path. in ( this . parameter) ;
}
}
}
repostiory
package com. devin. repository;
import com. devin. model. domain. User;
import com. devin. repository. base. BaseRepository;
import org. springframework. data. jpa. repository. JpaRepository;
public interface UserRepository extends BaseRepository < User, Long> {
User findByUserNameAndUserPass ( String userName, String userPass) ;
User findByUserEmailAndUserPass ( String userEmail, String userPass) ;
User findByUserIdAndUserPass ( Long userId, String userPass) ;
}
登录拦截器
package com. devin. web. interceptor;
import lombok. extern. slf4j. Slf4j;
import org. springframework. stereotype. Component;
import org. springframework. web. servlet. HandlerInterceptor;
import org. springframework. web. servlet. ModelAndView;
import javax. servlet. http. HttpServletRequest;
import javax. servlet. http. HttpServletResponse;
import static com. devin. model. dto. TheatreConst. USER_SESSION_KEY;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle ( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final Object obj = request. getSession ( ) . getAttribute ( USER_SESSION_KEY) ;
log. info ( "进入登入拦截器" ) ;
if ( null != obj) {
log. info ( "拦截器返回true" ) ;
return true ;
}
log. info ( "拦截器返回false" ) ;
response. sendRedirect ( "/user/login" ) ;
return false ;
}
@Override
public void postHandle ( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion ( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
AOP
package com. devin. aop;
import com. devin. model. enums. DateStyleEnum;
import com. devin. util. DateUtil;
import lombok. extern. slf4j. Slf4j;
import org. aspectj. lang. JoinPoint;
import org. aspectj. lang. annotation. *;
import org. springframework. stereotype. Component;
import org. springframework. web. context. request. RequestContextHolder;
import org. springframework. web. context. request. ServletRequestAttributes;
import javax. servlet. http. HttpServletRequest;
import java. util. Arrays;
import java. util. Date;
@Aspect
@Component
@Slf4j
public class ReControllerLogAdvice {
private Long startTime;
private Long endTime;
public ReControllerLogAdvice ( ) { }
@Pointcut ( "execution(public * com.devin.web.*.*.*(..))" )
public void webLogPointcut ( ) { }
@Before ( "webLogPointcut()" )
public void doBefore ( JoinPoint joinPoint) {
ServletRequestAttributes attributes = ( ServletRequestAttributes) RequestContextHolder. getRequestAttributes ( ) ;
assert attributes != null;
HttpServletRequest request = attributes. getRequest ( ) ;
startTime = System. currentTimeMillis ( ) ;
log. info ( "请求开始时间:{}" , DateUtil. formatDate ( new Date ( ) , DateStyleEnum. yyyy_MM_dd_HH_mm_ss) ) ;
log. info ( "请求Url : {}" , request. getRequestURL ( ) . toString ( ) ) ;
log. info ( "请求方式 : {}" , request. getMethod ( ) ) ;
log. info ( "请求ip : {}" , request. getRemoteAddr ( ) ) ;
log. info ( "请求方法 : {}" , joinPoint. getSignature ( ) . getDeclaringTypeName ( ) + "." + joinPoint. getSignature ( ) . getName ( ) ) ;
log. info ( "请求参数 : {}" , Arrays. toString ( joinPoint. getArgs ( ) ) ) ;
}
@AfterReturning ( returning = "ret" , pointcut = "webLogPointcut()" )
public void doAfterReturning ( Object ret) {
endTime = System. currentTimeMillis ( ) ;
log. info ( "请求结束时间:{}" , DateUtil. formatDate ( new Date ( ) , DateStyleEnum. yyyy_MM_dd_HH_mm_ss) ) ;
log. info ( "请求耗时:{}" , ( endTime - startTime) ) ;
log. info ( "请求返回 : {}" , ret) ;
}
@AfterThrowing ( value = "webLogPointcut()" , throwing = "throwable" )
public void doAfterThrowing ( Throwable throwable) {
log. error ( "发生异常时间:{}" , DateUtil. formatDate ( new Date ( ) , DateStyleEnum. yyyy_MM_dd_HH_mm_ss) ) ;
log. error ( "抛出异常:{}" , throwable. getMessage ( ) ) ;
}
}
html
<!DOCTYPE html>
< html xmlns: th= " http://www.thymeleaf.org" >
< html>
< head>
< meta name = " viewport" content = " width=device-width, initial-scale=1.0, minimum-scale=1.0" >
< title> Spring Boot WebSocket Chat Application | CalliCoder</ title>
< link rel = " stylesheet" href = " /css/main.css" />
</ head>
< body>
< noscript>
< h2> Sorry! Your browser doesn't support Javascript</ h2>
</ noscript>
< div id = " username-page" >
< div class = " username-page-container" >
< h1 class = " title" > Type your username</ h1>
< form id = " usernameForm" name = " usernameForm" >
< div class = " form-group" >
< input type = " text" id = " name" placeholder = " Username" autocomplete = " off" class = " form-control" />
</ div>
< div class = " form-group" >
< button type = " submit" class = " accent username-submit" > Start Chatting</ button>
</ div>
</ form>
</ div>
</ div>
< div id = " chat-page" class = " hidden" >
< div class = " chat-container" >
< div class = " chat-header" >
< h2> Spring WebSocket Chat Demo</ h2>
</ div>
< div class = " connecting" >
Connecting...
</ div>
< ul id = " messageArea" >
</ ul>
< form id = " messageForm" name = " messageForm" nameForm = " messageForm" >
< div class = " form-group" >
< div class = " input-group clearfix" >
< input type = " text" id = " message" placeholder = " Type a message..." autocomplete = " off" class = " form-control" />
< button type = " submit" class = " primary" > Send</ button>
</ div>
</ div>
</ form>
</ div>
</ div> static
< script src = " https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js" > </ script>
< script src = " https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js" > </ script>
< script src = " /js/main.js" > </ script>
< script src = " webjars/layui/src/layui.js" > </ script>
< script src = " /webjars/jquery/jquery.min.js" > </ script>
< script src = " /webjars/sockjs-client/sockjs.min.js" > </ script>
< script src = " /webjars/stomp-websocket/stomp.min.js" > </ script>
< script src = " /webjars/js-cookie/js.cookie.js" > </ script>
< script src = " index.js" type = " text/javascript" > </ script>
< script src = " js/main.js" type = " text/javascript" > </ script>
</ body>
</ html>