登录和相关的CRUD:

Nginx教程_54笨鸟
Nginx 从入门到实践,万字详解!_nginx_前端下午茶_InfoQ写作社区

相关概念:

image.png

1
2
3
4
5
6
7
8
9
10
11
1.DTO是数据传输对象是一种用于封装数据并将其从应用程序的一个子系统发送到另一个子系统的对象。
2.N 层应用程序中的服务层最常使用 DTO 在其自身和 UI 层之间传输数据。这里的主要好处是它减少了分布式
应用程序中需要通过线路发送的数据量。他们还在 MVC 模式中制作了很棒的模型。
3.DTO 的另一个用途是封装方法调用的参数。如果方法采用四个或五个以上参数(把这些参数封装为一个DTO对象),
这可能很有用。java这种单值返回对象,可以将多个不同的对象封装在一个DTO对象里面,然后返回该对象,
并把该对象传递给其他应用程序。
4.DTO类型的对象, 不应该掺杂任何业务逻辑; 只包含获取和设置属性的方法, 以及用于序列化或反序列化
的解析器。
5.如果想从数据库传输一些信息,但其中包含一些敏感信息,那么我们可以使用 DTO,只传输必要的信息。
6.DTO的好处之一:用于在服务器和客户端进程通信的时候,可以一次性传递更多的信息。节省了通信成本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1.是表现层状态转换,
2.REST API通过使用HTTP协议中的不同方法(如GET、POST、PUT、DELETE等)来对资源进行操作。
3.以下是REST API的一些关键特点:

资源(Resources):REST API通过URL(统一资源定位符)来表示资源。每个资源都有唯一的URL,用于
标识和访问该资源。例如,/users可以表示用户资源。

HTTP方法(HTTP Methods):REST API使用HTTP协议的不同方法来表示对资源的操作。常用的方法包括
GET(获取资源)、POST(创建资源)、PUT(更新资源)和DELETE(删除资源)等。

状态无关性(Statelessness):REST API是无状态的,意味着每个请求应该包含足够的信息来理解和处理
该请求,而不依赖于之前的请求。服务器不会保留客户端的状态,每个请求都是独立的。

表征性(Representation):REST API使用不同的表示形式来传输资源的状态。常见的表示形式包括JSON
(JavaScript Object Notation)和XML(eXtensible Markup Language)等。

超媒体驱动(HATEOAS):REST API可以通过在响应中提供超媒体链接来支持自描述性。这些链接可以指导
客户端在资源之间进行导航和操作。

通过使用REST API,客户端可以通过发送HTTP请求来与服务器进行通信,并执行对资源的操作。服务器根据
请求的方法和URL来确定要执行的操作,并返回相应的响应。

REST
VO:展示用的数据,会更加业务要求把DTO的数据进行删除和业务解释。
PO:PO就是数据库中的记录
BO:多个PO组成的业务对象
DAO:通常是一个接口或类,用来操作数据库的

Java 8 Streams :
[译] 一文带你玩转 Java8 Stream 流,从此操作集合 So Easy - 掘金
Java8 新特性教程 - 异常教程
JAVA8新特性–集合流操作Stream_stream().maptolong-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
 List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1");

myList
.stream() // 创建流
.filter(s -> s.startsWith("c")) // 执行过滤,过滤出以 c 为前缀的字符串
.map(String::toUpperCase) // 转换成大写
.sorted() // 排序
.forEach(System.out::println); // for 循环打印

// C1
// C2

JPA之@Entity、@Table、@Column、@Id - 仅此而已-远方 - 博客园
@Enity是将该对象和数据库的表联系起来(类名是表名的驼峰转换,eg:@Entity 表明该类 (UserEntity) 为一个实体类,它默认对应数据库中的表名是user_entity。),@Table是当@Enity注解的对象和数据库的表名不匹配的时候手动添加其对象联系的表。
@Mapper注解详解:
Mapper层注解讲解_爱吃牛肉的大老虎的博客-CSDN博客

1
2
3
4
5
6
7
1.@Mapper: 这个注解一般使用在Dao层接口上,它的作用就是将接口生成一个动态代理类。
使用@mapper后,不需要在spring配置中设置扫描地址,通过mapper.xml里面的namespace属性对应相关的
mapper类,spring将动态的生成Bean后注入到ServiceImpl中。@Mapper注解写在每个Dao接口层的接口类上,@MapperScan注解写在SpringBoot的启动类上。

当我们的一个项目中存在多个Dao层接口的时候,此时我们需要对每个接口类都写上
@Mapper注解,非常的麻烦,此时可以使用@MapperScan注解来解决这个问题。

