- SpringBoot多环境配置
- SpringMVC
- Spring
- MyBaits
- MyBatis Generator
- MyBatis PageHelper
- Druid
- Lombok
- JWT
- Spring Security
- JavaMail
- Thymeleaf
- HttpClient
- FileUpload
- Spring Scheduler
- Hibernate Validator
- Redis
- Spring Async
- Spring Cache
- Swagger
- Spring Test
- MockMvc
- HTTPS
- Spring DevTools
- Spring Actuator
- Logback+Slf4j多环境日志
- i18n
- Maven Multi-Module
- WebSocket(待整合)
- RabbitMQ(待整合)
- OAUTH(待整合)
- ElasticSearch
- 获取图片验证码
- 登录:解决重复登录问题
- 注册
- 分页查询用户信息
- 修改用户信息
- 一对一发送站内信
- 管理员广播
- 读取站内信(未读和已读)
- 一对多发送站内信
- 文件上传
- 文件下载
- 单独发送邮件
- 群发邮件
- Thymeleaf邮件模板
- 注解形式的权限校验
- 拦截器
- 增改删查
-
每个Mapper上都要加@Mapper
-
yaml文件 @Value获取xx.xx.xx不可行,必须使用@ConfigurationProperties,指定prefix,属性设置setter和getter
-
logback日志重复打印:自定义logger上加上
additivity="false"
-
SpringBoot 项目没有项目名
-
登录 Spring Security +JWT
-
已登录用户验证token
- 主要是在Filter中操作。 从requestHeader中取得token,检查token的合法性,检查这一步可以解析出username去查数据库; 也可以查询缓存,如果缓存中有该token,那么就没有问题,可以放行。
-
未登录用户进行登录
- 登录时要构造UsernamePasswordAuthenticationToken,用户名和密码来自于参数,然后调用AuthenticationManager的authenticate方法, 它会去调用UserDetailsService的loadFromUsername,参数是token的username,然后比对password,检查userDetails的一些状态。 如果一切正常,那么会返回Authentication。返回的Authentication的用户名和密码是正确的用户名和密码,并且还放入了之前查询出的Roles。 调用getAuthentication然后调用getPrinciple可以得到之前听过UserDetailsService查询出的UserDetails
-
在Controller中使用@PreAuthorize等注解需要在spring-web配置文件中扫描security包下的类
-
-
引用application.properties中的属性的方式:@ConfigurationProperties(prefix = "spring.mail") + @Component + setter + getter
-
引用其他自定义配置文件中的属性的方式:
- @Component
- @ConfigurationProperties(prefix = "auth")
- @PropertySource("classpath:auth.properties")
- setter & getter 不喜欢使用@Value,每个属性都要写一遍名字
-
所以写静态资源位置的时候,不要带上映射的目录名(如/static/,/public/ ,/resources/,/META-INF/resources/)!
-
所有的html都放在templates下面,只有index.html能直接访问,其他均不可,必须通过Controller的转发
-
静态资源都放在static下面,访问时Spring Security会检查URL,根据URL进行拦截。如果通过,那么会交给ViewResolver,添加前面的/static(无需配置,SpringBoot自动完成),得到最终的真实路径
-
Mybatis打印SQL http://www.cnblogs.com/lixuwu/p/6323739.html
-
访问Druid监控: http://localhost:8080/druid
-
spring-devtools热部署:
- 前提:把Idea的自动编译打开
- 修改类-->保存:应用会重启
- 修改配置文件-->保存:应用会重启
- 修改页面-->保存:应用不会重启,但会重新加载,页面会刷新(原理是将spring.thymeleaf.cache设为false)
-
@Bean
- 可以指定name,如果不指定那么使用方法名作为name
- 有一个initMethod和destroyMethod两个属性,值为该Bean的方法名 ;当然也可以直接在方法上使用注解@PostConstruct&@PreDestroy
-
@EnableAsync @EnableTransactionManagement @EnableCaching @EnableScheduling @EnableWebSecurity @EnableSwagger2
- @EnableTransactionManagement | @EnableWebSecurity 可以不加,自动配置
-
@Conditional
-
注册Servlet、Filter、Listener
-
ApplicationEvent Spring 提供的Observer模型骨架
-
SpringMVC 拦截器 实现HandlerInterceptor接口或继承HandlerInterceptorAdaptor类,然后将其注册为一个Bean,并在registry(继承了WebMvcConfigurerAdapter的配置类)中调用addInterceptor 如果想要自己完全控制WebMVC,就需要在@Configuration注解的配置类上增加@EnableWebMvc;否则会使用自动配置。一般不使用@EnableWebMvc
-
TODO:WebSocket ,RabbitMQ, Spring Batch,Docker
-
@SpringBootApplication是一个组合注解,组合了@EnableAutoConfiguration,根据类路径中的jar包依赖为当前项目进行自动配置
-
Jaskson 反序列化 mapper.readValue(content, new TypeReference<PageInfo>() {});
-
SSL配置
- 生成一个证书 .keystore
- 把该证书复制到项目根目录(父项目,不是子模块),然后在application.properties中添加如下配置
server: tomcat: uri-encoding: UTF-8 max-threads: 1000 min-spare-threads: 30 port: 8443 ssl: key-store: .keystore key-store-password: 130119 key-store-type: JKS key-alias: tomcat
- 设置HTTP转向HTTPS ->HTTPSConfig
-
如果想直接访问html,那么必须在WebConfig里设置registry 所有的html都放在/templates下
默认 9300 是 Java 客户端的端口。9200 是支持 Restful HTTP 的接口。
在实体类上加入
(indexName="article_index", //索引库的名称,个人建议以项目的名称命名(相当于一个Database) indexName 配置必须是全部小写,不然会出异常。 type="article", //类型,个人建议以实体的名称命名(相当于一张表) shards=5, //默认分区数 replicas=1, //每个分区默认的备份数 indexStoreType="fs", //索引文件存储类型 refreshInterval="-1" //刷新间隔 ) 在需要建立索引的类上加上@Document注解,即表明这个实体需要进行索引。默认情况下这个实体中所有的属性都会被建立索引、并且分词。 在主键上加入@Id 我们通过@Field注解来进行详细的指定。
(format=DateFormat.date_time, //default DateFormat.none; index=FieldIndex.no, //默认情况下分词 store=true, //默认情况下不存储原文 type=FieldType.Object) //自动检测属性的类型 private Date postTime;
关键字 例子
Elasticsearch查询语句
And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "?","analyze_wildcard" : true}}}}}
In findByNameIn(Collectionnames) {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn(Collectionnames) {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}
public interface BookRepository extends ElasticsearchRepository<Book, String> { @Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}") Page findByName(String name,Pageable pageable); }
Iterable search(QueryBuilder query); Page search(QueryBuilder query, Pageable pageable); Page search(SearchQuery searchQuery); Page searchSimilar(T entity, String[] fields, Pageable pageable);
注意:Mybatis PageHelper 的起始页码是1,而Spring Data分页的起始页码是0 它们的Page也不一样,统一使用时建议前端只看 total pages pageNum pageSize size
http://lrwinx.github.io/2017/07/09/%E5%86%8D%E8%B0%88websocket-%E8%AE%BA%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/ http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html http://blog.csdn.net/daniel7443/article/details/54377326 http://www.cnblogs.com/winkey4986/p/5622758.html
- 实现服务器端的消息推送,实时页面刷新
- 即时通讯,单聊&群聊
- @EnableWebSocketMessageBroker注解表示开启使用STOMP协议来传输基于代理的消息,Broker就是代理的意思。
- registerStompEndpoints方法表示注册STOMP协议的节点,并指定映射的URL。
- stompEndpointRegistry.addEndpoint("/endpointSang").withSockJS();这一行代码用来注册STOMP协议节点,同时指定使用SockJS协议。
- configureMessageBroker方法用来配置消息代理,由于我们是实现推送功能,这里的消息代理是/topic
- registry.enableSimpleBroker("/topic","/user");
- registry.setApplicationDestinationPrefixes("/app");
- registry.setUserDestinationPrefix("/user/"); 第一个是作为@SendTo的前缀 第二三个是作为客户端发送信息send的前缀,后接@MessageMapping
JWT 见WebSocketConfig
客户端有两种消息发送方式:
- 经过了服务器编写的MessageHandler(@MessageMapping),适用于需要服务器对消息进行处理的,客户端将消息发送给服务器,服务器将消息处理后 广播给所有用户。 示例:客户端订阅了/greetings,并会向/hello发送数据 实现:
- 服务器:@MessageMapping("/hello") @SendTo("/topic/greetings")
- 客户端:stompClient.send("/app/hello", {}, JSON.stringify(...)); stompClient.subscribe('/topic/greetings', function (response) { showResponse(JSON.parse(response.body).body); });
- 不经过服务器,客户端发送的消息直接广播给所有用户,此时send和subscribe的路径是一样的 示例:客户端订阅了/greetings,并会向/greetings发送数据 实现:
- 服务器:什么都不用做
- 客户端:stompClient.send("/topic/greetings", {}, JSON.stringify(...)); stompClient.subscribe('/topic/greetings', function (response) { showResponse(JSON.parse(response.body).body); });
共同点:都需要登录
-
@MessageMapping 客户端发送路径 @MessageMapping注解和我们之前使用的@RequestMapping类似。客户端向该(ApplicationPrefix+@MessageMapping)路径发送消息。 @MessageMapping("/hello") stompClient.send("/app/hello", {}, JSON.stringify({'body': name})); 客户端会先将信息发送到代理(Broker,位于服务器),然后Broker会再将处理后的信息发送给客户端
-
@SendTo 客户端接收路径 @SendTo注解表示当服务器有消息需要推送的时候,会对订阅了@SendTo中路径的浏览器发送消息。 @SendTo("/topic/xxx")中必须要以WebSocketConfig中messageBroker中设置的任一Prefix("/topic")为前缀
-
@SendToUser
发送给单一客户端的标志 注意是谁请求的发送给谁 -
convertAndSend template.convertAndSend("/topic/hello",greeting) //广播
-
convertAndSendToUser convertAndSendToUser(userId, "/message",userMessage) //一对一发送,发送特定的客户端
-
@MessageExceptionHandler
HTTP方法 路径 描述 鉴权 GET /autoconfig 查看自动配置的使用情况 true GET /configprops 查看配置属性,包括默认配置 true GET /beans 查看bean及其关系列表 true GET /dump 打印线程栈 true GET /env 查看所有环境变量 true GET /env/{name} 查看具体变量值 true GET /health 查看应用健康指标 false GET /info 查看应用信息 false GET /mappings 查看所有url映射 true GET /metrics 查看应用基本指标 true GET /metrics/{name} 查看具体指标 true POST /shutdown 关闭应用 true GET /trace 查看基本追踪信息 true
需要在Docker里安装MySQL,Redis,Nginx,ElasticSearch,JDK
docker pull java:8 docker pull mysql docker pull redis docker pull es