课程笔记

01-建立开发环境

前后端不分离->前后端分离(均部署在后端服务器)->微服务架构

  • 单体应用程序:单个可执行的应用程序

  • 随着云计算的发展,单体不够,分解成小颗粒->微服务:

    • 微服务运行时可以部署在不同服务器,协作完成;一个完整业务逻辑跨越多个微服务(分布式计算技术)
    • 通信:HTTP,与语言无关,适合协同

开发框架:可被应用开发者定制的应用骨架,方便开发

  • 层次不断提高:Spring->SpringBoot->SpringCloud(容器/微服务/DevOps)
    • RUOYI基于SpringBoot

Spring不可避免要学习,是Java生态圈的主流开发框架

  • 非侵入性:其开发出来的软件,可以轻松脱离Spring:解耦
  • 容器:bean:java对象。bean开发出来后给Spring,Spring帮助创建bean对象和依赖关系(蓝色方块)image-20230214195526166
  • AOP:面向切面编程,可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
  • 持久层

jdk版本推荐:8/11/17

Jar和War的区别

  • Jar是与平台无关的文件格式,它允许将许多文件组合成一个压缩文件
  • War是一个可以直接运行的web模块,通常用于网站,打成包部署到容器中

Spring常用依赖:

  • Spring Boot DebTools
  • Spring Web

tomcat/servlet/maven了解一下

加starter后会自动下载依赖的包,并维护所需版本

properties/yml格式均可,yml更常用

编译后放在classes文件夹下

@SpringBootApplication程序入口,进入

  • @EnableAutoConfiguration作用:帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建对应配置类的Bean,并把该Bean实体交给IoC容器进行管理。
  • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
    • 会搜索@Controller注解的bean,扫描类

image-20230214205839667

省去了业务层和数据访问层

先到控制器找到/,获得视图的名字home,拼接出前端的视图路径,找到templates下面的home.html

devtools依赖:开发时就可以随时println之类的

如何实时刷新:分别在vscode和页面安装LiveReload

image-20230214211910458

mvn spring-boot:run 快速启动程序

initializer:可以用官网下载zip,也可以用vscode插件

image-20230301110202581

devtools:一边写代码,一边看到效果

starter-web:实现Web场景开发,提供了嵌入的Tomcat以及Spring MVC的依赖。

starter-thymeleaf:前后端不分离的页面渲染。与Spring MVC等Web框架进行集成作为Web应用的模板引擎。能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。

02-依赖注入

考试不考:另一种方式叫IoC,控制反转

通过某种方式告诉spring我们的意图

image-20230301111214198

  1. 自动化配置:在代码里加注释
  2. JavaConfig:Bean注解
  3. XML配置(现在没人用)

image-20230301111443425

有一种光盘,要放入媒体播放器

@Component注解:告诉Spring有可能会创建类的实例,接口的实现

@ComponentScan注解:明确告诉Spring在当前类对应的包和相应的子包中搜索Component,如果有就自动创建实例对象

@Configuration注解:告诉Spring类要做配置,用ComponentScan组装

@AutoWired:需要在应用上下文里实现了CompactDisc的组件,这就建立了两个对象的依赖关系,通过构造方法,AutiWired告诉Spring进行注入

1
2
3
4
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}

怎么看是什么版本的jdk?

  • ctrl+shift+s选择user setting,输入runtimes,然后会跳转到settings.json,可以指定jdk版本

全程没有主动的new类,而是spring帮忙

注入:依赖的是接口,而不是实际的new对象,为了好修改,类不必修改

对组件的命名是采用的类的名字,如CDPlayer

1
MediaPlayer player = ctx.getBean(MediaPlayer.class);//这里理论上可以写类的名字,如CDPlayer,但我会报错

也可以自定义Component的名字

1
@Component("MyComponent")

自动化配置

@Autowired

  1. 用在构造器

    1
    2
    3
    4
    @Autowired
    public CDPlayer(CompactDisc cd) {
    this.cd = cd;
    }
  2. 用在setter方法

    1
    2
    3
    4
    @Autowired
    public void setCd(CompactDisc cd) {//右键generate setter
    this.cd = cd;
    }
  3. 直接加在私有成员

    1
    2
    @Autowired
    private CompactDisc cd;

JavaConfig

使用bean注释,并创建new对象

junit是java常用的测试库

main:功能代码 test:测试代码

在pom.xml中加依赖

1
2
3
4
5
6
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

XML装配

通过xml告诉bean组装的意图

1
2
3
4
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>

可以通过改class,指定整合的内容,匹配compactDisc接口;相互之间并不是直接关联的

混合配置

一部分注解用bean,一部分注解用component,xml

其他

@profile

开发环境:开发人员开发

测试环境:测试人员测试

生产环境:用户来用

@profile(“dev”)名字可以随便起

1
2
@Profile("dev")//开发环境,一开始就实例化出来
@Profile("prod")//只在生产环境下才实例化出来,所以并不是同时实例化

有两个datasource,使用哪个?下面一行可以实例化dev

1
@ActiveProfiles("dev")

@Conditional

1
2
3
4
5
6
7
8
@Conditional(MagicExistsCondition.class)//在某种情况下才实例化 参数是条件(这里是类实现)

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//上下文的信息,定义了哪些bean/bean的属性/关心的bean存不存在,注解的元数据(方法有哪些注解,是否存在)
Environment env = context.getEnvironment();
return env.containsProperty("magic");//获得返回值true/false
}

自动装配的歧义性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Autowired 如果有多个CompactDisc实例对象,不知道该挑哪个
public void setCd(CompactDisc cd) {//右键generate setter
this.cd = cd;
}

@Component
@Primary
public class SgtPeppers implements CompactDisc {// CD唱片接口的实现 这样就可以提升参数优先级
}

@Qualifier("abc")//还可以起名字,然后把同样的语句加在set方法处

@Autowired //如果有多个实例对象,不知道该挑哪个
@Qualifier("abc")//起名字
public void setCd(CompactDisc cd) {//右键generate setter
this.cd = cd;
}

自定义注解:如@Cold,继承自@Qualifier

Bean的作用域

image-20250204132849182

prototype:每次依赖注入,获取上下文,都会创建一个新的bean实例

session:包含客户端与服务端的多次的交互,整个过程是一个session,如ShoppingCart没结束

request:一次请求

通过代理注入给单例对象

注入的对象是代理对象,是个假的,会不同委托给不同接口

03-面向切面编程(AOP)

一个bean:实例化对象。bean之间有依赖关系,如何建立?

  1. 自动化配置
    1. @Component:告诉Spring这里要创建实例
    2. @Autowired把另一个对象的引用注入到里面
      1. 通过构造方法,到容器上下文里面去找
      2. 通过get/set方法
      3. 直接加载private属性上面,spring会帮忙做初始化
    3. @ComponentScan:告诉在哪个包的路径下搜(在当前类对应的包和相应的子包中搜索Component,如果有就自动创建实例对象)
  2. JavaConfig
    1. 加@Configuration注解,告诉Spring类要做配置,用ComponentScan组装

image-20250204132913502

image-20250204132921613

AOP解决的问题:在concert前后插入一些逻辑;其他代码都不动!

切入逻辑:advice,在before/after中

面向切面的编程:不用对业务代码做任何修改