源码浅析:
框架技术 — Mybatis动态_object.class.equals(method.getdeclaringclass()_码农C风的博客-CSDN博客
1.Mybatis会利用动态代理生成一个Dao接口的实例并注入到容器中,不需要自己创建对象了。而查询数据库的操作是通过SqlSession接口实现的。
JWT是在客户端发送登录信息,判断登录成功后在返回token.
JWT生成toke的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try {
// 设置过期时间,System.currentTimeMillis() 是 Java 中的一个静态方法,
//它返回当前系统时间的毫秒数,也称为时间戳;java中的Date对象本身的构造方法传递的是一个
//long的数值,它表示Date对象存储的是一个long形的时间戳,它的toring方法会将它转化为字符串
//格式的日期。
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// 私钥和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 设置头部信息
Map<String, Object> header = new HashMap<>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("loginName", username)
.withClaim("loginTime", loginTime)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
1
2
3
4
5
6
7
8
9
10
11
  // 设置过期时间为 1
long expirationTime = System.currentTimeMillis() + 86400000;
//
String token = Jwts.builder()
.setSubject("user123") // 设置 subject
.setExpiration(new Date(expirationTime)) // 设置过期时间
.signWith(SignatureAlgorithm.HS256, "secret") // 设置签名
.compact();//构建并获取最终的 JWT 字符串,没有这一步返回的是一个 JwtBuilder 对象
System.out.println(token);

//上面也可以使用setClaims(claims)方法设置playload

上面2种生成token的方式不一样,一个使用Jwt生成token一个使用Jwts生成token,也就是使用生成jwt的库不一样,导入的依赖不一样。第一个是使用Auth0 java-jwt库生成的,第二个是使用JJWT库生成的。
JWTS
Managing JWT With Auth0 java-jwt | Baeldung

1
2
3
4
5
6
7
8
logging:
level:
com:
sky:
mapper: debug
service: info
controller: info
//上面的代码分别是设置com包下的sky下的mapper和service和controller个包的日志级别

@RestControllerAdvice:
@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody等组成。其作用是将控制层的全局的错误俘获,然后将返回结果写进返回体中。@ExceptionHandler(value = Exception.class) ExceptionHandler的作用是用来捕获指定的异常,其中的value指定当某种异常发送后做什么处理,该注解常标记在方法上,标记的方法代表对某一具体的异常进行处理。当有多个@ExceptionHandler注解标识的类时,对于异常的处理是就近原则(优先按照该异常的类,没有找到就按照最近的父类处理)。Spring的@ExceptionHandler注解使用方法-CSDN博客该注解修饰的方法,如果在该注解之下方法之上添加,@ResponseBody
看看人家 SpringBoot 的全局异常处理,多么优雅。。。
spring中的错误处理:
Springboot核心功能:高级特性、原理解析-CSDN博客image.png
image.png
image.png
image.png
springsecurity的复习:
Spring Security核心接口用户权限获取,鉴权流程执行原理|8月更文挑战 - 掘金(暂时还不是很理解)

1
2
3
4
5
6
7
8
 //把result转成JSON
String json = objectMapper.writeValueAsString(result);
//响应出去
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
//上面的response.getWriter();指明了流要输出的对象是response里面,输入的是json字符串
//输出流一般都是指明输出的地址的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Spring Security中的安全上下文(SecurityContext)是一个存储当前用户认证信息和权限信息的对象,
它可以通过SecurityContextHolder类获取和设置。安全上下文的持续时间取决
于SecurityContextHolder的策略和HttpSession的生命周期。
SecurityContextHolder有三种策略,分别是:

• MODE_THREADLOCAL:这是默认的策略,它使用ThreadLocal来存储安全上下文,这意味着安全上下文只
在当前线程中有效,当线程结束时,安全上下文也会消失。
• MODE_INHERITABLETHREADLOCAL:这种策略也使用ThreadLocal来存储安全上下文,
但是它允许子线程继承父线程的安全上下文,这对于使用线程池的情况比较有用。

• MODE_GLOBAL:这种策略使用一个全局的变量来存储安全上下文,这意味着所有的线程都共享同一个
安全上下文,这种策略很少使用,因为它可能导致数据混乱和安全风险。
HttpSession是一个用于存储用户会话信息的对象,它可以在多个请求之间保持用户状态。
HttpSession的持续时间取决于服务器的配置和客户端的行为,一般有以下几种情况:

• 如果服务器设置了HttpSession的超时时间(timeout),那么当用户在一段时间内没有发起任何请求时,HttpSession会自动失效,安全上下文也会被清除。https://spring.io/projects/spring-security/

• 如果用户主动退出登录或关闭浏览器,那么HttpSession会被销毁,安全上下文也会被清除

• 如果用户在同一个浏览器中打开多个标签页或窗口访问同一个应用,那么它们会共享同一
个HttpSession和安全上下文。
• 如果用户在不同的浏览器或设备中访问同一个应用,那么它们会拥有不同的HttpSession
和安全上下文。

Java迭代器详解,看这一篇就够了-CSDN博客

1
DigestUtils是spring提供的加密类,DigestUtils.md5DigestAsHex(password.getBytes());

在IDEA中可以通过在注释中添加TODO来标识待完善的部分。可以点击左下角的TODO来快速定位TODO的位置。
image.png
Swagger:
image.png
springboot2和springboot3中swaager导入的版本不同
image.png
快速开始 | Knife4j
image.pngimage.pngimage.png
BeanUtils.copyProperties(employeeDTO, employee);是将employeeDTO该对象的相关属性赋值给employee.
image.png
image.png
controller层注入service接口,service接口的实现类注入mapper接口类,并利用注入的接口类中的接口方法操作数据库。
IDEA实现修改Springboot项目后不用重新启动就可以看到更改:
1.导入依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

2.image.png
3.image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
spring-boot-devtools 是 Spring Boot 提供的一个开发工具模块,旨在提高开发人员的开发体验和生产力。它为开发人员提供了一些有用的功能,以加快应用程序的开发、调试和重新加载过程。

以下是 spring-boot-devtools 提供的主要功能:

自动应用程序重启:当你进行代码更改时,spring-boot-devtools 可以自动监测到变化,并自动重新启动应用程序,以便立即看到更改的效果,而无需手动停止和启动应用程序。

自动重新加载:除了重启应用程序外,spring-boot-devtools 还支持许多类文件的自动重新加载,而无需重新启动整个应用程序。这对于一些轻量级的更改,如修改控制器、视图模板或静态资源文件等,可以更快地生效。

禁用模板缓存:在开发阶段,模板引擎通常会对模板文件进行缓存,以提高性能。但这也会导致在更改模板文件后,你无法立即看到更改的效果。spring-boot-devtools 可以自动禁用模板缓存,以便在每次请求时都重新加载模板文件。

静态资源自动更新:当你修改静态资源文件(如 CSS、JavaScript 或图片文件)时,spring-boot-devtools 可以自动检测到更改,并将其复制到应用程序的类路径中,以便立即生效。

全局设置更改:spring-boot-devtools 还提供了一个 META-INF/spring-devtools.properties 文件,你可以在其中配置一些全局设置,如禁用某些特性或自定义重新加载的策略等。

image.png

  1. @ConfigurationProperties 的 POJO类的命名比较严格,因为它必须和prefix的后缀名要一致, 不然值会绑定不上, 特殊的后缀名是“driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是 “driverClassName”

@ConfigurationProperties实现从配置文件自动注入对应的配置值到对应的Bean对象时所采取的绑定是宽松绑定(把中划线-、下划线_ 都去掉,且不区分大小写)但是prefix只能使用纯小写字母,数字,下划线。:SpringBoot松散绑定(宽松绑定)@ConfigurationProperties_spring boot 2.7 @configurationproperties_梨轻巧的博客-CSDN博客
request.getHeader()传入请求头的名,返回请求头的值。

1
2
3
4
5
6
7
8
9
10
11
/判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
使用了 instanceof 运算符,它可以判断一个对象是否属于某个类或接口。
你判断了 handler 是否属于 HandlerMethod 类,它是一个表示控制器方法的对象。
如果 handler 不是 HandlerMethod 的实例,说明当前拦截到的不是控制器方法,
而是其他资源,比如静态文件、视图等。这时,你直接返回 true,表示放行请求,
不进行任何拦截处理。如果 handler 是 HandlerMethod 的实例,说明当前拦截到的
是控制器方法,这时你可以根据你的业务逻辑来决定是否拦截或处理请求。
1
2
3
4
5
6
7
8
9
10
 Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token).getBody();
代码调用 Jwts.parser() 返回一个 JwtParser 对象,用于解析 JWT。

接下来,通过链式调用 setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) 方法设置签名的密钥。secretKey 是一个字符串表示的密钥,通过 getBytes() 方法将其转换为字节数组。

