debug:
测试全局异常时自定义异常AyhException为null
com.aiyouhui.common.advice.CommonExceptionHandler
@ControllerAdvice
public class CommonExceptionHandler {
@ControllerAdvice aop切面注解类要在启动类AyhItemApplication的同级及子包下才能扫描到,否则自定义异常AyhException为null
com.aiyouhui.AyhItemApplication
Vue父子组件通信
我们定义一个子组件:
const myList = {
template:'\
<ul>\
<li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
</ul>\
',
props:{ // 通过props来接收父组件传递来的属性
items:{// 这里定义items属性
type:Array,// 要求必须是Array类型
default:[] // 如果父组件没有传,那么给定默认值是[]
}
}
}
这个子组件可以对 items 进行迭代,并输出到页面。
但是组件中并未定义items属性。
通过props来定义需要从父组件中接收的属性
items:是要接收的属性名称
type:限定父组件传递来的必须是数组,否则报错
default:默认值
我们在父组件中使用它:
<div id="app">
<h2>已开设如下课程:</h2>
<!-- 使用子组件的同时,传递属性,这里使用了v-bind,指向了父组件自己的属性lessons -->
<my-list :items="lessons"/>
var app = new Vue({
el:"#app",
components:{
myList // 当key和value一样时,可以只写一个
},
data:{
lessons:[
{id:1, name: 'java'},
{id:2, name: 'php'},
{id:3, name: 'ios'},
]
}
})
父向子的通信 通过中间属性域props
num: {{num}}
子组件接收父组件的num属性
子组件定义点击按钮,点击后对num进行加或减操作
子向父的通信
通过v-on指令将父组件的函数绑定到子组件上:
<div id="app">
<h2>num: {{num}}</h2>
<counter :count="num" @inc="increment" @dec="decrement"></counter>
然后,当子组件中按钮被点击时,调用绑定的函数:
Vue.component("counter", {
template:'\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">减</button> \
</div>',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
vue提供了一个内置的this.$emit函数,用来调用父组件绑定的函数
项目架构:
- 后台管理系统使用vue.js框架建出单页应用(SPA)
SPA:single page application。整个后台管理系统只出现一个html页面,页面内容都由vue.js实现。 - webpack:前端资源打包工具,将js,image,css等资源当做一个模块打包
webpack的作用:
将许多小碎文件打包成整体,减少单页面内的衍生请求次数,提高网站效率
将es6等高级语法进行转换编译,已兼容浏览器
打包时将代码混淆,提高安全性 - vue-cli:快速搭建vue项目的脚手架
安装:npm install -g vue-cli
建立static web mnodule: vue-cli-demo
终端 cd vue-cli-demo, 建立webpack项目 vue init webpack
1 use ESline之前的选择都选择yes,最后一个选择yes use npm
- 安装好之后,npm run dev 运行
**
结构
**
build下是webpack的配置,里面定义了webpack入口entry等等属性
src
1.main.js是入口entry,里面import了许多其他组件
2 写的vue文件就是一个组件 分为3部分 <template> <script> <style>
3 route对应的是相应的组件的路径
4 menu.js:对应的菜单
config:
1 index.js里面配置了端口
dist: 打包好的目录,打包src
node_modules: 所有的依赖
根目录:
1 index.html是网站的原始页面,
2 package.json是写引用的依赖文件
有3个脚本,dev,start,build(构建,运行后会把src打包到dist)
Vuetify框架
1 基于vue的ui框架,写css样式。类似bootstrap。
建立项目(后台):
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
<mysql.version>5.1.32</mysql.version>
<pageHelper.starter.version>1.2.5</pageHelper.starter.version>
<mapper.starter.version>2.0.3</mapper.starter.version>
<fastDFS.client.version>1.26.1</fastDFS.client.version>
<test.version>2.0.6.RELEASE</test.version>
</properties>
<dependencyManagement> <!-- 用来管理版本不下载jar-->
<dependencies>
<!-- cloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 通用mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mapper.starter.version}</version>
</dependency>
<!-- 分页助手-->
<dependency>
<groupId>com.github.pagehekper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pageHelper.starter.version}</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- fastDFS-->
<dependency>
<groupId>com.github.taobao</groupId>
<artifactId>fastDFS-client</artifactId>
<version>${fastDFS.client.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${test.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies><!-- 实际下载jar-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
1 1.1 新建project:artifact :leyou griopid :com.leyou.parent 引入依赖
1 建立通用module
1 注册中心 ly-registry com.leuyou.common 并写好依赖,启动类和配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class LyRegistry {
server:
port: 10086
spring:
application:
name: ly-registry
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: false
fetch-registry: false
2 网关 ly-gateway com.leyou.common 并写好依赖,启动类和配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringCloudApplication
@EnableZuulProxy
public class lYGateway {
server:
port: 10010
spring:
application:
name: api-gateway
eureka:
client:
# registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: http://127.0.0.1
instance-id: ${spring.application.name}.${server.port}
zuul:
prefix: /api # 添加路由前缀
# routes:
# user-service: # 这里是路由id,随意写
# path: /user-service/** # 这里是映射路径
# service-id: user-service # 指定服务名称
# retryable: true #重试
ribbon:
ConnectTimeout: 1000 # 连接超时时间(ms)
ReadTimeout: 2500 # 通信超时时间(ms)
MaxAutoRetriesNextServer: 0 # 同一服务不同实例的重试次数
MaxAutoRetries: 0 # 同一实例的重试次数
# OkToRetryOnAllOperations: true # 是否对所有操作重试
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 6000 # 熔断超时时长:6000ms
1.2微服务结构:
1 将来肯定会有其它系统需要来调用服务中提供的接口,因此肯定也会使用 到接口中关联的实体类。因此这里我们需要使用聚合工程,将要提供的接口及相关实体类放到独立子工程中,以后别人引用的时候,只需要知道坐标即可。
maven deploy到私服 就可以给别人引用
2 在ly-item中创建两个子工程:
ly-item-interface:主要是对外暴露的接口及相关实体类
ly-item-service:所有业务逻辑及内部使用接口
3 写好依赖,启动类和配置,并在网关gateway配置文件里添加路由routes
依赖有
1starter-web启动器
2 mybatis启动器 3中包含
3通用mapper启动器
4分页助手启动器
5连接池,我们用默认的Hykira 3中包含
6 mysql驱动
7 Eureka客户端
8千万不能忘了,我们自己也需要ly-item-interface中的实体类
@SpringBootApplication
@EnableDiscoveryClient
public class AyhItemApplication {
server:
port: 8081
spring:
application:
name: ly-item-server
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/test
hikari:
maximum-pool-size: 30
minimum-idle: 10
eureka:
client:
service-url: ,
defaultZone: http://127.0.0.1:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
prefer-ip-address: true
ip-address: 127.0.0.1
instance-id: ${spring.application.name}:${server.port}
到gateway配
zuul:
prefix: /api # 添加路由前缀
# retryable: true # c重试
routes:
ly-item-server: # 这里是路由id,随意写
3.10.测试路由规则
为了测试路由规则是否畅通,我们是不是需要在item-service中编写一个controller接口呢?
其实不需要,Spring提供了一个依赖:actuator
只要我们添加了actuator的依赖,它就会为我们生成一系列的访问接口:
/info
/health
/refresh
...
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
因为我们没有添加信息,所以是一个空的json,但是可以肯定的是:我们能够访问到item-service了。
接下来我们通过路由访问试试,根据路由规则,我们需要访问的地址是:
http://127.0.0.1:10010/api/item/actuator/info
1.3新建一个通用的基础服务ly-common,放微服务共享的工具类。deploy私服任何人都能下载
1 新建包com.leyou.common.utils放入工具类
2 JsonUtils的用法复杂类型转换List<Map<String, String>>
String json="[{\"name\":\"zhangsan\",\"gender\":\"man\" },{\"name\":\"lisi\",\"gender\":\"man\" }]";
List<Map<String, String>> maps = nativeRead(json, new TypeReference<List<Map<String, String>>>() {
});
1.4通用异常处理
REST风格规范!
uo
yi
异常
注意:
``****com.aiyouhui.common.advice.CommonExceptionHandler
@ControllerAdvice
public class CommonExceptionHandler {
@ControllerAdvice aop切面注解类要在AyhItemApplication的同级及子包下才能扫描到,否则AyhException为null
com.aiyouhui.AyhItemApplication**
1 建立通用的异常处理工具类
1 注意:需引入webmvc依赖 并且在service微服务中引入common服务的依赖
2 注意:service服务启动类在com.leyou下,异常处理类在com.leyou下的包所以能扫描到
2 处理的方法
@PostMapping
public ResponseEntity<Item> saveItem(Item item) {
if (item.getPrice()==null){
//return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
throw new AyhException(ExceptionEnum.PRICE_NOT_BE_NULL);
}
item = itemService.saveItem(item);
return ResponseEntity.status(HttpStatus.CREATED).body(item);
}
抛出异常
@PostMapping
public ResponseEntity<Item> saveItem(Item item) {
if (item.getPrice()==null){
//return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
throw new AyhException(ExceptionEnum.PRICE_NOT_BE_NULL);
}
item = itemService.saveItem(item);
return ResponseEntity.status(HttpStatus.CREATED).body(item);
}
捕获和处理异常
@ControllerAdvice
public class CommonExceptionHandler {
@ExceptionHandler(AyhException.class)
public ResponseEntity<ExceptionResult> handleException(AyhException e) {
// return ResponseEntity.status(HttpStatus.CREATED).body(e.getMessage());
//ExceptionEnum em = e.getExceptionEnum();
//return ResponseEntity.status(em.getStatus()).body(em.getMessage());//字符串
return ResponseEntity.status(e.getExceptionEnum().getStatus())
.body(new ExceptionResult(e.getExceptionEnum()));//实体对象
}
}
创建由枚举组成异常
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class AyhException extends RuntimeException {
private ExceptionEnum exceptionEnum;
}
创建异常枚举
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnum {
PRICE_NOT_BE_NULL(400, "价格不能为空!"),
USERNAME_NOT_BE_NULL(400, "用户名不能为空!"),
;
int status;
String message;
}
创建异常结果实体
import java.util.Date;
@Data
public class ExceptionResult {
private int status;
private String message;
private Date timestamp;
public ExceptionResult(ExceptionEnum em) {
this.status = em.getStatus();
this.message = em.getMessage();
this.timestamp = new Date(System.currentTimeMillis());
}`
}**``