过程

  1. 定义切面类Audience.java @Aspect 很多advice(一个before/after就是一条advice)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Aspect//提示这是一个切面!
    public class Audience {
    @Before("execution(* concert.Performance.perform( .. ))")//包路径.接口.方法 ..表示不在意参数有多少个 *表示不关心返回值是什么
    //在被切入的方法调用之前,把当前方法逻辑执行
    //这里指在perform()之前,调用silenceCellPhones()
    public void silenceCellPhones() {
    System.out.println("Silencing cell phones");
    }

    @Before("execution(* concert.Performance.perform( .. ))")
    public void takeSeats() {
    System.out.println("Taking seats");
    }

    @AfterReturning("execution(* concert.Performance.perform( .. ))")
    //在applause()方法正常返回的时候切入
    public void applause() {
    System.out.println("CLAP CLAP CLAP!!!");
    }

    @AfterThrowing("execution(* concert.Performance.perform( .. ))")
    //在demandRefund()方法异常时切入
    public void demandRefund() {
    System.out.println("Demand a refund");
    }
    }
  2. 在config中将切面的实例化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Configuration
    @EnableAspectJAutoProxy //开启AspectJ的自动代理机制 提醒要用切面,对有切面需求的对象引用都使用代理
    public class ConcertConfig {//配置类,用于实例化bean
    @Bean
    public Performance concert() {
    return new Concert();
    }
    // @Bean
    // public Performance concert2() {
    // return new Concert();
    // }

    @Bean
    public Audience audience() { //定义Audience的bean
    return new Audience();
    }


    @Bean//需要实例化!
    public EncoreableIntroducer encoreableIntroducer() {
    return new EncoreableIntroducer();
    }

    或者使用自动化配置:在Audience类前加Component,在config类前加ComponentScan

  3. 加入注解@EnableAspectJAutoProxy,提醒要用切面,对有切面需求的对象引用都使用代理

image-20230301153558936

切点表达式

若每个切点表达式都是一样的,DRY原则:避免重复

  • 去重:

    1
    2
    3
    4
    5
    6
    7
    @Pointcut("execution(* concert.Performance.perform( .. ))")//把重复的切点表达式依附于一个方法,这样下面都可以复用
    public void performance() {
    }

    @Before("performance()")
    @AfterReturning("performance()")
    @AfterThrowing("performance()")

切点表达式当然可以切到不同的地方去

@Around:环绕,有点像把其他advice全合并集中了

切点指示器

  • 获取参数:args
  • 限定包路径:within
  • 限定bean名称:bean

引入接口

实现新的aspect切面,把新的切面进入,调用者获得代理对象

相当于切面就是原对象新的行为

1
2
3
4
5
6
@Aspect
public class EncoreableIntroducer {
@DeclareParents(value = "concert.Performance+",//后面的+表示应用到所有实现了该接口的Bean
defaultImpl = DefaultEncoreable.class)//增加的行为用DefaultEncoreable实现
public static Encoreable encoreable;//定义了接口的静态成员,可以依附于注解
}
1
2
3
4
@Bean//需要实例化!
public EncoreableIntroducer encoreableIntroducer() {
return new EncoreableIntroducer();
}

04-Web开发框架

AOP:解耦

利用@Before/@After来指定切入点

连接点:Spring只有方法这个连接点

使用代理对象引入新功能,调用者不知道是新对象还是旧对象,代理对象会管理

项目:在线订购系统(webapp前后端不分离)

image-20250204132943403

  • 程序分层
  • 控制器层:知道了用户要什么及格式,处理客户端的请求,满足客户端的响应
    • 控制器拿到数据后,很有可能返回JSON
    • 控制器得到模板和数据,经过第三方库,转化成html文档,在浏览器就会看到页面
  • 业务层:实现业务逻辑,处理控制器层的请求
  • 数据访问层:JDBC,写起来繁琐。OIM框架?如MyBatis

slf4j可以打印日志,有@Slf4j这个注解就相当于定义了static obj。

所以lombok简化代码书写

1
import lombok.extern.slf4j.Slf4j;

编译时,lombok帮助填写我们没填的代码;编译完了,lombok就没用了

使用@Data,lombok会帮忙生成get/set/equals/hashCode

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private String address;
private Integer age;
private String hobbit;
private String phone;
}

img

表示编译结束后,打包时,把lombok排除掉

1
2
3
4
5
6
7
#pom.xml
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>

localhost:8080/design发生什么

  1. 在controller中,@RequestMapping(“/design”)会配置请求的映射
  2. @GetMapping找到/design方法,因为是get,匹配到。返回字符串”design”。
  3. resources放资源,view放在resources/templates目录下,根据返回的字符串找到模板路径design.html
  4. thymeleaf根据模板、属性和对象渲染出浏览器可以使用的html文档
    1. 模板中所需的数据从哪里来?ingredient是控制器提供的对象,id、name这些属性可访问

点击提交按钮发生什么

  1. 在controller中,@RequestMapping(“/design”)会配置请求的映射
  2. @PostMapping找到processTaco方法。
    1. taco对象:服务端将前端输入的信息转成taco对象,使用Converter,把id转到Ingredient

会话是什么

服务端和客户端之间来来回回多趟,多次来往叫做会话session

1
@SessionAttributes("tacoOrder")//会话

tacoOrder对象在多次来往中,服务器一直维护着其存在

所以tacoOrder.addTaco(taco);可以存储一段时间内的多个taco对象

1
th:object="${tacoOrder}"

thymeleaf会得到对象,然后匹配各个属性值

校验

前后端分离的目的之一:减轻服务器的压力

1
2
import javax.validation.constraints.NotNull;//java验证的规范
import javax.validation.constraints.Size;
1
@Size(min=5, message="Name must be at least 5 characters long")

必须5个字符,否则在前端展示message字段作为错误信息

如何明确告诉spring要做校验?

@Valid注解会告诉spring进行校验(来自import javax.validation.Valid;)

如何将错误信息显示在浏览器

1
2
3
<span class="validationError"
th:if="${#fields.hasErrors('ingredients')}"
th:errors="*{ingredients}">Ingredient Error</span>

如果有错误,展示错误

1
2
3
public String processTaco(
@Valid Taco taco, Errors errors,
@ModelAttribute TacoOrder tacoOrder)

如果@Valid校验不通过,errors则不为空:如果校验不通过就转到design页面

@CreditCardNumber信用卡相关的校验

见TacoOrder.java

使用视图控制器

一个url进来会路由到控制器上

如果请求很简单,没啥逻辑,就不用路由到控制器上了:视图控制器

WebMvcConfigurer:接口有用,可以做相关配置

05-JDBC、JPA

现在基本前后端分离

数据的持久化:JDBC

示例1:jdbctemplate

一定要加这个模板

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

private JdbcTemplate jdbcTemplate;//模板 加了starter-jdbc依赖后,会帮忙写,只需提供变化的部分

jdbc:获得连接->写sql查询语句->执行查询语句

1
Repository是DAO层接口,表示持久化

mock工具,给一个接口,自动给接口的实现,不知道细节

image-20250204133006112

也可以用lambda表达式

初始化repo:在data.sql中初始化

一般在业务层访问repo(DAO层),在控制器转向业务层

控制器层处理request/get等请求

1
2
3
4
5
//JdbcOrderRepository.java
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();//获得id的渠道
jdbcOperations.update(psc, keyHolder);//真正插入的动作,往数据库插入一条数据
long orderId = keyHolder.getKey().longValue();
order.setId(orderId);//查询结果转换为的java对象,然后把获得的id赋值

很麻烦,order/taco/ingredient中要插入的数据、id都要自己指明,用下面的方法就很方便

示例2:sd-jdbc

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
  1. 定义接口:继承自CrudRepository。写一个接口就够了

image-20230308144003834

可以配置数据库的名字tacocloud(默认选项置为false),JDBC访问路径为jdbc:h2:mem:tacocloud

image-20230308144129623默认没有密码

执行schema.sql后产生了4张表image-20230308144257077

可以查询表的内容:

image-20230308144343291

加devtools依赖包:默认提供h2-console的访问,就可以及时验证数据库开发的结果

初始化repo:在dataloader中用java代码初始化

1
2
//@Table("mytable")//可以将当前的java对象对应到数据库的哪一张表
@Table //如果不写,或者只写@Table,当前Ingredient对象就对应数据库中的Ingredient表

示例3:data JPA

  1. 引入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  1. 写实体对象
  2. 写接口。实现由Spring自动实现。

jpa可以通过java对象,推出表的结构,所以schema.sql的文件省略了

image-20230308153621882

这个表可以表示TACO_ORDER和TACO一对多的关系

image-20230308153927760

接口中可以添加方法:根据名字,用邮编找到TacoOrder

作业的bug

  1. 关于持久化
1
@SessionAttributes("contactList")//持久化

注意这句话的意思是在本次session中这个attribute不变!所以如果写sessionStatus.setComplete();关闭session,list就不能持久化了,前端也就一次只有一个contact显示了。

  1. 关于页面显示:必须要写get方法