然后,调用 parseClaimsJws(token) 方法,并将 JWT 作为参数传递给该方法。token 是要解析的 JWT 字符串。

image.png

分页查询:

1
2
3
4
5
6
7
8
9
10
请求参数类型为 Query 是指在 HTTP 请求中,将参数以键值对的形式附加在 URL 后面,
用问号(?)分隔,用与号(&)连接。例如:

http://example.com/search?keyword=apple&sort=price

这个 URL 中,keyword 和 sort 就是 Query 参数的名称,apple 和 price 就是 Query 参数的值。
Query 参数可以用来传递一些简单的字符串类型的数据,比如搜索关键词、
排序方式、分页信息等。Query 参数的优点是可以直接在浏览器地址栏中输入和修改,
方便测试和调试。Query 参数的缺点是长度有限制,不能传递复杂的对象类型的数据
,而且会暴露在 URL 中,不适合传递敏感或私密的数据。
1
2
@Data 是一个 Lombok 注解,它可以自动生成一些常见的 Java 类代码,如字段的 getter 和 setter
方法、equals() 和 hashCode() 方法、toString() 方法等
1
log.info("分页查询:{}",employeePageQueryDTO);花括号{}充当变量值的占位符
1
2
3
4
5
6
7
8
9
10
11
12
<select id="pageQuery" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
</where>
order by create_time desc
</select>
//<where> 标签和 <if> 条件判断:用于根据条件动态生成查询语句的 WHERE 子句。
//其中的resultType绑定查询到的每条结果的映射。这里的mapper接口的方法是返回一个
//page<Employee>所以这里的resultType是com.sky.entity.Employee

