更好的阅读体验请前往鄙人博客
闪闪の小窝——传送门
Spring Boot是Spring社区较新的一个项目。该项目的目的是帮助开发者更容易的创建基于Spring的应用程序和服务,让更多人能更快的对Spring进行入门体验,让Java开发也能够实现Ruby on Rails那样的生产效率。为Spring生态系统提供了一种简约的、约定优于配置风格的框架。
了解有哪些依赖组件可用:https://docs.spring.io/spring-boot/docs/2.5.2/reference/html/using.html
是@Controller和@ResponseBody的合集,表示这是个控制器 bean。
这个注解是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。
@SpringBootApplication 是 @Configuration 、@EnableAutoConfiguration、@ComponentScan注解的集合。
启用 SpringBoot 的自动配置机制。
是一个类级别的注释,它指示此类提供了应用程序配置。
通过设置prefix属性找到配置文件中的对应的key 取到value值,从而赋值给对应的属性。
#自定义key=value
school.name=厦门大学
school.website=www.xmu.com
school.address=厦门
@Component
@ConfigurationProperties(prefix = "school")
public class SchoolInfo {
private String name;
private String website;
private String address;
//getter setter
//toString
}
在IDE中直接直接执行main方法,然后访问http://localhost:8080即可
用maven打包为可执行jar包,然后执行java -jar xxx.jar
mvn spring-boot:run
这些方式优先级如下:
k:
k1: v1
k11: v11
k2: v2
REST ( REpresentational State Transfer ),State Transfer 为 “状态传输” 或 "状态转移 “,Representational 中文有人翻译为"表征”、“具象”,合起来就是 “表征状态传输” 或 “具象状态传输” 或 “表述性状态转移”。
REST是一种架构风格,REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性。
仅有网站的路径,采用网站的多级路径携带参数信息 例如:
传统风格:
a/b/1.html
a/b/add.action?param1=123¶m2=456
rest风格:
a/b/123/456/add.action
(如果过滤器配成/*,就可以支持add/123/456)
这也是REST的核心,即状态转化:使用这种机制可以达到相同的路径,根据不同的请求方式,产生不同的效果。如:同样是“11/zhangsan/user”请求,用GET就可以是按条件查询,用PUT就可以是按条件修改等等。
这个状态指的是客户端与服务端交互时用于存储客户信息的作用域数据,一般用session存。但REST强调无状态,原因是,当下主流的项目都是前后端分离,前端和后端可能完全不在同一个服务里,那么作用域就毫无作用了。此时REST可以通过路径参数携带所有必要用户信息来发起请求,这样就不管谁对后端服务发起请求,后端服务都能够做出响应。从而突破了单一服务的限制,从此支持多服务与分布式场景,另外还提高了可用度
@Controller—>@RestController
@RequestMapping–>@GetMapping、@PostMapping、@PutMapping、@DeleteMapping
延承自SpringMVC的特性,支持路径参数:@PathVariable,JSON参数@RequestBody
@RestController
public class GoodsController {
@Autowired
private GoodsService goodsService;
@PostMapping("goods")
public JsonResult addGoods(@Valid Goods goods,BindingResult br) {
if(br.hasErrors()) {
throw new MyException(ErrorEnums.CHECK_ERROR, br.getFieldError().getDefaultMessage());
}else {
goodsService.addGoods(goods);
return new JsonResult("添加成功");
}
}
@PutMapping("goods")
public JsonResult editGoods(Goods goods) {
goodsService.editGoods(goods);
return new JsonResult("修改成功");
}
@DeleteMapping("goods/{gid}")
public JsonResult removeGoods(@PathVariable int gid) {
goodsService.removeGoods(gid);
return new JsonResult("删除成功");
}
@GetMapping("goods/less/{price}/{type}")
public JsonResult<Goods> findGoods(@PathVariable double price,@PathVariable String type){
GoodsExample ge = new GoodsExample();
ge.createCriteria().andTypeEqualTo(type).andPriceLessThan(price);
return new JsonResult<Goods>(goodsService.findByCondition(ge));
}
@GetMapping("goods/page/{pageNum}/{pageSize}")
public JsonResult<Goods> findAllGoods(@PathVariable int pageNum,@PathVariable int pageSize){
PageHelper.startPage(pageNum, pageSize);
return new JsonResult<Goods>(goodsService.findAll());
}
/*
* @GetMapping("goods") public JsonResult findAllGoods(){ return new
* JsonResult(goodsService.findAll()); }
*/
@GetMapping("goods")
public JsonResult<Goods> findAllGoods(int pageNum,int pageSize,String sort,String order,String gname,String type){
PageHelper.startPage(pageNum, pageSize, sort+" "+order); //sort+" "+order ===> cname desc
GoodsExample ge = new GoodsExample();
ge.createCriteria().andGnameLike("%"+gname+"%").andTypeLike("%"+type+"%");
PageInfo<Goods> pagedata = new PageInfo<Goods>(goodsService.findByCondition(ge));
return new JsonResult<Goods>(pagedata);
}
}
package com.xxx.advice;
@Aspect
@Component
public class HttpAspect {
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
@Pointcut("execution(public * com.etc.controller.*.*(..))")
public void log() {}
@Before("log()")
public void logbefore(JoinPoint point) {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//url
logger.info("url={}",request.getRequestURI());
//method
logger.info("method={}",request.getMethod());
//IP:Port
logger.info("IP:PORT={}",request.getRemoteAddr());
//调用的服务
logger.info("Class_Method={}",point.getSignature().getDeclaringTypeName()+"."+point.getSignature().getName());
//传递的参数
Object[] args = point.getArgs();
String argsStr = "";
for(Object arg : args) {
argsStr+=arg.toString();
}
logger.info("args={}",argsStr);
}
@AfterReturning(pointcut = "log()", returning = "ret")
public void logAfterRet(Object ret) {
logger.info("response={}",ret.toString());
}
}
package com.xxx.exception.handle;
@ControllerAdvice
public class ExceptionHandle {
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
@ExceptionHandler(value=Exception.class)
@ResponseBody
public JsonResult handle(Exception e) {
logger.error("【系统异常】:",e);
return new JsonResult(-1,"【系统异常】:"+e.getMessage());
}
@ExceptionHandler(value=MyException.class)
@ResponseBody
public JsonResult myHandle(MyException e) {
logger.error("【操作异常】:",e);
return new JsonResult(e.getCode(),"【操作异常】:"+e.getMessage());
}
}
package com.xxx.entity;
public class JsonResult<T> {
public static final int SUCCESS=0;
private int state;
private String message = "";
private T data;
private List<T> datas;
//分页特化处理
private PageInfo<T> pagedatas;
//total和rows是专门个bootstrap table做适配用的
private long total;
private List<T> rows;
//处理字符串数据
public JsonResult(String message) { //正常情况
this.state = SUCCESS;
this.message = message;
}
public JsonResult(int state,String message) { //异常情况
this.state = state;
this.message = message;
}
//处理对象数据
public JsonResult(T data) {
this.state = SUCCESS;
this.data = data;
}
public JsonResult(String message,T data) {
this.state = SUCCESS;
this.message = message;
this.data = data;
}
public JsonResult(int state,String message,T data) {
this.state = state;
this.message = message;
this.data = data;
}
//处理集合数据
public JsonResult(List<T> datas) {
this.state = SUCCESS;
this.datas = datas;
}
public JsonResult(String message,List<T> datas) {
this.state = SUCCESS;
this.message = message;
this.datas = datas;
}
public JsonResult(int state,String message,List<T> datas) {
this.state = state;
this.message = message;
this.datas = datas;
}
//处理分页数据
public JsonResult(PageInfo<T> pagedatas) {
this.state = SUCCESS;
this.pagedatas = pagedatas;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public List<T> getDatas() {
return datas;
}
public void setDatas(List<T> datas) {
this.datas = datas;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public PageInfo<T> getPagedatas() {
return pagedatas;
}
public void setPagedatas(PageInfo<T> pagedatas) {
this.pagedatas = pagedatas;
}
public long getTotal() {
return pagedatas!=null?pagedatas.getTotal():0;
}
public List<T> getRows() {
return pagedatas!=null?pagedatas.getList():null;
}
}
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模板引擎类似,也可以轻易地与 Spring MVC 等 Web 框架集成。
Thymeleaf 是新一代 Java 模板引擎,支持 HTML 原型,以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果。当在应用程序中会动态地替换掉页面设置的标签属性。
与其它模板引擎相比,Thymeleaf 最大的特点是,即使不启动 Web 应用,也可以直接在浏览器中打开并正确显示模板页面 。
它用于显示控制器传入的name值
如果name不存在,要显示默认值,则使用一下代码
局部变量,th:with能定义局部变量:
<div th:with="firstPer=${persons[0]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesarspan>.
p>
div>
它用于接收后台传过来的对象,如以下代码:
下方代码演示了if判断,表示:如果从控制器传来的role值等于"admin",则显示”欢迎您,管理员“;如果role值等于”vip“,则显示”欢迎您,vip会员“
<div th:if="${role} eq admin">
<span>欢迎您,管理员span>
div>
<div th:if="${role} eq vip">
<span>欢迎您,vip会员span>
div>
<table>
<thead>
<tr>
<th>序号th>
<th>用户名th>
<th>密码th>
<th>用户昵称th>
tr>
<tr th:each="user:${userlist}">
<td th:text="${user.id}">td>
<td th:text="${user.username}">td>
<td th:text="${user.password}">td>
<td th:text="${user.petname}">td>
tr>
thead>
table>
${…}:变量表达式
*{…}:选择表达式
#{…}:消息表达式(i18n)
@{…}:链接表达式(URL)
~{…}:片段表达式
string是redis最基本的类型。一个key对应一个value。
单键 —— 单值
在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为redis.net.cn。注意:一个键最大能存储512MB。
Redis列表是简单的字符串列表,按照插入顺序排序。你可以从头部(左边)或者尾部(右边)添加一个元素导列表。
单键——多值
常用案例:聊天系统、社交网络中获取用户最新发表的帖子、简单的消息队列、新闻的分页列表、博客的评论系统。
hash 是一个键值(key=>value)对集合。
KV模式不变,但是V是一个键值对(适合用来存java中的对象数据)
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。可以避免序列化的开销和并发修改控制的问题。
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HGETALL 命令,user:1 为键值。
set key value设置指定 key 的值[如果有键,则自动覆盖]
get key 获取指定 key 的值
del key 删除指定key
getset key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
incr key : 将键的整数值增一【只有数字才能进行加减】
decr key: 将键的整数值减少-1
INCRBY key increment 将 key 所储存的值加上给定的增量值(increment) 。
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
RPUSH key value1 [value2] 在列表中添加一个或多个值
LPOP key 移出并获取列表的第一个元素
RPOP key 移除并获取列表最后一个元素
hset key field value (field相当于类的属性)将哈希表 key 中的字段 field 的值设为 value
hget key field 获取存储在哈希表中指定字段的值
hmset key field1 value1 [field2 value2 ]同时将多个 field-value (域-值)对设置到哈希表 key 中。
hmget key field1[field2] 获取所有给定哈希字段的值
1.在pom.xml中配置依赖:
redis.clients
jedis
2.创建一个redis连接:
//也可以使用连接池
Jedis jedis=new Jedis("127.0.0.1", 6379);
3.写入一个字符串String;
jedis.set("key1", "string-value1");
String value1 = jedis.get("key1");
//设置key的时间 单位秒
jedis.expire("key1", 60);
// 打印string-value1
System.out.println(value1);
// key不存在则返回 null
System.out.println(jedis.get("key1"));
4.写入一个hash:
HashMap hashMap = new HashMap();
hashMap.put("name", "zhang-");
hashMap.put("age", "36");
jedis.hmset("user", hashMap);
jedis.expire("user", 20);
//获取按map中的key来获取数据,得到一个list
List hmget = jedis.hmget("user", "name", "age");
System.out.println(hmget);
//判断hashMap中是否存在某个字段
Boolean isExist = jedis.hexists("user", "name");
System.out.println(isExist);
//删除某个字段
jedis.hdel("user", "name");
//获取整个hashMap
Map map = jedis.hgetAll("user");
System.out.println(map);
5.写入一个list(列表)
// 在头部写入数据,列表数据是 [name2, name1]
jedis.lpush("user1", "name1", "name2");
// 在尾部写入数据,列表数据是[name1, name2]
jedis.rpush("user1", "name1", "name2");
// 按索引来获取数据
jedis.lindex("key", 1);
// 获取列表的长度
long length = jedis.llen("user2");
System.out.println(length);
6.无序set操作:
// 创建一个set
jedis.sadd("set1", "value1");
jedis.sadd("set1", "value2");
jedis.sadd("set1", "value3");
// 获取整个set
Set set1 = jedis.smembers("set1");
// 移出某个value
jedis.srem("set1", "value2");
// 判断是否存在该value
boolean sismember = jedis.sismember("set1", "value2");
7.有序set操作:
// 有序set
jedis.zadd("set2", 1, "value1");
jedis.zadd("set2", 10, "value10");
jedis.zadd("set2", 11, "value11");
jedis.zadd("set2", 9, "value9");
jedis.zadd("set2", 5, "value5");
// 获取set的长度
Long set21 = jedis.zcard("set2");
System.out.println(set21);
// 获取set的片段
Set set2 = jedis.zrange("set2", 0, 10);
System.out.println(set2);
Spring cloud是一个开发工具集,含多个子项目
Spring cloud 简化了分布式开发
Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量
问题1:分布式系统中为什么需要注册中心?
问题2:服务多的时候,如何找到最适合我的服务?
问题3:客户端和服务端所用语言不同怎么办?
问题4:原有项目如何拆分成微服务项目?
问题5:哪些项目不适合演进成微服务架构?
application.yml
eureka:
client:
registerWithEureka: false
fetchRegistry: false
application-e1.yml
server:
port: 8761
eureka:
instance:
hostname: eureka-1
client:
service-url:
defaultZone: http://eureka-2:8762/eureka/
application-e2.yml
server:
port: 8762
eureka:
instance:
hostname: eureka-2
client:
service-url:
defaultZone: http://eureka-1:8761/eureka/
AppClient
package com.xxx.controller;
@RestController
public class ClientController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("getmsg")
public String getMsg(String name) {
//1 第一种方式
//RestTemplate temp = new RestTemplate();
//String response = temp.getForObject("http://localhost:9091/hello?name="+name, String.class);
//2 第二种方式
String response = restTemplate.getForObject("http://APPSERVICE/hello?name="+name, String.class);
return response;
}
@PostMapping("goods")
public String addGood(@Valid Goods goods) throws Exception{
// System.out.println(goods.getGname());
String response = restTemplate.postForObject("http://APPSERVICE/goods",goods, String.class);
return response;
}
@PutMapping("goods")
public void editGoods(Goods goods) {
//@PathVariable int gid,@PathVariable String gname,@PathVariable String type,@PathVariable double price
// restTemplate.put("http://APPSERVICE/goods/"+gid+"/"+gname+"/"+type+"/"+price, String.class);
restTemplate.put("http://APPSERVICE/goods",goods);
}
@DeleteMapping("goods/{gid}")
public void removeGoods(@PathVariable int gid) {
restTemplate.delete("http://APPSERVICE/goods/"+gid, String.class);
}
@GetMapping("goods/{gid}")
public Goods findGood(@PathVariable int gid) {
Goods response = restTemplate.getForObject("http://APPSERVICE/goods/"+gid, Goods.class);
return response;
}
// @RequestMapping("goods/less/{price}/{type}")
// public List findGoods(@PathVariable double price,@PathVariable String type) {
// List response = restTemplate.getForObject("http://APPSERVICE/goods/less/"+price+"/"+type, List.class);
// return response;
// }
}
AppService
@RestController
public class GoodsController {
@Value("${server.port}")
private int port;
@Autowired
private GoodsService goodsService;
@PostMapping("goods")
public String addGoods(@RequestBody Goods goods,BindingResult br) {
if(br.hasErrors()) {
return br.getFieldError().getDefaultMessage();
}else {
System.out.println(goods.getGname());
System.out.println(goods.getGname());
System.out.println(goods.getGname());
System.out.println(goods.getGid());
goodsService.addGoods(goods);
return "添加成功";
}
}
@PutMapping("goods")
public String editGoods(@RequestBody Goods goods) {
goodsService.editGoods(goods);
return "修改成功";
}
@DeleteMapping("goods/{gid}")
public String removeGoods(@PathVariable int gid) {
goodsService.removeGoods(gid);
return "删除成功";
}
@GetMapping("goods/less/{price}/{type}")
public List<Goods> findGoods(@PathVariable double price,@PathVariable String type){
GoodsExample ge = new GoodsExample();
ge.createCriteria().andTypeEqualTo(type).andPriceLessThan(price);
return goodsService.findByCondition(ge);
}
@GetMapping("goods/{gid}")
public Goods findGood(@PathVariable int gid){
return goodsService.findByGid(gid);
}
// @GetMapping("goods/page/{pageNum}/{pageSize}")
// public List findAllGoods(@PathVariable int pageNum,@PathVariable int pageSize){
// PageHelper.startPage(pageNum, pageSize);
// return goodsService.findAll();
// }
}
AppClientApplication
package com.xxx;
@EnableEurekaClient
@SpringBootApplication
public class AppClientApplication {
public static void main(String[] args) {
SpringApplication.run(AppClientApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
package com.xxx;
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignClientApplication {
public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class, args);
}
}
package com.xxx.client;
@FeignClient("APPSERVICE")
public interface HelloClient {
@RequestMapping("hello")
public String sayHello(@RequestParam("name") String name);
}
package com.xxx.controller;
@RestController
public class ClientController {
@Autowired
private HelloClient helloClient;
@RequestMapping("msg")
public String getMsg(String name) {
String response = helloClient.sayHello(name);
return response;
}
@RequestMapping("/hi")
public String sayHi() {
return "hi";
}
}
application.yml
server:
port: 8888
spring:
application:
name: appclient
eureka:
client:
service-url:
defaultZone: http://eureka-1:8761/eureka/
application.yml
eureka:
client:
service-url:
defaultZone: http://eureka-1:8761/eureka/
spring:
application:
name: appservice
datasource:
url: jdbc:mysql://localhost:3306/shop?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
#config-location: classpath:*.xml
mapper-locations: classpath:com/etc/mapper/xml/GoodsMapper.xml
application-as1.yml
server:
port: 9091
application-as2.yml
server:
port: 9092
application-as3.yml
server:
port: 9093
application.yml
eureka:
client:
registerWithEureka: false
fetchRegistry: false
application-e1.yml
server:
port: 8761
eureka:
instance:
hostname: eureka-1
client:
service-url:
defaultZone: http://eureka-2:8762/eureka/
application-e2.yml
server:
port: 8762
eureka:
instance:
hostname: eureka-2
client:
service-url:
defaultZone: http://eureka-1:8761/eureka/
单选:10-15题 (每题2分)
多选:5-8题(每题3分)
判断:0-10题(每题1分)
简答:6-8题(每题6分)
编程:1-2题(每题10分)