1
2
3
4
@GetMapping
public String showContact() {
return "home";
}

写WebConfig似乎没有用?删了也不影响

1
2
3
4
5
6
7
8
9
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}

}

复习:对数据的操作等同于对java对象的操作。

schema.sql放在class根路径下,spring会自动执行脚本,完成对表的创建

data.sql对数据库初始化,经常用于测试,常常放在test路径下

06 Spring Security

安全框架解决的问题:拒绝不怀好意的请求

加依赖

确认请求来自于谁:用户的身份->会跳转到login

image-20230309235719344用户身份的认证

SpringSecurity帮助的工作:

1
2
3
4
   <!-- tag::thAction[] 没有写login的代码,controller是SpringSecurity实现的-->
<!-- post请求会传username和password,发到服务端后SpringSecurity会认证-->
<!-- SecurityConfig中有UserDetails,然后把username传到loadUserByUsername方法中,返回UserDetails对象-->
<!-- 根据UserDetails对象可以进一步获得存到数据库中的password,然后和客户端传来的password做比较,如果一样则认证成功-->

用户认证后,接下来会不断地请求响应:session

浏览器和服务器如何维持会话

无连接:不同的请求是无连接的(不像socket)

服务端判断不了是否来自同一个用户,怎么办?cookie

客户端登录,服务端认证成功,服务端返回cookie,通过response返回,浏览器通过协议,将cookie存入浏览器。

当后面有请求时,浏览器自动将session-id作为cookie传回服务端。每次都会带着session-id。只要session-id不变,服务端就认为来自同一个用户。

例:注册时cookie不变,登录后服务端返回新的session-id,接下来每一次请求都用这个id不变

如何定义请求资源的访问权限

1
2
3
4
5
//SecurityConfig.java
return http
.authorizeRequests()//对请求做鉴权:2个url之一,针对design/orders,确认当前登录的用户是否属于USER角色
.mvcMatchers("/design", "/orders").hasRole("USER")
.anyRequest().permitAll()//除了上面2个url,其他的访问畅通无阻

哪里定义的USER?

1
2
3
4
5
//User.java
@Override//硬编码,让这些用户默认具有USER权限,上面2个url即可访问
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}

也可以加权限,如:.mvcMatchers(“/design”, “/orders”).hasAuthority(“read”).hasRole(“USER”)

实现方法级别的安全(taco-cloud)

控制器层的代码在好几个地方会调用到,可能授权好几个url:可以统一授权

1
2
3
4
5
@PreAuthorize("hasRole('ADMIN')")//方法级别的注解
public void deleteAllOrders() {
orderRepository.deleteAll();
}
//@EnableGlobalMethodSecurity 如果打开,可以实现方法级别的授权:PreAuthorize生效

获取当前用户的信息

方法一:jdk里有个库jaas:java认证与授权服务,更底层的框架(相对SpringSecurity),principal接口来自库

  • 不同认证方式可插入,如密码、指纹,添加少量代码即可修改
  • 提供解耦操作,便于未来的扩展
  • SpringSecurity更简洁

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@PostMapping
public String processOrder(@Valid TacoOrder order, Errors errors,
SessionStatus sessionStatus,
@AuthenticationPrincipal User user) {

if (errors.hasErrors()) {
return "orderForm";
}

order.setUser(user);//订单里的信息可以用user身份填充(就是注册时包括的信息)

orderRepo.save(order);
sessionStatus.setComplete();

return "redirect:/";
}

方法三:通过安全上下文

image-20230310004719387

总结:SecurityConfig.java配权限,或者用@PreAuthorize(“hasRole(‘ADMIN’)”)在方法上配,这是最重点的内容

docker

虚拟容器,底层基于linux操作系统

image-20230310004937286

VM:虚拟机,独立的操作系统。不同的VM和底层的宿主机完全独立。

CONTAINER:使用者可以过滤底层差异,看作VM。不同点:底层机制是共享操作系统内核,也基于宿主机。(创建代价低,类比新起了一个进程,轻量级)

image-20230310005129070

docker是一个软件,用于运行、管理容器

docker run通过客户端发往host。host在linux操作系统上,client可以在windows上。指令通过远程连接,发往docker engine

仓库:镜像放在这里

image-20230310005402601

docker软件在这里,获得的容器来源于Linux

image-20230310005426183

常用右边的

docker desktop会自动在本机创建VM(虚拟化出的linux)

在docker hub创建账号,存的是容器的镜像,可以下载很多软件。

容器技术支撑了微服务

07 docker使用

上节课复习:

类的命名:大写开头

最底层安全框架jdk:JAAS

  • 可用其principal接口获得用户信息

Spring提供的Spring Security:一个包

docker:最初是linux上

docker desktop:针对windows的GUI软件

Spring本身是容器,放的是bean(面向对象的对象)

web容器,如tomcat,放了servelet、filter

docker容器:docker软件生成的容器

上面三个容器没有关系!!

容器就是一台轻量级虚拟机,里面有操作系统(文件管理等都有)

  • 可以快速创建虚拟机,像启动进程一样
  • 为什么快?共享底层的linux操作系统内核,很多东西是共用的,而不是自己独立
  • 为什么需要容器?希望有一个干净的操作系统,跑自己的程序

docker是软件,能帮助生成、管理容器(docker != 容器)

image-20230403211053944

不同的容器环境互不影响

集装箱:可以把软件打成容器的镜像,用户可以直接跑,而不需要乱七八糟的环境配置

  • 集装箱中间不需要拆开,可以直接交付给用户

改变了软件的生成过程,形成流水线,可以及时更新代码、测试

  • 技术基础是容器技术

改变软件技术架构:单体变成了多体,微服务开发:可以分别开发单体,然后合成多体

image-20230403213206034

微服务用Spring Cloud开发,怎么开发微服务?解决分布式中数据的配置(11)、多个微服务怎么彼此发现(12)、A调B服务B不存在怎么办(容错问题13)

K8S:解决集群化,把多个docker宿主机构建一个集群,在集群上虚拟出容器并管理生命周期

istio

image-20230403213858601

本质上只能跑在linux,为了方便desktop可以运行在windows,但底层是linux

image-20230403214106707

Client操作系统无所谓

docker_host一定在linux

镜像:自己生成docker build;或者来自镜像仓库 docker hub

docker用go开发

命令类型

  • 管理命令:
    • image
    • network:让虚拟机之间互联互通
    • volume:存储。容器消亡后什么都没有了,重要数据要外置到某个地方去
  • 操作命令

docker info:客户端和服务端的信息,如plugins:compose:容器编排信息。服务端的镜像数、版本

docker run hello-world:client和daemon建立联系;daemon获得镜像、创建容器、结果输送到用户看到的windows控制台。image-20230404004227502

08 容器镜像构建与重排

镜像:class 容器:object

dockerfile

image-20230404153834360

启动nginx:-p将本机端口映射到nginx

image-20230404151242688

image-20230404151351044

设置属于哪个“交换机/网关”:搭在同一个网络,所以可以互相通信

image-20230404151836727

如何增加新网卡,使3与12相连:用connect 并填入自己的idimage-20230404152343143

创建镜像:这里的.指上下文image-20230404161204836

运行镜像:image-20230404161516013

查看:得到容器的信息,如env、ports的映射、networks:bridge的网关和ip地址

image-20230404162356318

image-20230404163405531

java开发依赖jdk,运行时只需要jre。若只需要jre,可以用alpine,因为轻量级。

09 k8s使用

k8s是集群环境,部署了一系列容器,每个容器都提供了对外访问的端口号。如何从集群外部访问端口?统一的入口——ingress,类似nginx的反向代理,根据域名作转发。

image-20230404202142393

输入域名后,会优先在本地hosts找对应的地址。

controller返回视图

10 REST服务、微服务开发与部署

IDE开发

  • SonarLint静态检查
  • 自动化测试,放在test目录下:驱动Controller运行。Mock/Jmeter

spittr-test

微服务开发:用SpringBoot

image-20230405000140995

Sleuth:对日志做跟踪