如何使用分页插件
image.png

1
2
3
4
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
这个方法的第一个参数是查询第几页(第一页为最初页)第二个参数是每页的条数。如果显示的查询为
0页,不会返回查询对象。

image.png
https://www.baeldung.com/spring-httpmessageconverter-rest
利用SpringMVC框架,可以使得我们在开发时,只要在代码中使用@RequestBody和@ResponseBody两个注解,就可以分别完成从请求报文到对象和从对象到响应报文的转换。而在源码内部,其实这种灵活的消息转换机制就是利用HttpMessageConverter来实现的。
HttpMessageConverter的调用是RequestResponseBodyMethodProcessor类的解析请求参数的方法resolveArgument()和处理返回值的方法handleReturnValue()中进行调用的。这是关于@RequestBody和@ResponseBody两个注解的原理。


day3:
AOP,即面向切面编程,是一种编程范式,它可以将一些与业务逻辑无关的功能,如日志、安全、事务等,抽取出来,独立实现,然后在运行时动态地插入到目标对象中,从而实现功能的统一维护和复用https://zhuanlan.zhihu.com/p/395709988。AOP是OOP的延续和补充,它可以解决OOP中的横切关注点问题https://zhuanlan.zhihu.com/p/161705262

AOP的核心概念有以下几个https://bing.com/search?q=AOP%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F&form=SKPBOT

• 切面(Aspect):一个切面包含了一些通知和切点,定义了切面的功能和作用范围。

• 通知(Advice):一个通知是切面要执行的具体操作,比如在方法前后打印日志等。通知有五种类型:前置通知、后置通知、返回通知、异常通知和环绕通知。

• 切点(Pointcut):一个切点是指定哪些连接点需要被通知的条件,比如匹配某些类或方法等。切点可以通过AspectJ的切点表达式语言来定义。

• 连接点(Joinpoint):一个连接点是程序执行过程中的一个点,比如方法调用、异常抛出等。连接点是通知的应用对象。

• 织入(Weaving):织入是将切面应用到目标对象的过程,可以在编译期、类加载期或运行期进行。

AOP的优点有以下几个https://baike.baidu.com/item/AOP/1332219

• 可以提高代码的模块化和复用性,减少代码冗余和耦合。

• 可以提高代码的可读性和可维护性,增加代码的清晰度和一致性。

• 可以提高代码的安全性和性能,增加代码的灵活性和扩展性。
细说Spring——AOP详解(AOP概览)-CSDN博客
@Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME) 是一个 Java 注解,用于指定注解的保留策略(retention policy)。在这个特定的注解中,RetentionPolicy.RUNTIME 表示注解将在运行时保留,可以通过反射来获取注解的信息。
Java 注解的保留策略有三种:

  1. RetentionPolicy.SOURCE:源代码级别的保留策略。注解仅存在于源代码中,在编译后的字节码文件中不可见。
  2. RetentionPolicy.CLASS:类文件级别的保留策略。注解将保留到编译后的字节码文件中,但在运行时不可见。
  3. RetentionPolicy.RUNTIME:运行时级别的保留策略。注解将保留到编译后的字节码文件中,并且在运行时可以通过反射获取注解的信息。

当使用 @Retention 注解时,通过设置 RetentionPolicy 参数来指定所需的保留策略。对于 @Retention(RetentionPolicy.RUNTIME),它表示注解将在运行时保留,并且可以通过反射在运行时访问注解的信息。
以下是一个示例,展示了如何定义一个注解并使用 @Retention(RetentionPolicy.RUNTIME) 设置其保留策略为运行时级别:

1
2
3
4
5
6
7
8
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 注解的元素
String value();
}

在上述示例中,@MyAnnotation 是一个自定义的注解,通过 @Retention(RetentionPolicy.RUNTIME) 设置其保留策略为运行时级别。
通过设置注解的保留策略为运行时级别,可以在程序运行时使用反射来获取注解的信息。例如,可以获取注解的值、解析注解中的元素等。
请注意,注解的保留策略根据不同的使用场景进行选择。根据需要,您可以选择适合您应用程序的保留策略。

IOC和AOP:

IOC:
image.png
添加依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.12</version>
</dependency>

