修改博文属性

This commit is contained in:
fanxb 2018-12-25 18:40:48 +08:00
parent 24499f7500
commit 23a89e4b6d
21 changed files with 704 additions and 508 deletions

View File

@ -1,6 +1,12 @@
[id]:2018-08-13
[type]:javaee
[tag]:java,spring,springboot
---
id="2018-08-13-10-38"
title="springboot搭建"
headWord="前面的博客有说到spring boot搭建见另一篇博文其实那篇博文还没写现在来填个坑。我们使用spring initializr来构建idea和eclipse都支持这种方式构建过程类似这里以idea为例详细记录构建过程。"
tags=["java", "spring","springboot","idea"]
category="java"
serie="spring boot学习"
---
  前面的博客有说到 spring boot 搭建见另一篇博文,其实那篇博文还没写,现在来填个坑。我们使用 spring initializr 来构建idea 和 eclipse 都支持这种方式,构建过程类似,这里以 idea 为例,详细记录构建过程。
### 1.选择 spring initializr

View File

@ -1,8 +1,13 @@
[id]:2018-08-20
[type]:javaee
[tag]:java,spring,springsecurity,scurity
---
id="2018-08-20-10-38"
title="springboot+security整合1"
headWord="javaee中的非常重要的一个安全认证框架但是略微重量级但是既然领导交代要学那就学吧。。。"
tags=["java", "spring","springboot","spring-security","security"]
category="java"
serie="spring boot学习"
---
**说明springboot版本2.0.3**
**说明 springboot 版本 2.0.3<br/>项目地址:[点击跳转](https://github.com/FleyX/demo-project/tree/master/springboot_spirngsecurity_demo)**
## 一、 介绍
@ -11,6 +16,7 @@
## 二、 环境搭建
&emsp;&emsp;建立 springboot2 项目,加入 security 依赖,mybatis 依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
@ -27,7 +33,9 @@
<scope>runtime</scope>
</dependency>
```
数据库为传统的用户--角色--权限,权限表记录了 url 和 methodspringboot 配置文件如下:
```yml
mybatis:
type-aliases-package: com.example.demo.entity
@ -44,7 +52,9 @@ spring:
charset: utf-8
enabled: true
```
springboot 启动类中加入如下代码,设置路由匹配规则。
```java
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
@ -58,6 +68,7 @@ protected void configurePathMatch(PathMatchConfigurer configurer) {
&emsp;&emsp;默认情况下 security 是无需任何自定义配置就可使用的,我们不考虑这种方式,直接讲如何个性化登录过程。
#### 1、 建立 security 配置文件,目前配置文件中还没有任何配置。
```java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@ -65,8 +76,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
```
#### 2、 个性化登录security 中的登录如下:
![登录过程](./picFolder/pic1.png)
- security 需要一个 user 的实体类实现`UserDetails`接口,该实体类最后与系统中用户的实体类分开,代码如下:
```java
public class SecurityUser implements UserDetails{
private static final long serialVersionUID = 1L;
@ -74,7 +88,7 @@ public class SecurityUser implements UserDetails{
private String name;
List<GrantedAuthority> authorities;
public User(string name,string password) {
public SecurityUser(string name,string password) {
this.id = id;
this.password = password;
this.name = name;
@ -125,7 +139,9 @@ public class SecurityUser implements UserDetails{
}
}
```
- 编写了实体类还需要编写一个服务类 SecurityService 实现`UserDetailsService`接口,重写 loadByUsername 方法,通过这个方法根据用户名获取用户信息,代码如下:
```java
@Component
public class SecurityUserService implements UserDetailsService {
@ -158,7 +174,9 @@ public class SecurityUserService implements UserDetailsService {
}
}
```
- 通常我们会对密码进行加密,所有还要编写一个 passwordencode 类,实现 PasswordEncoder 接口,代码如下:
```java
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@ -178,7 +196,9 @@ public class MyPasswordEncoder implements PasswordEncoder {
```
#### 3、 编辑配置文件
- 编写 config Bean 以使用上面定义的验证逻辑,securityUserService、myPasswordEncoder 通过@Autowired 引入。
```java
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
@ -186,7 +206,9 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
.passwordEncoder(myPasswordEncoder);
}
```
- 然后编写 configure Bean和上一个不一样参数不同实现 security 验证逻辑,代码如下:
```java
@Override
protected void configure(HttpSecurity http) throws Exception {
@ -214,6 +236,7 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
.logoutSuccessUrl("public/logoutSuccess")
}
```
到这里便可实现 security 与 springboot 的基本整合。
## 四、实现记住我功能
@ -221,6 +244,7 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
#### 1、 建表
&emsp;&emsp;记住我功能需要数据库配合实现,首先要在数据库建一张表用户保存 cookie 和用户名,数据库建表语句如下:不能做修改
```sql
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
@ -232,7 +256,9 @@ CREATE TABLE `persistent_logins` (
```
#### 2、 编写 rememberMeservice Bean
&emsp;&emsp;代码如下:
```java
@Bean
public RememberMeServices rememberMeServices(){
@ -245,13 +271,17 @@ CREATE TABLE `persistent_logins` (
return rememberMeServices;
}
```
dataSource 为@Autowired 引入
#### 3、 配置文件设置 remember
&emsp;&emsp;在 config(HttpSecurity http)中加入记住我功能
```java
.rememberMe()
.rememberMeServices(rememberMeServices())
.key("INTERNAL_SECRET_KEY")
```
在登录表单中设置 remember-me 即可实现记住我功能。

View File

@ -1,3 +1,11 @@
---
id="2018-08-21-10-38"
title="springboot+security整合2"
headWord="文接上篇上一篇中登录验证都由security帮助我们完成了如果我们想要增加一个验证码登录或者其它的自定义校验就没办法了因此这一篇讲解如何实现这个功能。"
tags=["java", "spring","springboot","spring-security","security"]
category="java"
serie="spring boot学习"
---
[id]:2018-08-21
[type]:javaee
[tag]:java,spring,springsecurity,scurity

View File

@ -1,6 +1,11 @@
[id]:2018-08-22
[type]:javaee
[tag]:java,spring,springsecurity,scurity
---
id="2018-08-22-10-38"
title="springboot+security整合3"
headWord="文接上篇上篇说了那个啥自定义校验的功能这篇来学学如何自定义鉴权。感觉都定义到这个地步都不太需要security框架了再自己整整缓存方面的功能就是一个功能完成的鉴权模块了。"
tags=["java", "spring","springboot","spring-security","security"]
category="java"
serie="spring boot学习"
---
&emsp;&emsp;这篇讲解如何自定义鉴权过程,实现根据数据库查询出的 url 和 method 是否匹配当前请求的 url 和 method 来决定有没有权限。security 鉴权过程如下:
![鉴权流程](./picFolder/pic2.png)
@ -8,6 +13,7 @@
## 一、 重写 metadataSource 类
1. 编写 MyGranteAuthority 类,让权限包含 url 和 method 两个部分。
```java
public class MyGrantedAuthority implements GrantedAuthority {
private String method;
@ -42,7 +48,9 @@ public class MyGrantedAuthority implements GrantedAuthority {
}
}
```
2. 编写 MyConfigAttribute 类,实现 ConfigAttribute 接口,代码如下:
```java
public class MyConfigAttribute implements ConfigAttribute {
private HttpServletRequest httpServletRequest;
@ -71,7 +79,9 @@ public class MyConfigAttribute implements ConfigAttribute {
}
}
```
3. 编写 MySecurityMetadataSource 类,获取当前 url 所需要的权限
```java
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@ -123,6 +133,7 @@ public class MySecurityMetadataSource implements FilterInvocationSecurityMetadat
## 二、 编写 MyAccessDecisionManager 类
&emsp;&emsp;实现 AccessDecisionManager 接口以实现权限判断,直接 return 说明验证通过,如不通过需要抛出对应错误,代码如下:
```java
@Component
public class MyAccessDecisionManager implements AccessDecisionManager{
@ -163,6 +174,7 @@ public class MyAccessDecisionManager implements AccessDecisionManager{
## 三、 编写 MyFilterSecurityInterceptor 类
&emsp;&emsp;该类继承 AbstractSecurityInterceptor 类,实现 Filter 接口,代码如下:
```java
@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@ -214,7 +226,9 @@ public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor imp
```
## 四、 加入到 security 的过滤器链中
```java
.addFilterBefore(urlFilterSecurityInterceptor,FilterSecurityInterceptor.class)
```
完成

View File

@ -1,8 +1,13 @@
[id]:2018-08-25
[type]:javaee
[tag]:java,spring,websocket
---
id="2018-08-25-10-38"
title="springboot整合WebSocket"
headWord="webSocket作为http单向连接的补充实现了服务端浏览器端的双向通信还是有必要了解了解"
tags=["java", "spring","springboot","WebSocket"]
category="java"
serie="spring boot学习"
---
<h3 id="#一、背景">一、背景</h3>
### 一、背景
&emsp;&emsp;我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主流实现方式:
@ -13,11 +18,12 @@
**注意:如下都是针对使用 springboot 内置容器**
<h3 id="二、实现">二、实现</h3>
### 二、实现
<h4 id="1、依赖引入">1、依赖引入</h4>
#### 1、依赖引入
&emsp;&emsp;要使用 websocket 关键是`@ServerEndpoint`这个注解,该注解是 javaee 标准中的注解,tomcat7 及以上已经实现了,如果使用传统方法将 war 包部署到 tomcat 中,只需要引入如下 javaee 标准依赖即可:
```xml
<dependency>
<groupId>javax</groupId>
@ -26,7 +32,9 @@
<scope>provided</scope>
</dependency>
```
如使用 springboot 内置容器,无需引入springboot 已经做了包含。我们只需引入如下依赖即可:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
@ -36,9 +44,10 @@
</dependency>
```
<h4 id="2、注入Bean">2、注入Bean</h4>
#### 2、注入 Bean
&emsp;&emsp;首先注入一个**ServerEndpointExporter**Bean,该 Bean 会自动注册使用@ServerEndpoint 注解申明的 websocket endpoint。代码如下
```java
@Configuration
public class WebSocketConfig {
@ -49,9 +58,10 @@ public class WebSocketConfig {
}
```
<h4 id="3、申明endpoint">3、申明endpoint</h4>
#### 3、申明 endpoint
&emsp;&emsp;建立**MyWebSocket.java**类,在该类中处理 websocket 逻辑
```java
@ServerEndpoint(value = "/websocket") //接受websocket请求路径
@Component //注册到spring容器中
@ -135,21 +145,21 @@ public class MyWebSocket {
}
```
<h4 id="4、客户的实现">4、客户的实现</h4>
#### 4、客户的实现
&emsp;&emsp;客户端使用 h5 原生 websocket部分浏览器可能不支持。代码如下
```html
<html>
<head>
<title>websocket测试</title>
<meta charset="utf-8">
<meta charset="utf-8" />
</head>
<body>
<button onclick="sendMessage()">测试</button>
<script>
let socket = new WebSocket("ws://localhost:8080/websocket");
let socket = new WebSocket('ws://localhost:8080/websocket');
socket.onerror = err => {
console.log(err);
};
@ -160,24 +170,22 @@ public class MyWebSocket {
console.log(mess);
};
socket.onclose = () => {
console.log("连接关闭");
console.log('连接关闭');
};
function sendMessage() {
if (socket.readyState === 1)
socket.send("这是一个测试数据");
else
alert("尚未建立websocket连接");
if (socket.readyState === 1) socket.send('这是一个测试数据');
else alert('尚未建立websocket连接');
}
</script>
</body>
</html>
```
<h3 id="三、测试">三、测试</h3>
### 三、测试
&emsp;&emsp;建立一个 controller 测试群发,代码如下:
```java
@RestController
public class HomeController {
@ -188,7 +196,9 @@ public class HomeController {
}
}
```
然后打开上面的 html可以看到浏览器和服务器都输出连接成功的信息
```
浏览器:
Event {isTrusted: true, type: "open", target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}
@ -196,14 +206,17 @@ Event {isTrusted: true, type: "open", target: WebSocket, currentTarget: WebSock
服务端:
2018-08-01 14:05:34.727 INFO 12708 --- [nio-8080-exec-1] com.fxb.h5websocket.MyWebSocket : 新的连接加入0
```
点击测试按钮,可在服务端看到如下输出:
```
2018-08-01 15:00:34.644 INFO 12708 --- [nio-8080-exec-6] com.fxb.h5websocket.MyWebSocket : 收到客户端2消息这是一个测试数据
```
再次打开 html 页面,这样就有两个 websocket 客户端,然后在浏览器访问[localhost:8080/broadcast](localhost:8080/broadcast)测试群发功能,每个客户端都会输出如下信息:
```
MessageEvent {isTrusted: true, data: "这是一条测试广播", origin: "ws://localhost:8080", lastEventId: "", source: null, …}
```
<br/>
&emsp;&emsp;源码可在[github]()上下载记得点赞star哦
&emsp;&emsp;源码可在 [github 下载](https://github.com/FleyX/demo-project/tree/master/h5websocket) 上下载记得点赞star 哦

View File

@ -1,10 +1,17 @@
[id]:2018-09-01
[type]:javaee
[tag]:java,spring,mysql,mybatis,xml
---
id="2018-09-01-10-38"
title="springboot整合Mybatis(xml和注解)"
headWord="写的一手好sql就能玩转mybatis入门难度相较于hibernate小得多。"
tags=["java", "spring","springboot","mysql","mybatis","xml","注解"]
category="java"
serie="spring boot学习"
---
## 写在前面
刚毕业的第一份工作是java开发项目中需要用到mybatis特此记录学习过程这只是一个简单demomybatis用法很多不可能全部写出来有更复杂的需求建议查看mybatis的官方中文文档[点击跳转](http://www.mybatis.org/mybatis-3/zh/index.html)。下面时项目环境/版本。
&emsp;&emsp;项目源代码在github地址为[https://github.com/FleyX/demo-project/tree/master/mybatis-test](https://github.com/FleyX/demo-project/tree/master/mybatis-test),有需要的自取。
&emsp;&emsp;刚毕业的第一份工作是 java 开发,项目中需要用到 mybatis特此记录学习过程这只是一个简单 demomybatis 用法很多不可能全部写出来,有更复杂的需求建议查看 mybatis 的官方中文文档,[点击跳转](http://www.mybatis.org/mybatis-3/zh/index.html)。下面时项目环境/版本。
- 开发工具IDEA
- jdk 版本1.8
@ -87,7 +94,7 @@
## 1.创建项目
使用idea中的spring initializr生成maven项目项目命令为mybatis-test选择webmysqlmybatis依赖即可成功。详细过程不赘述如有需要学习springboot创建过程可参考[这篇文章]()。
使用 idea 中的 spring initializr 生成 maven 项目,项目命令为 mybatis-test选择 webmysqlmybatis 依赖,即可成功。(详细过程不赘述,如有需要学习 springboot 创建过程,可参考[这篇文章](http://tapme.top/blog/detail/2018-08-13-10-38)。
然后依照上面的 pom 文件,补齐缺少的依赖。接着创建包 entityservice 和 mybatis 映射文件夹 mapper创建。为了方便配置将 application.properties 改成 application.yml。由于我们时 REST 接口,故不需要 static 和 templates 目录。修改完毕后的项目结构如下:
@ -330,14 +337,10 @@ public class UserController {
![查询](./picFolder/pic3.png)
- 分页查询
![分页查询](./picFolder/pic4.png)
## 4.注解编写 sql
上面使用的是 xml 方式编写 sql 代码,其实 mybatis 也支持在注解中编写 sql这样可以避免编写复杂的 xml 查询文件,但同时也将 sql 语句耦合到了代码中,也不易实现复杂查询,因此多用于简单 sql 语句的编写。
@ -360,7 +363,3 @@ public interface UserDao {
```
然后重新启动项目测试,测试结果跟上面完全一样。
```
如果对你有帮助记得点赞、收藏哦!
```

View File

@ -1,34 +1,46 @@
[id]:2018-09-05
[type]:javaee
[tag]:java,spring,springboot,activemq
---
id="2018-09-05-10-38"
title="springboot整合ActiveMQ1"
headWord="稍大的项目中都会用到mq消息队列so这个自然也是要学的为啥是activeMQ呢?刚好目前的项目中用的就是这个拉。"
tags=["java", "spring","springboot","消息队列","activeMQ"]
category="java"
serie="spring boot学习"
---
**说明acitveMQ 版本为5.9.1springboot 版本为 2.0.3,项目地址:[点击跳转](https://github.com/FleyX/demo-project/tree/master/jms_demo)**<br/>
**说明acitveMQ版本为5.9.1springboot版本为2.0.3**<br/>
## 一. 下载安装windows
&emsp;&emsp;官方下载地址:[点我跳转](http://activemq.apache.org/download-archives.html),选择 windows 安装包下载,然后解压,解压后运行 bin 目录下的**activemq.bat**启动服务,无报错即可启动成功。默认管理地址为:[localhost:8161/admin](localhost:8161/admin),默认管理员账号密码为**admin**/**admin**。
## 二. springboot 整合
### &emsp;1. 创建springboot项目
### 1. 创建 springboot 项目
&emsp;&emsp;创建 springboot web 项目,加入 spring-boot-starter-activemq 依赖。
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
```
&emsp;&emsp;然后编辑配合文件加上一个配置61616 为 activeMQ 的默认端口,暂时不做其他配置,使用默认值。
```yml
spring:
activemq:
broker-url: tcp://localhost:61616
```
### &emsp;2. 创建生产者消费者
### 2. 创建生产者消费者
&emsp;&emsp;springboot 中 activeMQ 的默认配置为**生产-消费者模式**,还有一种模式为**发布-订阅模式**后面再讲。项目目录如下:
![项目目录](./picFolder/pic1.png)
&emsp;&emsp;首先编写配置类 Config.java代码如下
```java
@Configuration
public class Config {
@ -43,10 +55,12 @@ public class Config {
}
}
```
上面的代码建立了两个消息队列 queue1queue2,分别由 queue1 和 queue2 这两个 Bean 注入到 Spring 容器中。程序运行后会在 activeMQ 的管理页面->queue 中看到如下:
![队列](./picFolder/pic2.png)
&emsp;&emsp;生产者 Producer.java 代码如下:
```java
@RestController
public class Producer {
@ -70,9 +84,11 @@ public class Producer {
}
}
```
上面的类创建了两个 GET 接口,访问这两个接口分别向 queue1 和 queue2 中发送消息。
消费者 Comsumer.java 代码如下:
```java
@Component //将该类注解到Spring 容器中
public class Comsumer {
@ -101,17 +117,20 @@ public class Comsumer {
}
}
```
上面的代码定义了 4 个消费者,每两个消费一个消息队列。
## &emsp;3. 运行
### 3. 运行
&emsp;&emsp;启动项目后分别向/queue1?message=niihao,/queue2?message=nihaoa 发送 http 请求,然后我们可以在控制台中看到如下输出:
```
2I'm from queue2:nihaoa
1I'm from queue2:nihaoa
2I'm from queue1:nihao
1I'm from queue1:nihao
```
消息都成功被消费者消费,从打印结果也可看出生产者消费者的一个特点:一个消息只会被一个消费者消费。同时在管理页面中可以看到:
![运行结果](./picFolder/pic3.png)
每个消息队列有两个消费者,队列进入了三个消息,出了三个消息,说明消息都被消费掉了,如果注释掉消费者代码,再次运行,然后发送消息就会发现 MessagesEnqueued 数量大于 MessagesDequeued然后再让消费者上线会立即消费掉队列中的消息。

View File

@ -1,3 +1,11 @@
---
id="2018-09-06-10-38"
title="springboot整合ActiveMQ2"
headWord="接着上文来说这里来说如何实现activemq的主从备份"
tags=["java", "spring","springboot","消息队列","activeMQ"]
category="java"
serie="spring boot学习"
---
[id]:2018-09-06
[type]:javaee
[tag]:java,spring,activemq

View File

@ -1,6 +1,11 @@
[id]:2018-09-10
[type]:javaee
[tag]:java,spring,springboot,mybatis,读写分离
---
id="2018-09-10-10-38"
title="springboot配置读写分离(Mybatis)"
headWord="近日工作任务较轻,有空学习学习技术,遂来研究如果实现读写分离。这里用博客记录下过程,一方面可备日后查看,同时也能分享给大家(网上的资料真的大都是抄来抄去,,还不带格式的,看的真心难受)。"
tags=["java", "spring","springboot","mysql","主从备份","读写分离"]
category="java"
serie="spring boot学习"
---
&emsp;&emsp;近日工作任务较轻,有空学习学习技术,遂来研究如果实现读写分离。这里用博客记录下过程,一方面可备日后查看,同时也能分享给大家(网上的资料真的大都是抄来抄去,,还不带格式的,看的真心难受)。

View File

@ -1,7 +1,7 @@
---
id="2018-11-19-15-57-00"
title="springCloud之config"
headWord="本篇主要用于记录如何在spring cloud中将服务配置与服务代码分离开来通过向集中的配置服务请求获取某个微服务需要的配置。同时如何对敏感信息进行加密,比如密码一类的配置项"
title="springCloud学习1(集中式配置管理)"
headWord="本篇主要用于记录如何在spring cloud中将服务配置与服务代码分离开来通过向集中的配置服务请求获取某个微服务需要的配置。"
tags=["spring-boot", "spring-cloud-config","git"]
category="java"
serie="springCloud实战"
@ -280,4 +280,4 @@ spring:
## 总结
&emsp;&emsp;本篇只是用到了 spring-cloud-config 这个来进行配置集中管理,并没有涉及到微服务,在下一篇将开始微服务的学习。
&emsp;&emsp;本篇两个项目代码存放于:[记得补充啊]()
&emsp;&emsp;本篇两个项目代码存放于:[点击跳转](https://github.com/FleyX/demo-project/tree/master/springcloud/%E7%AC%AC%E4%B8%80%E7%AF%87%E6%89%80%E7%94%A8%E4%BB%A3%E7%A0%81)

View File

@ -1,6 +1,6 @@
---
id="2018-11-22-15-57"
title="springCloud之服务发现"
title="springCloud学习2(服务发现)"
headWord="在任何分布式架构中,都需要找到机器所在的物理地址,这个概念自分布式计算开始就已经存在,并且被正式称为服务发现,本篇是对服务发现的一个学习总结"
tags=["spring-boot", "spring-cloud-config","git"]
category="java"
@ -437,4 +437,4 @@ public Licensing getLicensingByFeign(@PathVariable("orgId") String orgId) {
# 总结
&emsp;&emsp;这一节磨磨蹭蹭写了好几天,虽然例子很简单,但是相信应该是能够看懂的。由于篇幅原因代码没有全部贴上,想要查看完整代码,可以访问这个链接:[记得补全啊]()。
&emsp;&emsp;这一节磨磨蹭蹭写了好几天,虽然例子很简单,但是相信应该是能够看懂的。由于篇幅原因代码没有全部贴上,想要查看完整代码,可以访问这个链接:[点击跳转](https://github.com/FleyX/demo-project/tree/master/springcloud/%E7%AC%AC%E4%BA%8C%E7%AF%87%E4%BB%A3%E7%A0%81)。

View File

@ -1,8 +1,8 @@
---
id="2018-11-28-15-57-00"
title="springCloud之Netflix Hystrix弹性客户端"
title="springCloud学习3Netflix Hystrix弹性客户端"
headWord="在任何分布式架构中,都需要找到机器所在的物理地址,这个概念自分布式计算开始就已经存在,并且被正式称为服务发现,本篇是对服务发现的一个学习总结"
tags=["spring-boot", "spring-cloud","netflix-hystrix"]
tags=["spring-boot", "spring-cloud","netflix-hystrix","熔断"]
category="java"
serie="springCloud实战"
---
@ -170,11 +170,13 @@ public class OrganizationFeignClientImpl implements OrganizationFeignClient{
}
}
```
然后修改 OrganizationFeignClient 接口的注解,将`@FeignClient("organizationservice")`改为`@FeignClient(name="organizationservice",fallback = OrganizationFeignClientImpl.class`
&emsp;&emsp;重启项目,多次访问[localhost:10011/licensingByFeign/11313/](localhost:10011/licensingByFeign/11313/),可发现后备服务起作用了。
&emsp;&emsp;在确认是否要启用后备服务时,要注意以下两点:
- 后备是一种在资源操时或失败时提供行动方案的机制。如果只是用后备来捕获操时异常然后只做日志记录,那只需要 try..catch 即可,捕获 HystrixRuntimeException 异常。
- 注意后备方法所执行的操作。如果在后备服务中调用另一个分布式服务,需要注意用@HystrixCommand 方法注解包装后备方法。
@ -197,8 +199,9 @@ public class OrganizationFeignClientImpl implements OrganizationFeignClient{
@HystrixProperty(name = "maxQueueSize", value = "10")
})
```
如果将`maxQueueSize`属性值设为-1将使用`SynchronousQueue`保存所有的传入请求,同步队列会强制要求正在处理中的请求数量永远不能超过线程池的大小。设为大于 1 的值将使用`LinkedBlockingQueue`
&emsp;&emsp;**注意**:示例代码中都是硬编码属性值到 Hystrix 注解中的。在实际应用环境中,一般都是将配置项配置在 Spring Cloud Config 中的,方便统一管理。
本次用到全部代码:[记得补全代码呀]()
本次用到全部代码:[点击跳转](https://github.com/FleyX/demo-project/tree/master/springcloud/%E7%AC%AC%E4%B8%89%E7%AF%87%E6%89%80%E7%94%A8%E4%BB%A3%E7%A0%81)

View File

@ -1,6 +1,11 @@
[id]:2018-09-22
[type]:java
[tag]:java,reflect,excel,hssfworksheet
---
id="2018-09-22-15-57-00"
title="java导出EXCEL文件"
headWord="最近在java上做了一个EXCEL的导出功能写了一个通用类在这里分享分享该类支持多sheet且无需手动进行复杂的类型转换."
tags=["reflex", "java","excel","hssfworksheet"]
category="java"
serie="java工具集"
---
## 一、背景
@ -176,6 +181,7 @@ private void setCell(HSSFCell cell, Object obj, Map<String, Field> fieldMap, Str
}
}
```
完整代码可以到 github 上查看下载,这里就不列出来了。
github 地址:[点击跳转]()

View File

@ -1 +1,8 @@
{"2018-11-22-15-57-00":14,"2018-11-20-10-38-05":18,"2018-11-19-15-57-00":3,"2018-10-22-10-38-05":14,"2018-10-21-10-38-05":3,"2018-10-20-10-38-05":20}
{
"2018-11-22-15-57-00": 14,
"2018-11-20-10-38-05": 18,
"2018-11-19-15-57-00": 3,
"2018-10-22-10-38-05": 14,
"2018-10-21-10-38-05": 3,
"2018-10-20-10-38-05": 20
}

View File

@ -4,7 +4,7 @@ title="vscode开发调试typescript"
headWord="本篇用于记录如何在vscode下开发typescript以及端点调试ts代码"
tags=["vscode", "node","typescript","ts"]
category="node"
serie="node开发环境配置"
serie="node开发配置"
---
### 1、安装 typescript

View File

@ -1,8 +1,13 @@
[id]:2018-10-01
[type]:项目
[tag]:node,vue,element-ui,axios,koa,redis,mysql
---
id="2018-10-01-13-58"
title="node,vue开发教学管理系统"
headWord="毕业才刚刚两个多月而已,现在想想大学生活是已经是那么的遥不可及,感觉已经过了好久好久,社会了两个月才明白学校的好啊。。。"
tags=["node", "vue","element-ui","axios","koa","redis","mysql","jwt"]
category="node"
serie="项目"
---
&emsp;&emsp;毕业才刚刚两个多月而已,现在想想大学生活是那么的遥不可及,感觉已经过了好久好久,社会了两个月才明白学校的好啊。。。额,扯远了,自从毕业开始就想找个时间写下毕设的记录总结,结果找了好久好久到今天才开始动笔。
&emsp;&emsp;毕业才刚刚两个多月而已,现在想想大学生活是已经是那么的遥不可及,感觉已经过了好久好久,社会了两个月才明白学校的好啊。。。额,扯远了,自从毕业开始就想找个时间写下毕设的记录总结,结果找了好久好久到今天才开始动笔。
&emsp;&emsp;我的毕业设计题目是:教学辅助系统的设计与实现,,是不是很俗。。。至于为啥是这个题目呢,完全是被导师坑了。。。。。
@ -70,12 +75,12 @@ KOA作为一个web框架其实它本身并没有提供路由功能需要配
&emsp;&emsp;KOA 作为一个 web 框架其实它本身并没有提供路由功能,需要配合使用 koa-router 来实现路由koa-router 以类似下面这样的风格来进行路由:
```javascript
const app = require("koa");
const router = require("koa-router");
router.get("/hello",koa=>{
koa.response="hello";
const app = require('koa');
const router = require('koa-router');
router.get('/hello', koa => {
koa.response = 'hello';
});
app.use(router.routes())
app.use(router.routes());
```
显然这样在项目中是很不方便的,如果每个路由都要手动进行挂载,很难将每个文件中的路由都挂载到一个 router 中。因此在参考网上的实现后,我写了一个方法在启动时自动扫描某个文件夹下所有的路由文件并挂载到 router 中,代码如下:
@ -100,7 +105,7 @@ function addMapping(router, filePath) {
} else if (url.startsWith('PUT ')) {
let temp = url.substring(4);
router.put(temp, mapping[url]);
console.log(`----PUT${temp}`)
console.log(`----PUT${temp}`);
} else if (url.startsWith('DELETE ')) {
let temp = url.substring(7);
router.delete(temp, mapping[url]);
@ -120,7 +125,7 @@ function addControllers(router, filePath) {
addControllers(router, temp);
} else {
if (!temp.endsWith('Helper.js')) {
console.log('\n--开始处理: ' + element + "路由");
console.log('\n--开始处理: ' + element + '路由');
addMapping(router, temp);
}
}
@ -150,17 +155,20 @@ const knowledgePointDao = require('../dao/knowledgePointDao.js');
/**
* 返回某门课的全部知识点,按章节分类
*/
exports["GET /course/:c_id/knowledge_point"] = async (ctx, next) => {
exports['GET /course/:c_id/knowledge_point'] = async (ctx, next) => {
let res = await knowledgePointDao.getPontsOrderBySection(ctx.params.c_id);
ctx.onSuccess(res);
}
};
//返回某位学生知识点答题情况
exports["GET /user/:u_id/course/:c_id/knowledge_point/condition"]=async(ctx,next)=>{
exports['GET /user/:u_id/course/:c_id/knowledge_point/condition'] = async (
ctx,
next
) => {
let { u_id, c_id } = ctx.params;
let res = await knowledgePointDao.getStudentCondition(u_id, c_id);
ctx.onSuccess(res);
}
};
```
#### b、权限验证
@ -171,12 +179,18 @@ exports["GET /user/:u_id/course/:c_id/knowledge_point/condition"]=async(ctx,next
//生成随机盐值
let str = StringHelper.getRandomString(0, 10);
//使用该盐值生成token
let token = jwt.sign({
let token = jwt.sign(
{
u_id: userInfo.u_id,
isRememberMe
}, str, {
expiresIn: isRememberMe ? config.longTokenExpiration:config.shortTokenExpiration
});
},
str,
{
expiresIn: isRememberMe
? config.longTokenExpiration
: config.shortTokenExpiration
}
);
//token-盐值存入redis如想让该token过期redis中清楚该token键值对即可
await RedisHelper.setString(token, str, 30 * 24 * 60 * 60);
res.code = 1;
@ -197,9 +211,9 @@ res.data = {
* 3 已登录,无操作权限 403
* 4 token已过期
*/
let verify = async (ctx) => {
let verify = async ctx => {
let token = ctx.headers.authorization;
if (typeof (token) != 'string') {
if (typeof token != 'string') {
return 2;
}
let yan = await redisHelper.getString(token);
@ -216,15 +230,19 @@ let verify = async (ctx) => {
return 4;
}
//判断是否需要刷新token如需要刷新将新token写入响应头
if (!data.isRememberMe && (data.exp * 1000 - Date.now()) < 30 * 60 * 1000) {
if (!data.isRememberMe && data.exp * 1000 - Date.now() < 30 * 60 * 1000) {
//token有效期不足半小时重新签发新token给客户端
let newYan = StringHelper.getRandomString(0, 10);
let newToken = jwt.sign({
let newToken = jwt.sign(
{
u_id: data.u_id,
isRememberMe: false
}, newYan, {
},
newYan,
{
expiresIn: config.shortTokenExpiration
});
}
);
// await redisHelper.deleteKey(token);
await redisHelper.setString(newToken, newYan, config.shortTokenExpiration);
ctx.response.set('new-token', newToken);
@ -234,14 +252,25 @@ let verify = async (ctx) => {
let userInfoKey = data.u_id + '_userInfo';
let userInfo = await redisHelper.getString(userInfoKey);
if (userInfo == null || Object.keys(userInfo).length != 3) {
userInfo = await mysqlHelper.first(`select u_id,u_type,j_id from user where u_id=?`, data.u_id);
await redisHelper.setString(userInfoKey, JSON.stringify(userInfo), 24 * 60 * 60);
userInfo = await mysqlHelper.first(
`select u_id,u_type,j_id from user where u_id=?`,
data.u_id
);
await redisHelper.setString(
userInfoKey,
JSON.stringify(userInfo),
24 * 60 * 60
);
} else {
userInfo = JSON.parse(userInfo);
}
ctx.userInfo = userInfo;
//更新用户上次访问时间
mysqlHelper.execute(`update user set last_login_time=? where u_id=?`,Date.now(),userInfo.u_id);
mysqlHelper.execute(
`update user set last_login_time=? where u_id=?`,
Date.now(),
userInfo.u_id
);
//管理员拥有全部权限
if (userInfo.u_type == 0) {
return 1;
@ -251,23 +280,30 @@ let verify = async (ctx) => {
let urls = await redisHelper.getObject(authKey);
// let urls = null;
if (urls == null) {
urls = await mysqlHelper.row(`
urls = await mysqlHelper.row(
`
select b.r_id,b.url,b.method from jurisdiction_resource a inner join resource b on a.r_id = b.r_id where a.j_id=?
`, userInfo.j_id);
`,
userInfo.j_id
);
let temp = {};
urls.forEach(item => {
temp[item.url + item.method] = true;
})
});
await redisHelper.setObject(authKey, temp);
urls = temp;
}
//判断是否拥有权限
if (urls.hasOwnProperty(ctx._matchedRoute.replace(config.url_prefix, '') + ctx.method)) {
if (
urls.hasOwnProperty(
ctx._matchedRoute.replace(config.url_prefix, '') + ctx.method
)
) {
return 1;
} else {
return 3;
}
}
};
```
根据用户 id 获取用户身份 id根据用户身份 id 从 redis 中获取拥有的权限,如为 null从 mysql 数据库中拉取,并存入 redis 中,然后判断是否拥有要访问的 url 权限。
@ -288,7 +324,8 @@ let verify = async (ctx) => {
export default new Router({
mode: 'history',
base: '/app/',
routes: [{
routes: [
{
path: '',
name: 'indexPage',
component: IndexPage
@ -303,11 +340,11 @@ export default new Router({
Public,
{
path: '*',
name: "NotFound",
name: 'NotFound',
component: NotFound
}
]
})
});
```
其中的 AdminClientPublic 分别为各部分的路由,以子路由的形式一级级组织。如下所示:
@ -366,15 +403,13 @@ export default {
```javascript
request = (url, method, params, form, isFormData, type) => {
let token;
if (type == 'admin')
token = getToken();
else
token = getClientToken();
if (type == 'admin') token = getToken();
else token = getClientToken();
let headers = {
'Authorization': token
Authorization: token
};
if (isFormData) {
headers['Content-Type'] = "multipart/form-data";
headers['Content-Type'] = 'multipart/form-data';
}
return new Promise((resolve, reject) => {
axios({
@ -382,30 +417,32 @@ request = (url, method, params, form, isFormData, type) => {
method,
params,
data: form,
headers,
headers
// timeout:2000
}).then(res => {
})
.then(res => {
resolve(res.data);
//检查是否有更新token
// console.log(res);
if (res.headers['new-token'] != undefined) {
console.log('set new token');
if (vm.$route.path.startsWith('/admin')) {
localStorage.setItem("token",res.headers['new-token']);
localStorage.setItem('token', res.headers['new-token']);
window.token = undefined;
} else if (vm.$route.path.startsWith('/client')) {
localStorage.setItem("clientToken",res.headers['new-token']);
localStorage.setItem('clientToken', res.headers['new-token']);
window.clientToken = undefined;
}
}
}).catch(err => {
})
.catch(err => {
reject(err);
if (err.code == 'ECONNABORTED') {
alertNotify("错误", "请求超时", "error");
alertNotify('错误', '请求超时', 'error');
return;
}
if (err.message == 'Network Error') {
alertNotify("错误", "无法连接服务器", 'error');
alertNotify('错误', '无法连接服务器', 'error');
return;
}
if (err.response != undefined) {
@ -416,41 +453,56 @@ request = (url, method, params, form, isFormData, type) => {
}
//使用该变量表示是否已经弹窗提示了,避免大量未登录弹窗堆积。
window.isGoToLogin = true;
vm.$alert(err.response.data, "警告", {
type: "warning",
vm.$alert(err.response.data, '警告', {
type: 'warning',
showClose: false
}).then(res => {
window.isGoToLogin = false;
if (vm.$route.path.startsWith('/admin/')) {
clearInfo();
vm.$router.replace("/public/admin_login");
vm.$router.replace('/public/admin_login');
} else {
clearClientInfo();
vm.$router.replace("/public/client_login");
vm.$router.replace('/public/client_login');
}
});
break;
case 403:
alertNotify("Error:403", '拒绝执行:' + err.response.data, "error");
alertNotify(
'Error:403',
'拒绝执行:' + err.response.data,
'error'
);
break;
case 404:
alertNotify("Error:404", "找不到资源:" + url.substr(0, url.indexOf('?')), 'error');
alertNotify(
'Error:404',
'找不到资源:' + url.substr(0, url.indexOf('?')),
'error'
);
break;
case 400:
alertNotify("Error:400", "请求参数错误:" + err.response.data, 'error');
alertNotify(
'Error:400',
'请求参数错误:' + err.response.data,
'error'
);
break;
case 500:
alertNotify("Error:500", "服务器内部错误:" + err.response.data, 'error');
alertNotify(
'Error:500',
'服务器内部错误:' + err.response.data,
'error'
);
default:
console.log('存在错误未处理:' + err);
}
} else {
console.log(err);
}
})
})
}
});
});
};
```
&emsp;&emsp;到这里就算是简单介绍完了,,想要更加深入了解的可以去 github 查看源代码,地址如下:[https://github.com/FleyX/teach_system](https://github.com/FleyX/teach_system)记得 star 哦!

View File

@ -1,8 +1,13 @@
[id]:2018-09-20
[type]:软件
[tag]:git,crlf,lf
---
id="2018-09-20-10-58"
title="git crlf、lf自动转换问题"
headWord="踩坑都是从git拉下来的就我老是报错怎么都跑不起来花了半天时间排查原因。。。"
tags=["git","crlf","lf","flyway"]
category="其他"
serie="踩坑"
---
&emsp;&emsp;项目组最近加了一个新功能到代码中使用flyway记录数据库版本变更该工具会记录每次数据库结构的修改并生成sql文件存在指定目录上当然必须用它来变更数据库外部的变更它是无法感知的然后每次启动时flyway会检查使用的数据库和当前项目代码中的sql变更版本是否一致一致正常启动不一致中如果是数据库落后将会更新数据库这样能够保证代码在任何地方运行数据库都是一致的),否则就报错了。数据库中有一张表记录版本信息,如下图:
&emsp;&emsp;项目组最近加了一个新功能到代码中,使用 flyway 记录数据库版本变更,该工具会记录每次数据库结构的修改并生成 sql 文件存在指定目录上(当然必须用它来变更数据库,外部的变更它是无法感知的),然后每次启动时 flyway 会检查使用的数据库和当前项目代码中的 sql 变更版本是否一致,一致正常启动,不一致中如果是数据库落后将会更新数据库(这样能够保证代码在任何地方运行数据库都是一致的),否则就报错了。数据库中有一张表记录版本信息,如下图:
![版本记录](./picFolder/版本记录.PNG),同时本地代码中也有一个文件夹保存每次操作的 sql 语句,如下图:
@ -18,4 +23,3 @@
git config --global core.autocrlf true //开启换行符自动转换
git config --global core.safecrlf true //禁止混用换行符
```

View File

@ -1,6 +1,11 @@
[id]:2018-09-30
[type]:java
[tag]:java,正则,pattern
---
id="2018-09-30-10-58"
title="正则匹配之正向/反向预测先行搜索"
headWord="今天领导让我写几个正则表达式来对密码做强度验证,听到写正则表达式内心是这样的感觉(哈哈,三分钟搞定,今天又可以打鱼了)..但是显然不会这么简单."
tags=["java","正则匹配","pattern","预测先行搜索"]
category="java"
serie="正则匹配"
---
## 一、背景
@ -24,19 +29,19 @@
关键是如何同时满足前三个条件,在我有限的知识里并不知道怎么搞,然后只好求助于万能的百度了,最终在找了几个小时后发现如下几个关键词,来源[菜鸟教程](http://www.runoob.com/java/java-regular-expressions.html)
- (?=*pattern*) :正向预测先行搜索
- (?=_pattern_) :正向预测先行搜索
名字看着高大上,不明所以,看完示例大概明白什么意思,这个表达式匹配从这个表达式起始的字符串(我也不知道咋解释),就是假设这样一个表达式 abc(?=[abc]) ,用它来匹配 abc123 字符串,(?=[abc])只会对作用于后面的 123这个显然是不匹配的后整个就不匹配了然后关键来了名字里有**预测**两个字,这两个字表名了这个表达式的特性:不占用字符,匹配后如果匹配成功就继续匹配了好像从来不存在这个东西一样,匹配失败就立即返回失败了。利用这个特性我们就可以给正则加限制条件了。
- (?!*pattern*) :反向预测先行搜索
- (?!_pattern_) :反向预测先行搜索
概念和上面一样但是效果是相反的abc(?![abc]),对于 abc123 是匹配成功的,对于 abca 匹配失败,如下所示:
```javascript
reg = /abc(?![abc])/;
reg.test("abc123")
reg.test('abc123');
//返回true
reg.test("abca")
reg.test('abca');
//返回false
```
@ -49,4 +54,3 @@
```javascript
^(?=.*?\d+.*?)(?=.*?[a-zA-Z]+.*?)(?=.*?[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+.*?)[\da-zA-Z\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{6,}$
```

View File

@ -1,6 +1,11 @@
[id]:2018-10-02
[type]:数据库
[tag]:acid,sql,分布式,事务
---
id="2018-10-03-10-58"
title="正则匹配之正向/反向预测先行搜索"
headWord="分布式听说很火但是分布式事务有很难解决so来了解了解"
tags=["分布式","sql","2PC","TCC","异步补偿"]
category="数据库"
serie="分布式事务"
---
## 前言

View File

@ -1,6 +1,11 @@
[id]:2018-10-03
[type]:数据库
[tag]:acid,sql,分布式,事务
---
id="2018-10-02-10-58"
title="数据库4个特性及隔离级别"
headWord="了解,了解。"
tags=["acid","sql","事务"]
category="数据库"
serie="分布式事务"
---
## 一、 数据库事务四大特性ACID

View File

@ -1,3 +1,11 @@
---
id="2018-08-04-10-58"
title="nat模式虚拟机主机相互ping通"
headWord="如题。"
tags=["vmware","nat","ping","ubuntu"]
category="java"
serie="正则匹配"
---
[id]:2018-08-04
[type]:软件
[tag]:vmware,vps,nat,ubuntu