@ResponseBody:告诉返回的不是Spittle对象,而是JSON格式字符串。

  • 来自于Spring.web框架。
  • 怎么转?到classpath路径找可以转的第三方包:fasterxml.jackson.image-20230405002733278
  • 如果参数中有,意为从JSON字符串转成Java对象image-20230405133019230

@PathVariable:路径变量

每个方法都要加很冗余,用@RestController解决,会自动JSON解编码(Java对象->字符串)

@Controller:1. 标识这是控制器 2. @Component:Spring扫描类,知道要实例化,就在上下文创建Bean

mvn jetty:run:借助jetty插件可以跑起来

  • host:localhost:8080。所以ingress知道域名。

ResponseEntity可以作为controller的返回值

  • headers.setLocation(url)
  • new ResponseEntity<>(saved, headers, HttpStatus.CREATED)内容/头部信息/201状态码

image-20230405004416461

@ExceptionHandler:处理所有方法中可能抛出的异常。用@ResponseStatus自己返回状态码。

section11

公司管理license

是SpringBoot

实现Rest接口:

image-20230405004847998

查看运行时信息:accutator

image-20230405004905454

自动生成镜像:maven-plungin插件

image-20230405004938629

@RequestMapping value是访问的url

dockerfile中maven validate阶段会把指定文件拷贝到文件夹中,并把变量替换掉

image-20230405133806943

11 基于NACOS的数据配置

section12-nacos

应用程序启动时,会根据不同的属性做不同的处理。比如数据库的类型:driverClassName,数据库的位置:url,数据库的用户名和密码

有些配置信息比较重要,最好不要直接写在代码里

image-20230405135218760

curl配置数据的三种访问方式

  • 服务名service:nacos-headless
  • 服务的集群IPimage-20230405142544463
  • pod的IP地址image-20230405142602283

不可访问:pod的名字不能,如licensingservice-69d757895c

bootstrap.yml中

SpringCloud下面如何指定域名和端口号:spring.cloud.nacos.config.server-addr

微服务的名字:spring.application.name

dataid的后缀:spring.cloud.nacos.config.file-extension

默认以服务名作为dataid的前缀:example

  • 找到example.properties配置,将内容取过来,作为当前微服务的属性文件
  • 可以取useLocalCache=true作为属性文件,就可以取属性值了

image-20230405143443481

通过@Value注解,指定属性名,如果没有就是false

image-20230405144031015

nacos在SpringCloud实现配置数据管理需要以下几步:

  1. 加入依赖,就可以使用nacos配置管理的能力image-20230405151425426

  2. 配上nacos的访问地址、服务名、dataid后缀image-20230405151521165

    image-20230405151539872

  3. @Value:指配置文件当中的属性名 @RefreshScope实时更新image-20230405151645782

cipher:加密

image-20230405154213649

12 基于NACOS的服务注册与发现

nacos发心跳来维持活性

image-20230405160140901

  • loadbalance:客户端的负载均衡(licensingservice调用organizationservice时,nacos会返回3个IP。loadbalance负责选择)
  • openfeign:客户端如何便捷地访问服务端

organization-service需要借助nacos进行服务注册,如何做到?

image-20230405162655919

  1. 需要把数据放在nacos中做集中管理

image-20230405162720335

  • 如果用到了,还要加loadbalance和feign的依赖
  1. 在bootstrap.yml加入server-addr访问地址

image-20230405162901053

  1. @EnableDiscoveryClient:意思是向nacos作登记

image-20230405162927274

licensing-service中:@LoadBalanced表示做负载均衡

image-20230405163351134

k8s-deploy.yml:会部署一个licensingservice和多个organizationservice

clientType是一个变量,根据类型feign/rest/discovery来判断调用哪一种方式

image-20230405165640732

image-20230405231248135

  • discoveryClient:与nacos解耦。getInstances可以获取某个服务的详细信息。instances.get(0)可选择服务。

  • rest:改进后的RestTemplate

  • feign:@EnableFeignClients要加,下面的@Bean就不用了image-20230405222246683

    consumes:希望返回json(会把organization转成json字符串)如果想访问另外的微服务,定义这样的接口,加@FeignClient(想访问的服务名)

    客户端看到的就是对象

    image-20230405222431187

随机策略:

image-20230405223443822

13 基于Sentinel的流控与熔断

image-20230405235140329

A发到B的请求叫流量,流控=流量控制,控制A到B的流量,如单位时间内只处理20个请求,超过就不处理了

容错:B可能异常,A可以把请求给另一个,或者使用缺省值

熔断:B老是出错:说明不稳定。后面就都会返回缺省值或者直接报错。类似漏电后及时把电路切断。

image-20230406000239341

使用Sentinel的步骤

定义资源:

  • try-catch
  • @SentiResource指明要监控的方法

使用try-catch定义资源

image-20230406192307872

流控规则

image-20230406192343490

资源定义在线程的执行代码中

  • try:entry资源的入口定义;统计通过的请求
  • catch:执行阻塞,统计阻止的请求
  • finally:统计通过+阻止的请求

image-20230406192541021

Timer每个1秒检查通过、阻止的请求数

image-20230406192635272

tick每隔1s会进行统计和输出:image-20230406192941605

使用注解定义资源

控制台的配置:

image-20230407133357524

DemoController:

image-20230407133444707

TestService:

  • randomlyRunLong()有三分之一的概率熔断
  • fallback:默认处理,发送限流/熔断会抛异常代码逻辑转向这里的代码,fallback()是方法(return缺省值)

image-20230407133513686

image-20230407133933855

运行代码,test和hello资源均被监控(展示基于对请求的访问,如这里是image-20230407134357713,返回hello at 1)

image-20230407134221000

spring cloud alibaba

限流规则

image-20230407161216547

熔断规则

image-20230407161232412

资源怎么来的?定义了一个个资源:Sentinel可以针对url创建对应的资源,可以注释掉@SentinelResource,只是针对url的话@GetMapping就够了

image-20230407161709366

微服务挂了后,DashBoard不会保存流控和熔断规则

调用后,资源就会加入控制台的簇点链路

考试重点

@Component的作用

单体应用程序的不足

HTTP状态码

yml格式:通过缩进

curl配置数据的三种访问方式

服务发现可以提高弹性(可靠性、容错性)

使用OpenFeign的方式调用服务

2种负载均衡策略

2种健康检查

服务发现的好处

01-建立开发环境

starter-web:实现Web场景开发,提供了嵌入的Tomcat以及Spring MVC的依赖。

starter-thymeleaf:前后端不分离的页面渲染。与Spring MVC等Web框架进行集成作为Web应用的模板引擎。能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。

开发期工具:spring-boot-devtools

开发时使用,生产环境不用

  • 可以一边写代码,一边看到效果
  • 结合spring-boot-run,结合ide(安装livereload):可以实时刷新

功能:

  1. 代码变更后应用会自动重启(需要借助 IDE 的自动编译,局部重启,只重启修改过的)

  2. 当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器

    1. 开放端口以便与浏览器通信
    • 应用会暴露 LiveReload 端口,日志如:LiveReload server is running on port 35729
    • 需要安装 VSCode 插件 LiveReload (IntelliJ IDEA 要做的配置见下页 ppt)
    • 需要安装浏览器插件:LiveReload,并打开
  3. 自动禁用(页面渲染的)模板缓存

    1. 模板指页面渲染的模板
  4. 如果使用 H2 数据库,则内置了 H2 控制台。访问:http://localhost:8080/h2-consle

    1. 该工具只在运行期使用,是runtime依赖,与编译器无关,不会编译优化/插入调试信息等

源代码仓库管理/git

也称为版本控制(version control)系统,常用工具有:GitLab、SVN(Subversion)、Bitbucket 等;

需纳入版本控制的有:功能代码、测试代码、测试脚本、构建脚本、部署脚本、配置文件等;

image-20230408132704866

index是暂存区,通过add添加

commit:从暂存区到本地仓库

02-依赖注入

依赖注入(Dependency Injection),又叫控制反转(IoC)

Spring的两个核心技术

  1. DI (Dependency Injection):保留抽象接口,让组件(Component)依赖于抽象接口,当组件要与其他实际的对象发生依赖关系时,由抽象接口来注入依赖的实际对象
  2. AOP (Aspect Oriented Programming):通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率