spring-context是Spring框架的一个模块,该模块的功能是实现IOC(控制反转容器),它使用依赖注入来实现控制反转的。在springboot项目中可以不用导入该依赖,因为 spring-boot-starter-* 依赖来自动导入 Spring Framework 的核心模块,包括 spring-context。
依赖注入设计模式:就算将一个类的依赖不用在本类中new,而是交给一个容器来利用反射机制来创建。
一起来学设计模式之依赖注入模式 - 掘金

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;//required是一个方法,用来表示注解元素是否是必须的

}

// 定义Java类的接口及实现类
public interface UserService {
void sayHello();
}


public class UserServiceImpl implements UserService {

@Autowired
private UserService userService1;

@Override
public void sayHello() {
userService1.sayHello();
}
}


public class UserService1Impl implements UserService {

@Override
public void sayHello() {
System.out.println("Hello, userService1");
}
}

// 使用反射机制在IOC容器中创建Java对象
public class BeanFactory {
public static Object getBean(String className) {
Object object = null;
try {
Class clazz = Class.forName(className);
object = clazz.newInstance();// clazz.newInstance()是使用
//无参构造创建实例
} catch (ClassNotFoundException e) {
// handle exception
} catch (InstantiationException e) {
// handle exception
} catch (IllegalAccessException e) {
// handle exception
}
return object;
}
}

// 定义IOC容器类,即存储Java对象的容器
// 对象依赖关系的维护,即在创建对象时,扫描对象的属性并注入依赖的对象
public class Container {
private Map<String, Object> beans = new HashMap<>();
public void register(String beanName, Object bean) {
beans.put(beanName, bean);
}
public Object getBean(String beanName) {
Object object = beans.get(beanName);
// 获取对象的所有属性,通过反射获取所有的成员对象数组
Field[] fields = object.getClass().getDeclaredFields();
// 遍历属性,注入依赖的对象
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
String fieldName = field.getName();
Object dependency = getBean(fieldName);
if (dependency != null) {
try {
field.setAccessible(true);
field.set(object, dependency);
} catch (IllegalAccessException e) {
// handle exception
}
}
}
}
return object;
}
}


public class Main {
public static void main(String[] args) {
Container container = new Container();
// 注册Java对象到IOC容器
container.register("userService", BeanFactory.getBean("com.java.design.dependency.UserServiceImpl"));
container.register("userService1", BeanFactory.getBean("com.java.design.dependency.UserService1Impl"));

// 从IOC容器中获取Java对象并使用
UserService userService = (UserService) container.getBean("userService");
userService.sayHello();

// Hello, userService1
}
}
//后面的userServiceImp1和userServiceImp2互相依赖。但是他们并不都是在直接类中new对方实例;
而是通过BeanFactory.getBean方法,无参构造一个对象然后注入中进行管理。容器根据扫描注入类的
属性,看是否有 @Autowired注解修饰,如果有就通过反射在该对象类创建依赖的对象。

一个类A依赖类B,按理来说类A中的方法要使用到类B的实例,就需要在类A中new一个类B,但是这样类A和类B的耦合度就高了。依赖注入就算让类A不在使用new来声明B实例了,而是在A中的B属性前用@Autowierd 注解表示自动注入。这个时候声明一个容器,这个容器存放类A和类B的实例(一般是无参构造的实例),把A和B放入容器后,容器利用反射机制扫描类A和类B的属性,发现了@Autowired 注解修饰的属性时,这里也就扫描A的属性发现了B属性,然后将B属性的实例名在容器中寻找B实例。将该属性的值赋值为容器中找到的B实例。至于为什么会有依赖注入和IOC的设置我暂且不知道,可能是为了更好的维护吧?
AOP:
Spring中的AOP面向切面编程 - 掘金
AOP的实现是靠动态代理实现的,AOP的动态代理有JDK动态代理和CGLib动态代理。JDK动态代理是实现要代理的类的全部接口,继承了Proxy类;使用JDK动态代理生成的对应的AOP方法(如果该方法在原类不是实现接口的方法)无法对非接口中的方法生效。CGLib是通过操作字节码重写父类的方法生成子类实现的,但是由于重新无法重新fianl修饰的方法,所以该代理生成的AOP方法无法对原类中的final修饰的方法生效。
浅析Spring中AOP的实现原理——动态代理 - 特务依昂 - 博客园
Spring基础 - Spring核心之面向切面编程(AOP)
AOP编程的方法可以自己声明一个注解标记在要进行AOP操作的方法上,写一个@Aspect修饰的处理类。
@Aspect 声明的方法中有一个单独的@Pointcut声明的切入点方法。后面的@Before等 通知的注解的值可以是这个,在相关的通知方法利用反射机制书写特定的逻辑。然后在要进行织入的方法上用该注解修饰。
AOP切面如果书写的切面类的切面点是一个私有方法,那么将无法代理该私有方法,也就是切面方法无法织入到切面点的。JDK代理是生成公共的接口,私有方法不会存在于代理类中,CGLib代理会在生成代理对象的过程中pass掉私有方法。
反射:
Java基础篇:反射机制详解_java反射机制原理详解-CSDN博客
Java 基础 - 反射机制详解
Java虚拟机:对象创建过程与类加载机制、双亲委派模型-CSDN博客
文件上传:
这里使用的阿里云图片上传的OSS技术,其实在图片上传的时候是将图片上传到阿里云的OSS然后在将返回地址返回给前端,然后前端在把图片地址信息和其他相关信息上传给后端,然后后端在数据库当中储存图片的地址。
上传到阿里云OSS成功后但是后面的访问无法访问的问题:
原因是阿里云OSS的文件权限的问题设置为私有的。
image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
MultipartFile 是 Spring Framework 中提供的一个接口,用于处理文件上传的封装。

