-
添加数据返回包装类
-
添加 IdWorker谷歌雪花算法的ID生成类
-
修改 POM.XML文件
- 添加 MySQL数据库连接驱动依赖
- 添加 DAO层解决方案 JPA依赖
- 添加 tensquare_commom 模块依赖
-
注入 IdWoeker 到容器内 (可以用@service注解, 但是, 很多没有用到, 所以推荐使用@bean)
-
添加 controller 层代码 (CRUD)
-
添加 pojo 层
- jpa 规定实体类上需要添加 @Entity @Table(name = "tb_xxx") 主键@id
- 在分布式 微服务的架构中 实体类在各模块中通信 需要将实体类实现 Serializable 接口
- 如果是联合主键 需要在类上添加 @IdClass(Clazz.class)
-
添加 dao 层
- jpa 规定在dao接口中 需要继承 JpaRepository<Clazz, id的类型>, 同时如果是复杂类型(比如分页) 还要需要继承 JpaSpecificationExecutor
-
添加 service 层
- jpa findById() 返回值是一个 容器对象 Optional (JDK1.8的新特性, Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)) 可以依靠 Optional.get() 取值
- jap save() 可以新增也可以修改 如果没有ID就是新增, 如果参数有ID 就会去数据库查询 并修改参数ID的数据
-
添加 BaseExceptionHandler 异常处理
- 注解 类上 @RestControllerAdvice 是controller层的Advice 是 Spring AOP切面增加
- 注解 方法上 @ExceptionHandler(value = Exception.class) 表示处理异常为 Exception类型的异常 助手类
- logger.error(request.getRequestURI()); 日志记录 发生异常的请求 logger.error(e.toString(), e); 日志记录 发生异常的值栈信息
-
JPA 带条件的查询
- findAll(new Specification() xxx) 需要重写 Specification类的toPredicate(Root root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder)方法
- root 根对象,也就是要把条件封装到哪个对象中, where 列名 = label.getid
- criteriaQuery 封装的都是查询关键字, 比如 Group by 或 Order by
- criteriaBuilder 用来封装条件对象的(类似于mybatis中 example) 如果直接返回 null 表示不需要任何条件
- 因为查询的条件不确定, 可能是多个或是一个, 所以 criteriaBuilder.and(必须是初始化长度的数组), 但是条件的个数不知道, 数组的长度无法确认, 所以需要用集合来存放
- list.toArray(predicates); 和 predicates = list.toArray(); 是一样的 , 都将集合的值转成了数组后 赋值给了参数数组
-
JPA 带条件的分页查询
- 带条件的分页查询和只带条件的查询差不多, 主要区别于, 分页的findAll() 方法需要两个参数, 第二个参数是Pageable 分页对象的方法 PageRequest.of(page-1, size);
- 主要JPA的分页查询中 传入的page 页 必须是从零开始的, 所以前端传过来的参数必须减1, 返回的对象不推荐是Page, Page对象封装了很多对象, 我们只需要通过page.getTotalElements(), page.getContent() 取出总记录和每页显示条数
- 配置recruit 模块的一些参数, 连接数据库名称, POM文件, 工程名, 端口号, 运行主类
- JPA 命名规则生成SQL语句
- 多对多 中间表 关联查询 在持久层DAO的方法上 加注解@Query() 里面的字符串是JQL 如果要用SQL语句 需要 加上nativeQuery = ture
- JQL 的语法 是所有关于SQL中表名 全部换成对象名
- 在JPA 的持久层中除了查询 其他操作都应该加上 注解 @Modifying
@Modifying
@Query("UPDATE Article art SET art.state = '1' WHERE art.id = ?1")
/* 注意: UPDATE Article SET Article.state = '1' WHERE Article.id = ?1 这样写会报空指针异常
update Article set state='1' where id=?1 正确的也可以写成这样
自己分析 应该是 Article SET 后面 再次出现 算是新的对象 并不是要修改的对象 也就是说是 SET 前的那个 Article */
- 注意的问题
2019-05-13 16:51:18.122 ERROR 5320 --- [nio-9004-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Table 'tensquare_base.tb_article' doesn't exist
其实是数据库连错了
- 实现文章的缓存处理
- 需要在启动类上 添加注解 @EnableCacheing 开启缓存
- 在需要缓存的方法上添加注解 @Cacheable(value="xxx",key="#xxx") 参数 value 表示 cacheManage 的名称 参数 key 表示 缓存的键
- 如果修改,添加 需要删除原来的缓存 @CacheEvict(value="xxx",key="#xx.xxx")
-
BCrypt 密码加密
- 导入 spring-boot-starter-security 依赖
- 添加配置类 配置类继承 WebSecurityConfigurerAdapter 并重写 configure(HttpSecurity http) 方法, 这个类上需添加两个注解 @Configuration @EnableWebSecurity
- 在启动类中配置Bean 注入 BCryptPasswordEncoder
- 在MySQL数据库中字段类型为 bigint 对应Java 类型Long, 而不是long
-
添加拦截器
-
继承 HandlerInterceptorAdapter 适配器 可以重写三个方法
也可直接实现 implements HandlerInterceptor 重写三个方法
-
预处理 perHandle 可以进行编码, 安全控制等处理
-
后处理 postHandle 可以修改 ModelAndView
-
返回处理 afterCompletion 可以根据exception 是否null 判断是否发生异常, 进行日志记录
-
-
添加配置类 注册拦截器
- 添加配置类 注解 @Configuration
- 重新 addInterceptors(InterceptorRegistry registry) 添加注册拦截器
- 添加 配置类 进行拦截器的注册 该类需要添加 @configuration 注解 并且重写这个接口对象实例 new WebMvcConfigurer(){ ... }
- addCorsMappings(CorsRegistry registry) 重写父类提供的跨域请求处理的接口
- addInterceptors(InterceptorRegistry registry) 注册拦截器
- 该配置类的方法实例对象需要加上 @Bean 去注入到spring容器中
-
添加 Eureka server 服务端
- 创建 Eureka 模块 作为微服务的管理端
- 导入 SpringCloud Eureka 服务端的依赖 spring-cloud-starter-netflix-eureka-server
- 启动类 添加 @EnableEurekaServer 注解
- 配置文件 添加如下配置
server: port: 6868 # 自定义 eureka: client: register-with-eureka: false # 是否将自己注册到Eureka服务中, 模块本身作为服务器, 则无需注册 默认值为 true fetch-registry: false service-url: defaultZone: http://127.0.0.1:${server.port}/eureka/
- Eureka 管理信息UI界面 浏览器链接地址 IP地址:6868,
-
添加 Eureka client 客服端
-
在需要注册到服务端的模块添加依赖 spring-cloud-starter-netflix-eureka-client
-
配置文件中
eureka: client: service-url: # Eureka客户端与Eureka服务端进行交互的地址 defaultZone: http://127.0.0.1:6868/eureka/ instance: prefer-ip-address: true
-
在启动类中添加注解 @EnableEurekaClient
-
-
在需要调用的模块中
-
添加依赖 spring‐cloud‐starter‐openfeign
-
在需要调用模块的启动类上添加注解 @EnableDiscoveryClient @EnableFeignClients
-
创建client 包, 添加一个interface 接口
-
接口添加注解@FeignClient(value = "需要被调用的模块项目名称")
-
添加和被调用模块的同名方法 注意 @PathVariable注 解一定要指定参数名称,否则出错 , requestmapping 的URL 需要带上被调用的完整路径
-
在调用模块的controller 中注入接口, 并调用
-
-
在需要调用的模块中
-
添加配置
feign: hystrix: enabled: true
-
在调用的模块中的client包的接口 创建impl 包 ,添加接口的实现类 重新接口的方法 实现由于被调用的模块如果出错后, 执行的逻辑, 避免自己出错而引起的雪崩效应 注意添加@component 注解 将实现类添加的Spring容器中
-
修改client 中接口的注解 @FeignClient(name = "tensquare-base", fallback = BaseLabelClientImpl.class) 注意 接口也需要添加一个 @component 注解
-
-
创建一个后端管理模块 manage 和一个前端访问模块 web
-
添加依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <!-- 注意这里的artifactID 都是带有starter的依赖 -->
-
添加配置文件
spring: # =================================================================== # 项目名称 # =================================================================== application: name: tensquare-manage # =================================================================== # Spring cloud eureka 配置 客服端 # =================================================================== eureka: client: service-url: # Eureka客户端与Eureka服务端进行交互的地址 defaultZone: http://127.0.0.1:6868/eureka/ instance: prefer-ip-address: true # =================================================================== # Spring cloud zuul 配置 网关路由 # =================================================================== zuul: routes: tensquare-gathering: # 活动 path: /gathering/** # 配置请求URL的请求规则 serviceId: tensquare-gathering # 指定 eureka 注册中心的服务ID tensquare-article: # 文章 path: /article/** # 配置请求URL的请求规则 serviceId: tensquare-article # 指定 eureka 注册中心的服务ID tensquare-base: # 基础 path: /base/** # 配置请求URL的请求规则 serviceId: tensquare-base # 指定 eureka 注册中心的服务ID tensquare-friend: # 交友 path: /friend/** # 配置请求URL的请求规则 serviceId: tensquare-friend # 指定 eureka 注册中心的服务ID tensquare-qa: # 问答 path: /qa/** # 配置请求URL的请求规则 serviceId: tensquare-qa # 指定 eureka 注册中心的服务ID tensquare-recruit: # 招聘 path: /recruit/** # 配置请求URL的请求规则 serviceId: tensquare-recruit # 指定 eureka 注册中心的服务ID tensquare-spit: # 吐槽 path: /spit/** # 配置请求URL的请求规则 serviceId: tensquare-spit # 指定 eureka 注册中心的服务ID tensquare-user: # 用户 path: /user/** # 配置请求URL的请求规则 serviecId: tensquare-user # 指定 eureka 注册中心的服务ID
-
给启动类添加注解 @EnableZuulProxy
-
注意访问的时候端口号是网关的端口号, 并且端口号后面是网关路由匹配的规则 然后才是访问接口的URL映射地址,
-
还有最后网关好像需要最后启动,不然会报错
-
-
配置 Zuul 过滤器
-
添加一个过滤器类 并继承 ZuulFilter
-
重写四个方法
@Component public class ManageFilter extends ZuulFilter { @Override public String filterType() { return "pre"; // 前置过滤器 } @Override public int filterOrder() { return 0; // 优先级为0 数字越大 优先级越低 } @Override public boolean shouldFilter() { return true; // 是否执行过滤器, 此处为true, 说明需要过滤 } @Override public Object run() throws ZuulException { System.out.println("zuul 过滤器执行了"); return null; } }
-
filterType :返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过 滤器类型
- pre: 请求路由之前调用
- route: 在路由请求的时候调用
- post: router 和 error 过滤器之后被调用
- error: 处理请求发送错误时调用
-
filterOrder: 通过int 值来定义过滤器的执行顺序
-
shouldFilter: 返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可 实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效
-
run: 过滤器的具体逻辑
-
webfilter 并不需要权限校验, 因为未登录的用户也可以浏览信息, 而managefilter 则需要验证, 因为后端管理是需要权限的
-
-
配置服务端
-
Git 新建一个仓库, 并将配置文件 改名为 {application}-{profile}.yml 上传
-
创建新模块 tensquare_config 添加新的依赖 Spring-cloud-config-server
-
创建启动类 添加注解 @EnableConfigServer
-
编写配置文件
server: # =================================================================== # 端口号 # =================================================================== port: 12000 spring: # =================================================================== # 项目名称 # =================================================================== application: name: tensquare-config # =================================================================== # spring-cloud-config 配置中心服务 # =================================================================== cloud: config: server: git: uri: https://gitee.com/Freya0016/tensquare-config.git
-
注意服务端:Authentication is required but no CredentialsProvider has been registered 异常信息 需要将Git仓库改成公开的才行
-
-
配置客服端
-
在需要配置的模块中 添加 spring-cloud-starter-config 依赖
-
添加bootstrap.yml 删除application.yml
-
-
通过Maven插件自动部署
-
修改宿主机的docker配置,让其可以远程访问
vi /lib/systemd/system/docker.service 其中 ExecStart=后添加配置 ‐H tcp://0.0.0.0:2375 ‐H unix:///var/run/docker.sock 具体位置 插入到 --seccomp-profile ... \ ‐H tcp://0.0.0.0:2375 ‐H unix:///var/run/docker.sock \ $OPTIONS \ systemctl daemon‐reload // 重新载入docker 守护线程 systemctl restart docker // 重启docker 服务 docker start registry // 重启私有仓库服务 注意其中 一定要开启防护墙 的端口 2375 不然无法访问 教训啊 firewall-cmd --zone=public --add-port=80/tcp --permanent (--permanent永久生效,没有此参数重启后失效) firewall-cmd --reload 查看所有打开的端口: firewall-cmd --zone=public --list-ports
-
在工程pom.xml 增加配置
<build> <finalName>app</finalName> <!-- maven 打包上传后的文件名 --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- springboot 整合 maven 插件 --> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.13</version> <!-- docker的maven插件 官网:https://github.com/spotify/docker-maven-plugin --> <configuration> <imageName>{ip}:5000/${project.artifactId}:${project.version}</imageName> <!-- 标记要上传的(docker tag)镜像名 ip地址:端口号/artifactId/version --> <baseImage>jdk1.8</baseImage> <!-- 集成的基础镜像 springboot 打包的项目 需要依赖 JDK1.8运行环境 --> <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint> <!-- docker把镜像生成容器后 执行该命令 java -jar 项目文件名.jar --> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> <dockerHost>http//{ip}:2375</dockerHost> <!-- docker 连接地址 --> </configuration> </plugin> </plugins> </build>
-
-
安装之前的 配置
mkdir /home/jenkins
创建jenkins 挂载的文件夹mkdir /home/jenkins/java
创建一个存放 jdk 的文件夹mkdir /home/jenkins/maven
创建一个存放 maven 的文件夹- 将JDK 和 maven 上传到对应文件夹下 解压 tar -zxvf xxxx.tar.gz
- vim /home/jenkins/maven/apache-maven-3.6.1/conf/settings.xml
- 添加 /var/jenkins_home/RepMaven
注意: 这里的粗体字, 因为maven所在的文件夹是在容器jenkins 容器挂载在宿主机上的文件, 所以需要写上jenkins能识别的路径, 如果写成宿主机的路径 /home/jenkins/RepMaven 会无法找到文件 ,maven是在jenkins中运行的, 所有的依赖将会下载到jenkins 容器对应路径的 文件下 /var/jenkins_home/RepMaven 教训啊
- 需要给容器jenkins挂载的文件授权chown -R 1000:1000 jenkins/ 给UID为1000的权限 不然容器无法启动
-
安装jenkins
- 需要安装 docker.io/jenkins/jenkins 而docker.io/jenkins版本太旧无法下载插件
- docker run -d --name jenkins -p 8888:8080 -p 50000:50000 -v /home/jenkins:/var/jenkins_home --privileged docker.io/jenkins/jenkins
- 注意 挂载的路径对应 并且--privileged 授权
- jenkins 需要安装 插件 Maven Integration
- 配置全局 JDK 和 maven
注意这里的路径 和上面的引用说明一样 不能直接写宿主机挂载的路径 /home/jenkins/java/jdk1.8.0_201 而应该是 /var/jenkins_home/java/jdk1.8.0_201 因为JDK 是在jenkins中运行的 配置的路径也应该是 容器挂载的映射地址 maven 也一样 不能是 /home/jenkins/maven/apache-maven-3.6.1 而应该是 /var/jenkins_home/maven/apache-maven-3.6.1
解释一下我这里为什么是/var/jenkins_home/ 因为我首先jenkins是安装在docker里的,这时候使用的就是docker里的路径, 我们上面运行jenkins的时候,看我标红的地方; 这里我的jdk是复制了一份放在/home/jenkins/下面的,所以这里路径就可以直接这么写了; 注意:这里是JAVA_HOME,所以不要加bin目录;
-
spring cloud config 的 docker 部署 一些注意事项
-
springcloud config 在本地测试正常 但是部署docker容器后 无法正确访问到Git上的配置文件
-
docker容器与宿主机定义的hosts不是共享的
-
解决方案使用 docker exec -it [eureka容器id] bash 进入eureka注册中心[config 配置中心]的docker容器中, 然后使用cat /etc/hosts命令查看容器的IP地
spring: cloud: config: name: eureka # 配置文件的前缀名 profile: dev # 配置文件的后缀名 label: master # Git 主分支 uri: http://172.17.0.4:12000 # 修改eureka 的bootstrap.yml 文件中uri的宿主机ip地址 改成从config容器中查出的ip地址
-