依赖注入:创建对象实例时,为这个对象注入属性值或其它对象实例

  • 依赖于容器注入它所需要的外部资源

  • 依赖的是接口,而不是实际的new对象。为了好修改,类不必修改。

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

@Component的作用*

@Component 通常是通过路径扫描来自动侦测以及自动装配到 Spring 容器中

  • 可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装
  • 配的类自动装配到 Spring 的 bean 容器中

@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean

诉了 Spring 这是某个类的实例,当我们需要用它的时候还给我。

@Component(和@Service@Repository)用于自动检测和使用类路径扫描自动配置bean。注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。

@Bean用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许精确地创建和配置bean。

Spring配置方案

管理Bean分为注册和装配Bean两部分。完成管理Bean任务有以下三种方式:

image-20230407164119121

自动化配置

image-20230407163935539

组件扫描

@Component注解:告诉Spring有可能会创建类的实例,接口的实现(在上下文中创建对象)

@ComponentScan注解:明确告诉Spring在当前类对应的包和相应的子包中搜索Component,如果有就自动创建实例对象

@Configuration注解:告诉Spring类要做配置,用ComponentScan组装

1
2
3
4
5
6
@Configuration
@ComponentScan
public class CDPlayerConfig {
//配置类,告诉Spring组装的入口
//ComponentScan可以加参数,告诉去哪里搜
}

自动装配

@Autowired:把上下文中的另一个对象注入到当前类中,建立依赖关系

如CompactDisc建立到CDPlayerimage-20230407164243028

  • 用在构造器

    1
    2
    3
    4
    @Autowired
    public CDPlayer(CompactDisc cd) {
    this.cd = cd;
    }
  • 用在setter方法

    1
    2
    3
    4
    @Autowired
    public void setCd(CompactDisc cd) {//右键generate setter
    this.cd = cd;
    }
  • 直接加在私有成员

    1
    2
    @Autowired
    private CompactDisc cd;

JavaConfig

使用@Bean注解,并创建new对象

@Configuration注解:告诉Spring类要做配置,用ComponentScan组装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* cdplayer配置
* 配置类的用途就是生成并注入 Bean
*/
@Configuration
public class CDPlayerConfig {//Spring的入口

@Bean//告诉Spring来调这个方法,怎么来new是程序员自己考虑
public CompactDisc compactDisc() {
return new SgtPeppers();
}

// @Bean//需要参数,Spring会自动在上下文找是否实现了这个接口的对象,找CompactDisc的实例(加不加Autowired效果都一样)
// public CDPlayer cdPlayer(CompactDisc cd) {
// return new CDPlayer(cd);
// }

@Bean//不是new了一个对象,而是获得上下文的对象的引用 不是应用代码!因为使用了 @Configuration 和 @Bean 注解,多次调用该方法也不会多次 new
public CDPlayer cdPlayer() {
return new CDPlayer(compactDisc()) ;
}

}

XML装配

通过xml告诉bean组装的意图

1
2
3
4
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>

可以通过改class,指定整合的内容,匹配compactDisc接口;相互之间并不是直接关联的

Bean的作用域

@Scope 可以与 @Component@Bean 一起使用,指定作用域

  • Singleton,单例,不使用 @Scope默认,在整个应用中,只创建 bean 的一个实例
  • Prototype,原型,每次注入或者通过 Spring 应用上下文获取的时候,都会创建一个新 bean 实例
  • Session,会话,在 Web 应用中,为每个会话创建一个 bean 实例
    • 包含客户端与服务端的多次的交互,整个过程是一个session,如ShoppingCart没结束
  • Request,请求,在 Web 应用中,为每个请求创建一个 bean 实例
    • 一次请求一个实例

使用会话和请求作用域,重定义的话用@Scope

1
2
3
4
5
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart(){

}

默认是Singleton

03-面向切面编程(AOP)

AOP术语

  1. 通知(Advice):切面做什么以及何时做
    1. @Before: 前置通知, 在方法执行之前执行
    2. @After: 后置通知, 在方法执行之后执行
    3. @AfterRunning: 返回通知, 在方法返回结果之后执行
    4. @AfterThrowing: 异常通知, 在方法抛出异常之后
    5. @Around: 环绕通知, 围绕着方法执行
  2. 切点(Pointcut):何处
  3. 切面(Aspect):Advice 和 Pointcut 的结合
  4. 连接点(Join point):方法、字段修改、构造方法
    • Spring只能在方法切,不能在字段修改和构造方法切
  5. 引入(introduction):引入新的行为和状态
  6. 织入(Weaving):切面应用到目标对象的过程
    1. 编译期
    2. 类加载期
    3. 运行期:Spring采纳的方式

@Before:指在perform()之前,调用silenceCellPhones()

image-20230407164848136

@Aspect切面类中有很多advice。@Before是一个advice,里面是切点表达式。一个before/after就是一条advice。

  • 切点表达式:在哪些类/类的哪些方法/哪些包切

@Aspect具有@Component的效果吗?没有!Spring扫描不到不会实例化

  • 要么在javaconfig类中实例化image-20230407165054287

  • 要么在Audience类前加@Component,在config类前加@ComponentScan

  • @Controller、@Service、@Repository具有@Component的效果