MultipartFile 接口定义了用于获取上传文件信息和操作文件内容的方法。它是对 HTTP 请求中的文件数据进行封装,使得在 Spring Web 应用中可以方便地处理文件上传操作。

通过使用 MultipartFile,您可以轻松地获取上传文件的原始文件名、文件类型、文件大小以及文件内容。

以下是 MultipartFile 接口的一些常用方法:

getOriginalFilename():获取上传文件的原始文件名。
getContentType():获取上传文件的内容类型。
getSize():获取上传文件的大小。
getBytes():获取上传文件的字节数组。
getInputStream():获取上传文件的输入流,用于读取文件内容。

mybits如果插入的是一个链表:这里的flavors是mapper接口传来的参数,是一个链表。这里使用的是foreach标签来实现循环插入

1
2
3
4
5
6
7
<insert id="insertBatch">
insert into dish_flavor (dish_id, name, value) VALUES
<foreach collection="flavors" item="df" separator=",">
(#{df.dishId},#{df.name},#{df.value})
</foreach>
</insert>

1
2
3
4
5
6
7
8
9
10
11
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)
values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser}, #{status})
</insert>
//insert 是 <insert> 标签的 id 属性,用于标识该插入语句的唯一标识符,以便在其他地方引用。
useGeneratedKeys="true" 是 <insert> 标签的属性之一,用于指示 MyBatis 是否应该返回生成的主键值
。将其设置为 true 表示希望获取自动生成的主键值。
keyProperty="id" 是 <insert> 标签的属性之一,用于指定将生成的主键值设置到哪个属性中。
在这个例子中,生成的主键值将被设置到名为 id 的属性中。
这段代码表明在执行插入操作后,MyBatis 将自动获取生成的主键值,并将其设置到名为 id 的属性中。
这样可以方便地获取插入操作后生成的主键值,以便后续的处理或展示。

最全面的SpringBoot配置文件详解
image.png
image.png
解决IDEAmapper文件报未设置SQL方言警告的方法:
IDEA设置里面选中SQL相关的设置,按照下面的配置:
image.pngimage.png
代码规范:方法注释模板设置,实现在方法头部添加注释实现在注释块添加返回参数类型和方法参数类型
1.进入
- 设置 - 编辑器 - 活动模板
添加如下模板

1
2
3
4
5
6
7
**
$param$
* @return $return$
* @date $date$ $time$
* @description $description$
*/

image.png
编辑环境变量:
image.png

1
groovyScript("def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {result+='* @param: ' + params[i] + ((i < params.size() - 1) ? '\\n ' : '')};return result", methodParameters())  