过程

  1. 定义切面类Audience.java @Aspect 很多advice

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Aspect//提示这是一个切面!
    public class Audience {
    @Before("execution(* concert.Performance.perform( .. ))")//包路径.接口.方法 ..表示不在意参数有多少个 *表示不关心返回值是什么
    //在被切入的方法调用之前,把当前方法逻辑执行
    //这里指在perform()之前,调用silenceCellPhones()
    public void silenceCellPhones() {
    System.out.println("Silencing cell phones");
    }

    @Before("execution(* concert.Performance.perform( .. ))")
    public void takeSeats() {
    System.out.println("Taking seats");
    }

    @AfterReturning("execution(* concert.Performance.perform( .. ))")
    //在perform()方法正常返回的时候切入
    public void applause() {
    System.out.println("CLAP CLAP CLAP!!!");
    }

    @AfterThrowing("execution(* concert.Performance.perform( .. ))")
    //在perform()方法异常时切入
    public void demandRefund() {
    System.out.println("Demand a refund");
    }
    }
  2. 在config中将切面的实例化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Configuration
    @EnableAspectJAutoProxy //开启AspectJ的自动代理机制 提醒要用切面,对有切面需求的对象引用都使用代理
    public class ConcertConfig {//配置类,用于实例化bean
    @Bean
    public Performance concert() {
    return new Concert();
    }
    // @Bean
    // public Performance concert2() {
    // return new Concert();
    // }
    @Bean
    public Audience audience() { //定义Audience的bean
    return new Audience();
    }

    @Bean//需要实例化!
    public EncoreableIntroducer encoreableIntroducer() {
    return new EncoreableIntroducer();
    }

    或者在Audience类前加Component,在config类前加ComponentScan

  3. 加入注解@EnableAspectJAutoProxy,提醒要用切面,对有切面需求的对象引用都使用代理

image-20230301153558936

切点指示器

用来限定连接点满足某些条件,如限定参数、包路径、bean名称

  • execution:用来匹配方法执行的连接点,也就是哪些方法要应用切面
  • within:用来限定连接点必须在确定的类型或包中
  • args:用来限定连接点,也就是方法执行时它的参数属于给定类型的一个实例
  • bean:限定bean名称

image-20230407165728930

04-Web开发框架

lombok

image-20230407170142114

lombok可以简化java代码的书写:编译时,lombok帮助填写我们没填的代码

  • 如使用@Data,lombok会帮忙生成get/set/equals/hashCode

  • 要装插件,否则省略很多内容后会报错

请求映射注解

注解 描述
@RequestMapping 通用的请求处理,一般只在类级别使用
@GetMapping 处理 HTTP GET 请求
@PostMapping 处理 HTTP POST 请求
@PutMapping 处理 HTTP PUT 请求
@DeleteMapping 处理 HTTP DELETE 请求
@PatchMapping 处理 HTTP PATCH 请求

@RequestMapping:既可以加在方法上,也可以加在类上

@SpringBootApplication:指明程序的入口

image-20230407170345461

localhost:8080/design发生什么(我加的)

  1. 在controller中,@RequestMapping(“/design”)会配置请求的映射
  2. @GetMapping找到/design方法,因为是get,匹配到。返回字符串”design”。
  3. resources放资源,view放在resources/templates目录下,根据返回的字符串找到模板路径design.html
  4. thymeleaf根据模板、属性和对象渲染出浏览器可以使用的html文档
    1. 模板中所需的数据从哪里来?ingredient是控制器提供的对象,id、name这些属性可访问

点击提交按钮发生什么(我加的)

  1. 在controller中,@RequestMapping(“/design”)会配置请求的映射
  2. @PostMapping找到processTaco方法。
    1. taco对象:服务端将前端输入的信息转成taco对象,使用Converter,把id转到Ingredient

MVC分层/Web项目有哪些层次【2020】

image-20230407171558883

进入控制器层:进行参数解析

客户端请求参数分类:

  1. 路径参数,@PathVariable
  2. 请求参数(查询参数),@RequestParam
  3. 表单参数,应用于前后端不分离的传统场景,默认,对应 model 对象,可以使用 @Valid 校验
  4. json 请求体,应用于前后端分离的场景,使用 @RequestBodyjson 格式转成 java 对象;@ResponseBody,把 java 对象转成 json 格式

进入业务层:做数据持久化,访问数据访问层

控制器层拿到返回值后怎么处理?

  • 前后端不分离时:客户端请求页面,所以返回时将model的属性作为页面渲染的属性,返回视图名,通过第三方页面渲染返回页面(如thymeleaf)

  • 前后端分离时:请求JSON格式。加@ResponseBody,指需要将java对象变成json。每个方法上面写很麻烦,在类上面写@RestController

05-JDBC、JPA

特点 JdbcTemplate Spring Data JDBC JDA
实现具体类(Repository.java) 需要 不需要,只要写明继承关系 不需要,只要写明继承关系
定义实体类和数据库表的映射关系 不需要 需要 需要
程序员维护表之间的关系 需要 不需要 不需要
显式提供表结构(建表 SQL 脚本) 需要 需要 不需要,可以自动推断

JdbcTemplate

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

private JdbcTemplate jdbcTemplate;//模板 加了starter-jdbc依赖后,会帮忙写,只需提供变化的部分

特点:

  • 解决样板式代码的问题(重复性),只需要提供查询逻辑。

    • 提供了大量查询更新数据库的方法,如updateexecutequery等。可以帮忙完成重复性代码。
    • 如果要覆盖的话写@Override即可。
  • 需要实现具体类 JdbcIngredientRepository 而其他两种方法不用;

  • 需要提供 src/main/resources/schema.sql 文件作为表结构的定义(建表脚本)。

    • 很复杂,要进行许多表的关联,如order/taco/ingredient中要插入的数据、id都要自己指明image-20230407200204682

    • sql放在class根路径下,spring会自动执行脚本,完成对表的创建

SpringDataJDBC

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

特点:

  • 需要定义实体类和数据库表的映射关系;

    • 需要@Table、@Id、@Column等注解

    • @Data//lombok会帮忙生成get/set/equals/hashCode
      //@Table("mytable")//可以将当前的java对象对应到数据库的哪一张表
      @Table //如果不写,或者只写@Table,当前Ingredient对象就对应数据库中的Ingredient表
      @AllArgsConstructor
      @NoArgsConstructor(access=AccessLevel.PRIVATE, force=true)
      public class Ingredient implements Persistable<String> {
      
        @Id
        private String id;
      
        private String name;
        private Type type;
      
        @Override
         public boolean isNew() {
            return true;
         }
      
        public enum Type {
          WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
        }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21

      - 不需要实现具体类,只需要写好继承关系

      - 定义接口并继承CrudRepository
      - ![image-20230407200224879](https://lapsey-pictures.oss-cn-shenzhen.aliyuncs.com/typora_imgs/202502041323333.png)

      - 需要提供 `src/main/resources/schema.sql` 文件作为表结构的定义(建表脚本)。

      > 加devtools依赖包:默认提供h2-console的访问,就可以及时验证数据库开发的结果

      ## SpringDataJPA的开发流程和特征【2020】

      开发流程:

      1. 引入依赖

      ```java
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>
  1. 定义接口,继承自CrudRepository

image-20230407200520266

  1. 定义实体类和数据库表的对应关系
    1. @Entity说明这个class是实体类,可以从数据库找到,如果没有它会帮你创建一个数据表的作用(Entity是共同规范,与厂家无关)
    2. @id:声明一个属性将映射到数据库主键的字段。
  • jpa可以通过java对象,推出表的结构,所以不需要schema.sql的文件

image-20230407200545641

特征:

  • 需要定义实体类和数据库表的映射关系;
  • 不需要实现具体类,只需要写好继承关系;
  • 依据实体类推断表结构,不需要建表脚本;
  • 可以自定义查询方法。

与JDBC的共同点:定义接口、定义对应关系

自定义查询方法

定义查询方法,无需实现:

  • 领域特定语言(domain specific language DSL)spring data 的命名约定
  • 查询动词 + 主题 + 断言
  • 查询动词: get 、 read 、 find 、 count
  • 例子:
1
List<TacoOrder> findByDeliveryZip( String deliveryZip );

声明自定义查询(JDQL 面向对象查询语言):

不符合方法命名约定时,或者命名太长时

1
2
@Query("Order o where o.deliveryCity = 'Seattle'")
List<TacoOrder> readOrdersDeliveredInSeattle( );

Jpa、Hibernate、Spring Data Jpa 三者之间的关系

  • JPA 的宗旨是为 POJO 提供持久化标准规范;
  • Hibernate 作为厂家实现了这一规范;

img

06 Spring Security

用户信息存储

持久化的3种方式:

  • 内存用户存储
  • JDBC用户存储
  • LDAP用户存储

SpringSecurity开发

img

开发人员实现SpringSecurity提供的UserDetialsService接口(告诉权限等),实例化到上下文,SpringSecurity就可以在上下文找到接口对象,返回UserDetails用户的详细信息

BEANPassWordEncoder也是需要开发人员实现SpringSecurity提供的接口->实例化到上下文,以便SpringSecurity进行加解密

绿色部分不需要开发人员操心,帮助进行用户名密码的认证工作(不需要自己实现Controller)会用到BEANPassWordEncoder和UserDetialsService实例

image-20230407202421177

告诉SpringSecurity需要对哪些url进行控制

  • @EnableGlobalMethodSecurity 如果打开,可以实现方法级别的授权:PreAuthorize生效

    1
    2
    3
    4
    @PreAuthorize("hasRole('ADMIN')")//方法级别的注解
    public void deleteAllOrders() {
    orderRepository.deleteAll();
    }

创建自定义登录页

  • 当需要认证时转向的登录页:.loginPage("/")
  • 视图控制器,定义 login 请求对应的视图:registry.addViewController("/login");
  • 登录的 post 请求由 Spring Security 自动处理,名称默认:usernamepassword,可配置

07 docker使用

docker

容器就是一台轻量级虚拟机,里面有操作系统(文件管理等都有)

  • 可以快速创建虚拟机,像启动进程一样
  • 为什么快速?共享底层的linux操作系统内核(在宿主机上),很多东西是共用的,而不是自己独立
  • 为什么需要容器?希望有一个干净的操作系统,跑自己的程序
  • 使用者可以过滤底层差异,看作VM

docker是一个软件,用于运行、管理容器

docker desktop是针对windows的GUI软件,会自动在本机创建VM(虚拟化出的linux)

在docker hub创建账号,存的是容器的镜像,可以下载很多软件。

容器技术支撑了微服务

docker run

image-20230408201927366

-p:左边是主机端口,右边是容器端口

–rm:退出后自动删除容器

-d:后台运行,不用交互

-e:设置环境变量,操作系统中会有对应的环境变量

管理命令

image-20230407204526513

输入docker --help

volume:存储。容器消亡后,使数据可以继续保留在volumn里。

  • docker管理卷
  • 绑定挂载卷

network:让虚拟机之间互联互通

  • 默认bridge网络,每个容器创建后自动挂在docker0的桥上

container

image

此外,docker stop的意思是停止运行的容器;但停止后可以重新启动(restart)

08 容器镜像构建与重排

dockerfile文件指令

image-20230408210634034

RUN:构建正在创建的映像,会创建image的新层

  • docker run 指令(-it/-p/-d等) 镜像名 参数(/bin/sh、bash等)
  • docker run命令中,镜像名后面的都是给容器执行的命令参数
  • 所以docker run -it test:1docker run -it test:1 bash,前者不会覆盖cmd,后者会覆盖cmd

ADD和COPY的区别:add会自动解压,copy只会拷贝(都可以将本地文件添加到容器中)

cmd和entrypoint的区别

CMD:容器启动以后,默认的执行的命令,如启动python程序

  • 如果docker run没有指定任何的执行命令或者dockerfile里面也没有entrypoint,那么,就会使用cmd指定的默认的执行命令执行。
  • 如果我们在run时指定了命令或者有entrypoint,那么cmd就会被覆盖。
  • 它不会为Image创建新层,而只是运行命令

ENTRYPOINT:用entrypoint的中括号形式作为docker 容器启动以后的默认执行命令

  • 区别1:ENTRYPOINT不会被docker run中的参数命令覆盖

  • 区别2:如果在Dockerfile中CMD与ENTRYPOINT都存在,则CMD中的指

    令将会被作为ENTRYPOINT中的参数,即这时候CMD里的指令会失去它的

    作用,只是作为一个参数(可以理解为就是字符串)了。这时docker run

    后的参数指令依然会覆盖CMD,但是也会失去他本身的作用,仅仅是作为

    参数(字符串)提供给ENTRYPOINT中的命令

编写最佳的dockerfile

image-20230408211951689

dockerignore:把不想要的文件排除在外

容器本来就轻量级,只运行单个应用

多个RUN合并:减少镜像的分层。可以把差不多变化频率的层放在一起。

docker-compose及常用指令

image-20230407210015916

和k8s同级,均可用于部署服务,如将多个服务一次性部署。

  • 一个服务对应一或多个容器
  • 项目是由一组关联的应用容器组成的一个完整业务单元

image-20230407210034235

docker-compose ps:不是呈现docker系统中的所有容器,只是呈现当前目录下docker-compose所部署的容器(因为会运行当前目录下的docker-compose.yml文件进行部署)

docker-compose images同理,只是当前目录下的镜像

docker-compose logs -f [pod名字/服务的名字] 把里面的容器的日志打印出来

yaml文件*

image-20230407210025648

连续的-或者一行

对象用{}

09 k8s使用

k8s中的资源

image-20230407210818637

ingress:如何从集群外部访问端口?统一的入口——ingress,类似nginx的反向代理,根据域名作转发。

  • k8s是集群环境,部署了一系列容器,每个容器都提供了对外访问的端口号。
  • 输入域名后,会优先在本地hosts找对应的地址。
  • ingress由两部分组成:
    • ingress controller:将新加入的Ingress转化成Nginx的配置文件并使之生效
    • ingress服务:将Nginx的配置抽象成一个Ingress对象,每添加一个新的服务只需写一个新的Ingress的yaml文件即可

service :在k8s中不要直接访问具体pod,因为pod经常变换ip会变,所以用service。

  • 从逻辑上代表了一组Pod,具体是哪些 Pod 则是由 label 来挑选。
  • 有自己的IP,而且这个IP是不变的
  • 客户端只需要访问 Service的IP,Kubernetes 则负责建立和维护 Service 与Pod的映射关系。无论后端Pod如何变化,对客户端不会有任何影响,因为Service没有变。

deployment:pod版本管理的工具,用来区分不同版本的pod

  • 单独创建pod的时候就不会有deployment出现,但是创建deployment的时候一定会创建pod,因为pod是一个基础的单位。

  • 顾名思义“部署”:除了运行代码(即pod)之外,需要考虑更新策略,副本数量,回滚,重启等步骤

  • 自动伸缩,设定伸缩个数的区间:

    1
    kubectl autoscale deployment spittr --min=10 --max=15 --cpu-percent=80

pod:调度的最小单位,一个或多个容器

pod

image-20230407210949745

背命名空间:默认情况下同一个POD的不同容器的哪些名称空间是打通的。这里先说一下结论,共享的是UTS、IPC、NET、USER。

pod可以在多个容器直接共享

是K8S调度的最小单元

访问服务的方法

  1. port-forward创建pod/service端口到本机端口的映射,实现对部署服务的访问
  2. 创建ingress,就可以通过域名来做路由。
    1. 查询hosts文件得到本地ip
    2. 来自域名的请求会被转发到所设置的服务的端口
  3. 启动curl工具:基于http的访问image-20230407211801930

Label、service 和 pod 之间的关系

image-20230407213004599

通过label、service找到对应的pod

service有集群ip,也可以用来访问service(pod因为经常消亡,ip会变所以不能用)

k8s service和nacos service的异同点

共同点:通过服务名访问多个动态的服务实例。

  1. 一个服务的背后可能有多个服务的实例,多个服务的实例通过pod体现。
  2. 因为pod不断增加消亡、动态变化,所以服务实例也是动态变化的。客户端只需要知道服务名,k8s和nacos负责具体管理。

不同点:

  1. k8s service是pod层级,与ingress/pod等资源配合使用。nacos service是服务层级,与开发框架相关。
    1. k8s 的 service 底层还有 pod,nacos 的 service 本身就是最低粒度的单位了
  2. 在服务注册与发现上,nacos的与k8s的service可以二选一
  3. k8s除了作为服务发现和注册中心,还有管理部署的功能。
    1. 当拿 k8s 作为部署工具的时候,nacos 的底层可以依赖 k8s。此时nacos维护每个服务的元数据,每个服务的部署、升级、重启等依赖底层的k8s
    2. 但nacos也可以通过其他方式部署服务实例,注册到nacos。比如换 docker swarm 或者其它部署工具的时候,nacos 就可以不依赖 k8s

10 REST服务、微服务开发与部署

微服务架构模式的特征【2020】

  1. 应用程序分解为具有明确定义了职责范围的细粒度组件
  2. 完全独立部署,独立测试,并可复用
  3. 使用轻量级通信协议 HTTP 和 JSON ,松耦合
  4. 服务实现可使用多种编程语言和技术
  5. 将大型团队划分成多个小型开发团队,每个团队只负责他们各自的服务

单体应用程序的不足*【2020】

  1. 数据库的表对所有模块可见
  2. 一个人的修改,整个应用都要重新构建、测试、部署
  3. 整体复制分布式部署,不能拆分按需部署

HTTP状态码*

image-20230407214435705

1-5的含义

消息转换器

image-20230409163422944

在每个方法上加@ResponseBody或在类上加@ResponseController

可以加在方法上面,或者传参时参数里面

  • 接收data时:文本如JSON串->Java对象
  • 返回data时:Java对象->JSON字符串

运维实践

  1. 功能代码和测试脚本等都在源代码库中
  2. 指定 JAR 依赖的版本号
  3. 配置数据与源代码分开放,配置当中有很多常变化的、敏感的信息
  4. 已构建的服务是不可变的,不能再被修改
  5. 微服务应该是无状态的
  6. 并发,通过启动更多的微服务实例横向扩展,多线程是纵向扩展

11 基于NACOS的数据配置

nacos:动态配置服务+服务发现及管理,可在docker/k8s部署

微服务的两大配置数据来源:Spring Cloud Config或nacos

如何基于nacos配置管理做微服务开发

  1. 加依赖image-20230407214951871
  2. 在bootstrap中定义nacos的访问地址(server-addr)、后缀(file-extension)、服务名(application.name,可用来组合dataID,有固定的格式)image-20230407215034722
  3. 在代码中获取某一属性的值
    1. @Value指定属性文件里key的名字,由此可以从nacos中获取最新属性值(这里设置了缺省值false)

    2. @RefreshScope:nacos更新配置数据后,SpringBoot及时地获取数据

dataid

image-20230407220719824

在nacos平台上创建dataid,内容是配置数据img

12 基于NACOS的服务注册与发现

如何使用OpenFeign的方式调用服务/如何使用nacos进行服务注册与发现*【2020】

  1. 需要把数据放在nacos中做集中管理,最核心是nacos-discovery

还有nacos-configimage-20230407221406283

如果用到了,还要加loadbalance和feign的依赖

  1. 在bootstrap.yml加入server-addr访问地址(nacos访问地址)

  2. 在application.java中加入@EnableDiscoveryClient(这个第二三种都要加)和@EnableFeignClients(第三种)注解

    1. 第三种方式使用OpenFeign,加两个注解

image-20230405223044074

  1. 定义要访问的接口。@FeignClient()的value为要访问的服务名,会拿来向nacos询问背后的实例
    • value可以指定要访问的url

image-20230405223304039

  1. openfeign会自动实现接口,但需要我们用@Autowired注入依赖:image-20230407221544886

curl配置数据的访问方式*

  1. 服务名service:nacos-headless
  2. 服务的集群IPimage-20230405142544463
  3. pod的IP地址image-20230405142602283

不可访问:pod的名字不能,如licensingservice-69d757895c

服务发现的好处*

  1. 快速水平伸缩,而不是垂直伸缩。不影响客户端
    1. 水平伸缩:任意增删实例,对客户端不感知
    2. 垂直伸缩:增加计算资源(如CPU、内存)来处理大量请求
  2. 提高应用程序的弹性
    1. 如可靠性、容错性。有一个挂了,nacos会用其他健康实例。

SpringCloudAlibaba包括学过的nacos和sentinel,其他不用管

使用到的starter依赖

  • 服务配置: com.alibaba.cloud, spring-cloud-starter-alibaba-nacos-config
  • 服务注册: com.alibaba.cloud, spring-cloud-starter-alibaba-nacos-discovery
  • 客户端负载均衡: org.springframework.cloud, spring-cloud-starter-loadbalancer
  • 简化客户端调用: org.springframework.cloud, spring-cloud-starter-openfeign

调用服务的三种方式

后两种方法自动做负载均衡,因此一般不建议使用第一种(只在测试时使用),更常见的是第三种。我们定义的负载平衡策略(轮询、随机等)能影响到后两种方式。

健康检查的两种机制*

  1. 临时实例的客户端主动上报机制, 临时实例每隔 5s 发送一个心跳包给 Nacos 服务器端

    1. 部署在k8s上的实例主动向nacos发心跳。
  2. 永久实例的服务端反向探测机制,永久实例支持 3 种探测协议,TCP、HTTP 和 MySQL,默认探测协议为 TCP,也就是通过不断 ping 的方式来判断实例是否健康。

    1. nacos主动和实例联系,不断ping。实例可以写REST接口,返回状态码判断是否健康。

img

服务部署

licensingservice如何调用organizationservice?

image-20230407221228485

错误:用nacos转发,licensingservice->nacos->organizationservice

正确:从nacos获取organizationservice的实例,如3个IP,客户端的loadbalancer以轮询或某种略侧从3个中选一个,然后直接通讯

  • loadbalance:客户端的负载均衡,负责选择
  • openfeign:客户端如何便捷地访问服务端

13 基于Sentinel的流控与熔断

Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

  • 容错:B可能异常,A可以把请求给另一个,或者使用缺省值
  • 熔断:B老是出错:说明不稳定。后面就都会返回缺省值或者直接报错。类似漏电后及时把电路切断。

定义资源的方式

3种定义资源的方式

  1. 通过代码直接定义:一段代码定义一个资源,try-catch做保护
  2. 使用注解定义:@SentinelResource(value=”test”, fallback=”helloFallBack”)代表要监控test方法,发送限流/熔断会抛异常代码逻辑转向fallback()方法
  3. SpringCloud框架基于url自动定义。

强调:外置文件只能定义规则,不能定义资源!

image-20230408011850566

规则的种类

可在代码定义或者dashboard定义

QPS:每秒查询率。对查询服务器在规定时间内处理流量多少的衡量标准。

  1. 流量控制规则:当QPS超过任意规则的阈值后,新的请求会被立刻拒绝,方式为抛出FlowException
  2. 熔断降级规则:复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。
    1. 因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
  3. 系统保护规则:系统高负荷工作、CPU利用率占有率高时的保护规则
  4. 来源访问控制规则:根据不同来源采取不同措施
  5. 热点参数规则:同样的请求,依据参数来做限定

BlockException

  • DegradeException:熔断

  • FlowException:限流

熔断处理与策略

  • 针对耗时长的情况
  • 针对业务本身抛出的异常

Sentinel处理后会返回缺省值或者异常。

策略:

  • 慢调用:若某个请求的处理时间>RT,则认为是慢调用。
    • 若单位统计时长内有慢调用比例>阈值+请求数目>最小请求数目,则熔断。
    • 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常:若单位统计时长内有异常比例>阈值+请求数目>最小请求数目,则熔断。
    • 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)。若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
  • 异常数:当单位统计时长内的异常数目>阈值,则自动进行熔断。
    • 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)。若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

Sentinel组成

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo/Spring Cloud 等框架也有较好的支持
    • 在微服务开发时进行交互
    • 可以用Sentinel的API,如FlowRule、DegradeRule、SystemRule等
  • 控制台(Dashboard Dashboard):主要负责管理推送规则、监控、管理机器信息等
    • 已经实现好了,可以直接用

img

控制台不维护规则,通过端口号 8719 查询规则,如果服务故障则规则丢失。

在控制台定义规则后,会立刻应用到服务上。

其他注解

@SpringBootApplication程序入口,进入

  • @EnableAutoConfiguration作用:帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建对应配置类的Bean,并把该Bean实体交给IoC容器进行管理。
  • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
    • 会搜索@Controller注解的bean,扫描类

@profile(“dev”)名字可以随便起

1
2
@Profile("dev")//开发环境,一开始就实例化出来
@Profile("prod")//只在生产环境下才实例化出来,所以并不是同时实例化

@Conditional

1
@Conditional(MagicExistsCondition.class)//在某种情况下才实例化 参数是条件(这里是类实现)

@Qualifier :指出我们想要使用哪个 bean

@Valid注解会告诉spring进行校验(来自import javax.validation.Valid;)

1
2
3
4
5
6
7
8
9
10
11
12
@Data//lombok会帮忙生成get/set/equals/hashCode
public class Taco {

@NotNull
@Size(min=5, message="Name must be at least 5 characters long")
private String name;//taco的名字

@NotNull
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients;//用户所指定的配料 Converter将id转成Ingredient

}

@SessionAttributes(“tacoOrder”)维持一段会话内tacoOrder的状态

@ModelAttribute:将请求参数绑定到 Model 对象。

@Repository:持久层的接口

@Controller:控制器层的接口

@Query:标记在继承了Repository的自定义接口方法上,就不需要遵循查询方法命名规则

作业

6

  1. GitHub - AliyunContainerService/k8s-for-docker-desktop: 为Docker Desktop for Mac/Windows开启Kubernetes和Istio。照着安装到ingress,我之前已经安了3.7,这里下的是3.8

  2. image-20230323011351348

  3. 涉及到pull/build等,一定要上梯子

  4. image-20230323014020833k8s-depoly.yaml把ppt里的语句合并了

  5. 最后一个坑:注意修改本机hosts文件!

7

在dockerfile所在目录下运行命令,遇见问题与解决:

img

https://blog.csdn.net/m0_47256162/article/details/127872759

刚开始没部署成功,加了这个就绿了image-20230326125759351

manifest for java:latest not found: manifest unknown: manifest unknown的解决:加了一堆镜像

image-20230324123218450

pod结点绿但是无法访问的原因:换行符问题,mvn clean package之后把target目录下的dockerfile与run.sh格式改为LF,然后mvn docker:build,再create部署即可img

9

把run.sh改为LF格式

https://github.com/AliyunContainerService/k8s-for-docker-desktop

kubectl proxy开启网页,访问http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

Windows环境下:$TOKEN=((kubectl -n kube-system describe secret default | Select-String “token:”) -split “ +”)[1]
kubectl config set-credentials docker-desktop –token=”${TOKEN}”
echo $TOKEN