在方法上面使用注解块的时候/**然后按tab就可以生成了。
mybatis笔记之使用Mapper接口注解 - CC11001100 - 博客园

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
27
28
在 Spring AOP 中,@within 是一个切点表达式,用于选择标记有特定注解的类或类的方法。

@within 切点表达式的语法如下:

Copy
@within(annotationType)
其中,annotationType 是一个注解类型的名称,用于选择标记有该注解的类或类的方法。

以下是一个示例,展示如何在 Spring AOP 中使用 @within 切点表达式:

java
Copy
@Aspect
public class MyAspect {

@Pointcut("@within(org.springframework.stereotype.Controller)")
public void controllerMethods() {}

@Before("controllerMethods()")
public void beforeControllerMethod(JoinPoint joinPoint) {
// 在标记有 @Controller 注解的类的方法执行前执行
// ...
}
}
在上述示例中,我们定义了一个切点 controllerMethods(),使用 @within(org.springframework.stereotype.Controller) 来选择标记有 @Controller 注解的类或类的方法。然后,我们在 beforeControllerMethod() 方法上使用 @Before 注解来指定在符合切点条件的方法执行前执行的通知。

这样,当任何标记有 @Controller 注解的类的方法被调用时,beforeControllerMethod() 方法将会在方法执行前被调用。
需要注意的是,切点表达式中的注解类型名称应使用全限定名,例如 org.springframework.stereotype.Controller。

mybatis笔记之使用Mapper接口注解 - CC11001100 - 博客园

第二部分:

redis:

redis设置远程连接:
redis远程连接配置文件如果远程连接中的时候要关闭掉原来的redis-serve进程,但是这个时候用kill-9命令却是无法关闭掉redis-server的时候可以用service redis-server stop命令关闭redis-server进程,然后在使用redis-server “新的redis配置文件的路径”启动redis.之所以上面的kill -9无法关闭启动了的redis的原因是:redis将启动设置写入了一个配置文件中了的,这个时候关闭了redis,相应的配置文件会利用其父进程生成新的redis-server
redis操作:
Spring Data Redis 最佳实践! - 掘金
spring boot整合redis ---- RedisTemplate 三分钟快速入门-开源基础软件社区-51CTO.COM
IOC容器中注入了2个一样的Bean

1
2
3
4
5
6
如果向Spring的IOC容器中注入两个同类的Bean,会发生什么呢?这取决于您如何配置和使用这两个Bean。一般来说,有以下几种可能的情况:

• 如果您为这两个Bean指定了不同的id或name属性,那么它们就是不同的Bean实例,可以通过它们的id或name来区分和引用
• 如果您为这两个Bean指定了相同的id或name属性,那么后面定义的Bean会覆盖前面定义的Bean,只有后面定义的Bean会被创建和使用
• 如果您没有为这两个Bean指定id或name属性,那么它们就是匿名Bean,不能直接被引用,但可以作为其他Bean的依赖注入
• 如果您想要在一个地方使用这两个Bean,那么您需要使用@Qualifier注解或其他方式来指定您想要使用哪一个Bean,否则会出现歧义(NoUniqueBeanDefinitionException)
1
2
3
Bean中如果注入的是方法返回的bean,那么这个bean的id默认为方法名,如果容器中已经存在同Id的bean。
那么后面注入的bean会覆盖前面的;常见的@configuration就是这样实现对原本自动注入容器中的bean
进行覆盖的。如果注入的是类,那么该bean的id是全类名

同名bean:

SpringBoot基础篇之重名Bean的解决与多实例选择 - 掘金
同名bean:多个bean 有相同的 name 或者 id,称之为同名bean
bean 的id 和 name的区别
id和name都是spring 容器中中bean 的唯一标识符

  1. id: 一个bean的唯一标识 , 命名格式必须符合XML ID属性的命名规范
  2. name: 可以用特殊字符,并且一个bean可以用多个名称:name=“bean1,bean2,bean3”
    ,用逗号或者分号或者空格隔开。如果没有id,则name的第一个名称默认是id

spring 容器如何处理同名bean?

  • 同一个spring配置文件中,bean的 id、name是不能够重复的,否则spring容器启动时会报错。
    如果一个spring容器从多个配置文件中加载配置信息,则多个配置文件中是允许有同名bean的,并且后面加载的配置文件的中的bean定义会覆盖前面加载的同名bean。
    1、在spring同一个配置文件中,不能存在id相同的两个bean,否则会报错。
    2、在两个不同的spring配置文件中,可以存在id相同的两个bean,启动时,不会报错。这是因为spring
    ioc容器在加载bean的过程中,类DefaultListableBeanFactory会对id相同的bean进行处理:后加载的配置文件的bean,覆盖先加载的配置文件的bean。DefaultListableBeanFactory类中,有个属性allowBeanDefinitionOverriding,默认值为true,该值就是用来指定出现两个bean的id相同的情况下,如何进行处理。如果该值为false,则不会进行覆盖,而是抛出异常。

spring 容器如何处理没有指定id、name属性的bean?
如果 一个 bean 标签未指定 id、name 属性,则 spring容器会给其一个默认的id,值为其类全名。
如果有多个bean标签未指定 id、name 属性,则spring容器会按照其出现的次序,分别给其指定 id 值为 “类全名#1”, “类全名#2”
如下:
配置文件:

1
2
3
4
5
6
7
8
9
10
11
<bean class="com.xxx.UserInfo">  
<property name="accountName" value="no-id-no-name0"></property>
</bean>

<bean class="com.xxx.UserInfo">
<property name="accountName" value="no-id-no-name1"></property>
</bean>

<bean class="com.xxx.UserInfo">
<property name="accountName" value="no-id-no-name2"></property>
</bean>

获取bean的方式:

1
2
3
UserInfo u4 = (UserInfo)ctx.getBean("com.xxx.UserInfo");  
UserInfo u5 = (UserInfo)ctx.getBean("com.xxx.UserInfo#1");
UserInfo u6 = (UserInfo)ctx.getBean("com.xxx.UserInfo#2");
1
2
3
4
5
当一个类被标记为 @Configuration 时,它就变成了一个配置类,可以用来定义 beans、配置组件扫描、
声明依赖关系等。配置类主要用于替代传统的 XML 配置文件,通过 Java 代码方式进行配置。
@Configuration 注解告诉 Spring 框架这个类是一个配置类,Spring 在启动时会扫描这个类,
并根据其中的配置信息来创建和初始化相应的 beans。通过配置类的方式,
可以更加灵活和可读性强的方式来配置和组织应用程序。

【小家Spring】Redis序列化、RedisTemplate序列化方式大解读,介绍Genericjackson2jsonredisserializer序列化器的坑-腾讯云开发者社区-腾讯云
IDEA无法连接远程的redis解决方法:
我这里的原因是因为版本不兼容的原因造成的,也就是springboot和redis中的版本不兼容造成无法连接的。就不该在这上面浪费那么多时间的!!!下次在选择软件搭配的时候,第一件事情就是想着版本搭配。
image.png
image.png
消息转换器:
Spring Mvc:HttpMessageConverter 消息转换器 - 掘金
SpringBoot消息转换器:HttpMessageConverter-CSDN博客
HttpClient
从零开始 Spring Boot 15:Http Client - 红茶的个人站点
【HttpClient】在 SpringBoot 中使用 HttpClient 实现 HTTP 请求_springboot使用httpclient-CSDN博客
Just a moment…
springboot实战之常用http客户端整合-腾讯云开发者社区-腾讯云
Sending HTTP requests with Spring WebClient

小程序开发:

1.小程序中的json不能有注释。

查看appid和appsecret的方法:

登录:微信公众平台
image.png
SpringBoot教程(十四) | SpringBoot集成Redis(全网最全) - 掘金
springboot在使用redis的时候导入了相关的依赖的时候,这个依赖里面有相关的配置类和配置文件中的redis配置信息相关联,redis会根据导入的客户端类来判断redis使用的客户端是什么,是根据@ConditionalOnClass组件来判断加载那种客户端。可以自己配置redis的配置类从而覆盖依赖导入的配置类,从而实现修改redis序列化时自己的序列化方式。redis导入依赖的时候就把redisTemplate类注入到容器了,在控制层使用redisTemplate的时候可以使用@AutoWired 的方式注入该依赖。

1
2
3
Set keys = redisTemplate.keys(pattern);
keys(pattern)是RedisTemplate中的一个方法,它可以根据一个通配符模式来查询匹配的key集合,
返回一个Set类型的结果。

SpringCache:

1
2
3
4
5
6
7
8
  @Insert("insert into user(name,age) values (#{name},#{age})")
@Options(useGeneratedKeys = true,keyProperty = "id")
void insert(User user);
@options指定SQL语句的其他选项。
useGeneratedKeys 设置为"true" 表明要 MyBatis 获取由数据库自动生成的主键。
keyProperty=“eqCsImgId” 指定把获取到的主键值注入到相对应实体类中 eqCsImgId属性。
keyColumn="eq_cs_img_id指定数据中自增主键的名称。

1
2
@CachePut(value = "userCache", key = "#user.id")

springboot缓存:

IDEA的插件有时候会因为版本原因等问题导致插件是无法正常工作的。这个时候在排查错误的时候如果实战没有发现错误的时候,一定不要忘了可能是插件的版本或者插件中存在的问题。
image.png
image.pngimage.png

@Caching(参数)

用于同时添加多个缓存注解,比如:

1
2
3
4
5
6
@Caching(evict={
@CacheEvict(...)
@CacheEvict(...)
...
})

1
2
3
4
5
6
   @Cacheable(cacheNames = "user" ,condition="#id!=null")
public User getById( Long id){
这里的cacheNames和上面的vaule是一样的。
这里的condition的意思是要缓存的是下面方法的参数id但是该id不能为null
`condition``@Cacheable` 注解的一个参数属性,用于指定缓存条件。
如果指定了 `condition` 属性,则只有当条件表达式为 `true` 时才会进行缓存操作

Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍
SpringBoot使用Spring缓存注解_org.springframework.cache.annotation.abstractcachi-CSDN博客
mybatis之多行插入(批量操作)_mybatis多行插入_百日梦想家的博客-CSDN博客
mysql插入语句
mybatis之foreach用法 - Boblim - 博客园
Mybatis——foreach用法_mybatis foreach_火山彬的博客-CSDN博客
mybatis foreach标签的使用-CSDN博客
数据库中表关系中的主键和外键设置后,删除表的时候特别不方便。删除主键表必须要把其关联的外键表删除了!这个时候为了方便删除表,可以在业务层也就是用代码对插入删除进行特殊的处理让他们(这些表)处于一种逻辑上面的主键和外键关系。
springTask:
mybatis的Update之if标签的使用-CSDN博客
Spring Boot 集成 WebSocket(原生注解与Spring封装)_springboot集成websocket-CSDN博客
一文吃透 WebSocket 原理 刚面试完,趁热赶紧整理 - 掘金
https://echarts.apache.org/handbook/zh/get-started/?sid=iDR6W2