一:Java语法
1.1、注解
注解困扰了我很长时间,看了一堆概念。要理解注解,首先得理解两个概念元数据和反射机制
元数据是关于数据的数据。它提供了关于其他数据的信息或描述。例如,在数据库中,记录的结构(字段类型、字段名称等)就算是元数据。
反射机制是一种功能,使得程序能够在运行时获取类的信息(例如类名、方法、属性等),并可以动态调用这些类的方法或访问其属性。
注解可以看作是特殊形式的元数据,常与反射结合使用,允许根据注解的信息动态处理代码逻辑。
如下是标准概念:
Java 注解是一种特殊的语法,用于向代码添加元数据。简单来说,注解就像是给代码加上标签,提供额外的信息。这些信息可以在编译时、运行时或通过工具处理时被利用,但注解本身不会直接影响代码的执行。
1.1.1、@RequestMapping
@RequestMapping
注解用于定义一个可以被 HTTP 请求访问的控制器方法。当 JavaScript 向 /sys/gtVal
发送 POST 请求时,Spring 框架会根据请求的路径和方法将其映射到 login
方法上,并执行该方法。
@RequestMapping(value = "/sys/gtVal", method = RequestMethod.POST)
public V login() {
...
}
该注解还分类级、方法级别
- 类级别的
@RequestMapping
用于定义一个基础路径,所有在该类中的方法都将基于这个路径展开。 - 方法级别的
@RequestMapping
则用于细化具体的路径及请求方式。
@RequestMapping("/miniapp")
public class MiniappSqykzController {
@RequestMapping(value = "/miniList", method = RequestMethod.GET)
public List miniList(@RequestParam("type") String type) {
...
}
}
上面代码,它的完整URL就是/miniapp/miniList
。
1.1.1.1、Post/GetMapping
@PostMapping
与@GetMapping
是@RequestMapping
的特化用法,更简洁易读。@PostMapping
用于处理 HTTP POST 请求,@GetMapping
用于处理 HTTP GET 请求。
1.1.1.2、发起与接收请求
@GetMapping
、@PostMapping及@RequestMapping
一般都是用来接收请求的。- 当与 HTTP 客户端库(如 Feign)配合时,是用来发起请求的。
@FeignClient(value = "service-provider")
public interface ApiProviderFeign {
@PostMapping("/api/service/provider/getUserInfo")// 此处发起请求
Map<String, String> getUserInfo(@RequestParam("username") String username);
}
1.1.2、@Override
@Override
注解是 Java 中的一个标记注解,用于指示一个方法正在重写父类中的方法。它可以帮助编译器检查是否正确地重写了方法,并使代码更具可读性。使用 @Override
注解的优点包括:
-
编译时检查:如果方法名或参数不匹配,编译器会报错,帮助避免潜在的错误。
-
代码可读性:明确表明该方法是重写的,有助于其他开发者理解代码。
-
维护性:在将来的代码更改中,确保方法的重写关系不被意外破坏。
常见用法示例:
class Parent {
void display() {
System.out.println("Display from Parent");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("Display from Child");
}
}
在上面的例子中,Child
类重写了 Parent
类的 display
方法。在重写时使用 @Override
注解是一个良好的编程习惯。
1.1.3、@FeignClient
@FeignClient
是 Spring Cloud Feign 模块中的一个注解,用于声明一个 Feign 客户端。Feign 是一个声明式的 Web 服务客户端,可以帮助简化 HTTP API 的调用。使用 @FeignClient
注解,开发者能够通过接口定义与其他服务的 RESTful API 的交互,以下是关键点:
-
声明式 HTTP 客户端:通过接口定义 API 方法,Feign 会为这些方法自动生成 HTTP 请求。你只需关注接口的设计,而无需处理底层的 HTTP 细节。
-
简化的配置:可以通过使用注解,如
@GetMapping
、@PostMapping
等,指定 HTTP 方法和路径。Feign 会自动实现这些方法。 -
服务发现:与 Spring Cloud Eureka 集成时,可以在
@FeignClient
中指定服务名,Feign 会根据服务名称动态解析其具体地址。 -
负载均衡:结合 Ribbon 等负载均衡组件,Feign 支持自动负载均衡。
-
集成其他特性:可以与 Spring Cloud 的其他功能(如 Hystrix、熔断器)集成,以增强服务调用的可靠性。
示例:
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
在这个例子中,UserServiceClient
接口通过 @FeignClient
注解定义与名为 user-service
的服务的交互,并使用 @GetMapping
指定具体的 API 路径。
-
@FeignClient
注解:用于定义一个 Feign 客户端,以便在服务之间进行 HTTP 调用。Feign 客户端允许你通过接口的形式来调用其他微服务的 RESTful API。 -
name
属性:name
属性指定了 Feign 客户端的名称。在这里,user-service
通常代表另一个微服务的名称,这个名称通常是在服务注册中心(例如 Eureka)中注册的服务ID。通过这个名称,Feign 可以找到并与对应的服务进行通信。
1.1.4、@RequestBody
@RequestBody
是 Spring 框架中用于处理 HTTP 请求体的一个注解。它通常用于将 HTTP 请求的正文内容绑定到控制器方法的参数上。这个注解可以应用在方法的参数上,也可以应用在整个类上,以指示 Spring MVC 如何处理该参数或整个请求体。
以下是 @RequestBody
的一些常见用法和示例:
假设你有一个控制器方法需要从请求体中接收 JSON 数据并将其转换为 Java 对象,你可以使用 @RequestBody
来实现这一点:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class MyController {
@PostMapping("/user")
public User createUser(@RequestBody User user) {
// 处理用户数据
return user;
}
}
在这个示例中,当客户端发送一个包含 JSON 数据的 POST 请求到 /api/user
时,Spring 会自动将 JSON 数据反序列化为 User
对象,并传递给 createUser
方法。
1.1.5、@ResponseBody
@ResponseBody是Spring MVC框架中的一个核心注解,用于指示方法的返回值应该直接写入HTTP响应体中。
在Spring框架中,@ResponseBody注解的使用非常关键,它允许开发者轻松地将方法的返回值直接写入HTTP响应体中。这对于构建RESTful Web服务尤其重要,因为它允许以一种声明式的方式处理JSON、XML等格式的数据。通过使用@ResponseBody注解,开发者可以简化开发过程,专注于业务逻辑的实现,而无需关心数据的序列化和传输细节。
此外,当使用@RestController注解时,所有方法都隐式地具有@ResponseBody效果,但在非@RestController类中需要显式添加此注解。这意味着在@RestController类中,即使没有明确标注@ResponseBody,方法的返回值也会被自动序列化为JSON或XML格式,并写入HTTP响应体中。而在其他类型的控制器类中,则需要在方法上明确标注@ResponseBody以实现相同的效果。
1.1.6、@SysLog
@SysLog("banner列表")
1.2、反射
反射是指java在运行时允许查询、访问和修改类、接口、字段和方法的信息。反射提供了一种动态地操作类的能力,这在很多框架和库中被广泛使用,例如Spring框架的依赖注入。
这个“运行时”是指Java虚拟机(JVM)执行字节码后的阶段,也就是当程序已经被编译成.class文件,并在JVM中运行时。
java代码在计算机中经历的三个阶段
Java中的反射概念允许程序在运行时获取类的信息和操作对象。而在JavaScript中,虽然没有严格意义上的“反射”,但可以通过一些内置对象和方法实现类似的功能。
在JavaScript中,常用的相关概念包括:
typeof
:获取变量的数据类型。instanceof
:检查对象是否是某个构造函数的实例。Object.keys()
、Object.values()
、和Object.entries()
:获取对象的键、值或键值对。Reflect
:ES6引入的Reflect对象,提供了很多与对象操作相关的静态方法,支持一些类似反射的功能。
通过这些特性,JavaScript能够在运行时动态访问和操作对象的属性和方法。JS中感觉正常到都忽略了其存在的用法,在java中竟然被提升到一个专门的语法概念来讲了。
反射优秀博文:JAVA反射机制_java的反射机制-优快云博客
1.3、HashMap
HashMap就是键值对,与es6中Map类似。
添加元素使用put()
方法,获取元素使用get()
方法,删除元素使用remove()
方法,遍历元素可以使用增强的for循环或迭代器。
1.4、函数
在 Java 中,不同的函数声明有不同的目的和使用场景。
-
public static void main(String[] args)
:- 这是 Java 应用程序的入口点。
main
方法是程序启动时执行的特殊方法。 public
:表示该方法可以被任何其他类访问。static
:意味着该方法属于类本身,而不是类的实例,可以直接通过类名调用。void
:表示该方法不返回任何值。String[] args
:用于接收命令行参数的字符串数组。
- 这是 Java 应用程序的入口点。
-
public R pwdLogin(String[] args)
:- 这是一个普通的实例方法(如果没有
static
关键字)。它执行特定的功能,例如用户登录。 R
是该方法的返回类型,表示该方法将返回一个R
类型的对象。- 方法中包含若干参数,用于接收用户名、密码、动作名称以及 HTTP 请求和响应对象。
- 这是一个普通的实例方法(如果没有
总结:
main
方法是程序的入口点,而其他方法(如pwdLogin
)用于实现特定功能。- 返回类型(如
void
或R
)决定了该方法是否返回结果以及返回的结果类型。 - 参数数量和类型根据方法的需要而变化。
1.5、文件类型
1.5.1、properties文件
在Java编程中,pro.properties文件通常是一个属性文件,用于存储应用程序的配置信息。这种文件格式简单易读,每行包含一个键值对,以等号(=)分隔,如key=value
。属性文件广泛应用于各种场景,如数据库连接配置、系统设置、国际化资源等。
1.6、package
java文件首行中package语句是不是可有可无?该语句只是为了便于阅读代码?
在Java中,package
语句并不是可有可无的。它用于声明一个类所属的包(package)。包是Java中的一种命名空间机制,用于组织类和接口,避免命名冲突。
作用
- 组织代码:通过包可以将相关的类和接口组织在一起,使项目结构更加清晰。
- 命名空间管理:不同包中的类可以有相同的名字,而不会冲突。例如,两个不同的包中都可以有一个名为
MyClass
的类。 - 访问控制:包还可以用于控制类的访问权限。默认情况下,只有同一个包内的类才能互相访问,除非使用特定的访问修饰符(如
public
、protected
)。
总结
- 必须性:
package
语句不是可有可无的,它是Java语言的一部分,用于定义类的包。 - 功能:它不仅有助于代码的组织和管理,还提供了一种有效的命名空间管理方式。
- 可读性:虽然
package
语句本身并不直接影响代码的运行,但它提高了代码的可读性和可维护性。
一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
同一个package下的类之间互相调用时,不需要使用import语句。这是因为在同一个包内,所有的类都是可见的,可以直接通过类名进行访问。js中没有包的概念,java的包真好用!
import com.runoob.mypackage.*;
这样,你可以导入 com.runoob.mypackage 包中的所有类,从而在当前源文件中使用该包中的任何类的方法、变量或常量。注意,使用通配符 * 导入整个包时,只会导入包中的类,而不会导入包中的子包
1.7、接口Interface
让我们来看一个例子,这个例子创建了一个叫做animals的包。通常使用小写的字母来命名避免与类、接口名字的冲突。
在 animals 包中加入一个接口(interface):
Animal.java 文件代码:
/* 文件名: Animal.java */
package animals;
interface Animal {
public void eat();
public void travel();
}
接下来,在同一个包中加入该接口的实现:
MammalInt.java 文件代码:
package animals;
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
然后,编译这两个文件,并把他们放在一个叫做animals的子目录中。 就是包和接口的实践了
1.8、数据类型
1.8.1、数组
java中数组的声明方式跟js中有啥区别?
1. 声明方式
Java:
int[] numbers = new int[5]; // 声明一个整型数组,长度为5
String[] names = {"Alice", "Bob", "Charlie"}; // 使用初始化列表
JavaScript:
let numbers = new Array(5); // 声明一个数组,长度为5
let names = ["Alice", "Bob", "Charlie"]; // 使用初始化列表
2. 类型
-
Java:强类型语言,数组必须指定类型,如
int[]
,String[]
等。 -
JavaScript:动态类型语言,数组可以包含多种类型的数据。
3. 长度
-
Java:数组长度在创建时固定,不能动态改变。
-
JavaScript:数组的长度是动态的,可以随时添加或减少元素。
4. 方法和属性
- Java:数组是对象,但更像基本数据结构,没有太多内置方法。
- JavaScript:数组是对象,拥有丰富的内置方法(例如
push()
,pop()
,map()
,filter()
5.返回值
Java中返回空数据不像JS中return [],这么随意。如下
if (whiteArr == null) {
return new String[0];
}else {
return whiteArr;
}
1.8.2、字符串
1.8.2.1、char和String的区别?
Java中String跟JS区别较大,JS中较粗暴String可以是任意长度的,也统一。java则区分较细,如下
字符:char
使用单引号,长度为1的单个字符。操作方法很少
字符串:String
使用双引号,包含多个字符。操作方法较多
1.8.3、数据结构
数组(Array):
int[] array = new int[5];
- 特点: 固定大小,存储相同类型的元素。
- 优点: 随机访问元素效率高。
- 缺点: 大小固定,插入和删除元素相对较慢。
列表(List):
List<String> sites = new ArrayList<String>();
sites.add("Google");
sites.get(0); // 访问第1个元素
- 特点: 动态数组,可变大小。
- 优点: 高效的随机访问和快速尾部插入。
- 缺点: 中间插入和删除相对较慢。
Array与List的区别在于一个固定大小,一个动态数组
映射(Map):
js中的对象,对应java中的map(键值对)
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
hashMap.put("one", "Google");
hashMap.put("two", "Runoob");
hashMap.get('one'); // "Google"
...详情参考:Java 数据结构 | 菜鸟教程
1.9、interface
接口本身并不能被调用,必须通过实现它的类或代理对象来进行调用。
二:Idea
2.1、项目启动
从git获取到项目代码后,用idea打开。
- 安装依赖
- 完成Marven/JDK等配置
- 检查数据库配置
- 启动相关服务
安装依赖
如果个别依赖从私服下载不了,可以去maven官网下载补充。
如果run时提示程序包xx不存在,在项目目录右键Marven->ReLoad Project,重新下载依赖。
数据库配置
数据库连接如果异常,可在idea服务器连接工具完成测试工作。
启动相关服务
无服务架构的项目,一般需要多个服务来支持应用。注意别忘了注册中心,比如zookeeper或Eureka
2.2、启动报错汇总
2.1.1 compiler error
这个错是我在第一次导入团队代码后,run services时发生的,完整报错内容为
java: Compilation failed: internal java compiler error
报错信息翻译下就是编译异常,个人觉得以上报错主要原因可能是sdk不一致,或者是build时分配的内存太小。定位起来较繁琐,我花了一上午折腾。
解决办法:
1、查看项目的 jdk(Ctrl+Alt+shift+S)
File ->Project Structure->Project Settings ->Project
2、查看工程的 jdk(Ctrl+Alt+shift+S)
File ->Project Structure->Project Settings -> Modules -> (需要修改的工程名称) -> Sources ->
3、查看 idea 中 Java 配置
File ->Setting ->Build,Execution,Deployment -> Compiler -> Java Compiler
上面三步做完还是失败的话
清除 IDEA 缓存 重启 IDEA
File->Invalidate Caches/Restart
还有一种情况是因项目过大,需要修改这个堆的大小
解决方法如下:
在 setting-->Build,Execution,Deployment-->Compiler 中找到 build process heap size(Mbytes):700 改为 1024(视情况而定)
(我就是这个原因造成的,改了一下堆大小就好了)
2.1.2 Unable start Tomcat
Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
考虑是否数据库连接失败,可在本地测试,确保正常连接后重启服务。有的数据库有网络要求,需要代理文件 。
数据库参数一般在application.properties文件中,datasource.slave.username配置中。
2.1.3 部分服务没有端口号
删除缓存后,重启服务可解决。File->Invalidate Caches,打开后全选所有选项,可彻底删除缓存。
2.3、搜索
"Find in Files"和双击Shift键(Search Everywhere)都是IntelliJ IDEA中强大的搜索工具,但它们的使用场景和目的有所不同。"Find in Files"更适合于在项目中查找特定的文本或代码片段,而双击Shift键(Search Everywhere)则提供了一种更广泛的搜索方式,可以帮助你快速找到几乎所有类型的元素。
Find in Files快捷键Ctrl shift F,如果无效,找Edit->find->find in files
2.4、自动补全
controller中直接调用某个类中没有的方法test,该方法会飘红。鼠标悬停在方法名称上会有Create method ’test‘提示,点击该提示会自动在该类中创建此方法。
interface中添加一个函数,实现类文件名称会飘红。在该文件中使用快捷键ctrl + i, 然后Enter即可。注意自动生成的函数没有@RequestBody,需要手动添加。
2.5、debugger
平时启动服务时不要选择run,直接选择debugger。然后可以随时在任何一行代码上添加红点开始调试,爽的一笔。
进入断点后的操作,如下图
上图的停止会结束服务,如果想在调试中途结束调试,但不结束服务。可点击红色方块下2位的三角符号(resume program)。
2.6、数据库可视化工具
在哪里写sql?
库名右键->New->Query Console
三:框架/工具
3.1、SpringBoot框架结构
在 Spring Boot 或微服务架构中,每个服务的文件目录结构通常遵循一定的约定。以下是一个常见的 Spring Boot 服务目录结构示例,以及各个文件和目录的简要说明:
my-service
│
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── myservice
│ │ │ ├── MyServiceApplication.java # 启动类
│ │ │ ├── controller # 控制器包
│ │ │ │ └── MyController.java # 处理请求的控制器
│ │ │ ├── service # 服务包
│ │ │ │ └── MyService.java # 业务逻辑
│ │ │ ├── repository # 数据访问包
│ │ │ │ └── MyRepository.java # 数据访问层
│ │ │ └── model # 模型包
│ │ │ └── MyModel.java # 实体类
│ │ └── resources
│ │ ├── application.properties # 配置文件
│ │ └── static # 静态资源(如 HTML, CSS, JS)
│ │ └── index.html # 主页
│ └── test
│ └── java
│ └── com
│ └── example
│ └── myservice
│ └── MyServiceApplicationTests.java # 测试类
│
├── pom.xml # Maven 项目管理文件
|---target marven 生成的文件
└── README.md # 项目说明文件
主要目录和文件说明:
-
src/main/java
: 存放 Java 代码的主目录。MyServiceApplication.java
: Spring Boot 启动类,包含main
方法。controller
: 处理 HTTP 请求的控制器类。service
: 包含业务逻辑的服务类。repository
: 数据访问层,通常接口定义,与数据库交互。model
: 存放实体类,定义核心数据模型。
-
src/main/resources
: 存放资源文件的目录。application.properties
: 应用配置文件,定义数据库连接、端口等配置。static
: 静态资源目录,存放前端资源。
-
src/test/java
: 存放测试代码的目录。MyServiceApplicationTests.java
: 单元测试类,用于测试应用功能。
-
pom.xml
: Maven 项目的配置和依赖管理文件。 -
README.md
: 项目的说明文档。 -
target:项目构建的输出结果存放位置,构建、测试和其他过程产生的文件都集中在这里。每次执行 Maven 构建命令(如 mvn clean 或 mvn package)时,target 目录会被重新生成。
这个结构可以根据项目的具体需求做相应调整,但以上是一个基本的布局示例。
3.2、External Libraries
java中external libraries跟前端项目中 node_modules作用与概念一致,都是依赖包或者说外部库
具体使用上有区别,如下
- Java项目:外部库由构建工具Maven等管理,不直接放在项目根目录中;依赖关系通过
pom.xml
或build.gradle
文件定义。 - 前端项目:依赖库直接下载到项目根目录中的
node_modules
文件夹中;依赖关系通过webpack等工具,package.json
文件定义。
3.3、Maven
Java开发中的Maven就类似前端开发中的webpack
Apache Maven 是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。官网 :http://maven.apache.org
3.4、MyBatis
mybatis是一个优秀的基于java的持久层框架,它内部封装了 jdbc,使开发者只需要关注sql语句本身,而不需要花费精力 去处理加载驱动、创建连接、创建statement等繁杂的过程。
它支持定制化 SQL、存储过程以及高级映射。MyBatis 可以简化数据库访问操作,提供了以下主要功能:
-
SQL 映射:用户可以直接在 XML 文件或注解中定义 SQL 语句,从而控制 SQL 的执行。同时,MyBatis 还支持动态 SQL 功能。
-
对象关系映射:MyBatis 能够将数据库结果集自动映射到 Java 对象,支持复杂数据类型的映射。
-
灵活性:MyBatis 则没有强制要求使用特定的编程模型,开发者可以自由选择 SQL 语句的编写方式,更符合项目需求。
-
支持多种数据库:MyBatis 可以与多种关系型数据库搭配使用,适应性强。
-
缓存机制:MyBatis 提供了一级缓存和二级缓存,旨在提高数据访问性能。
MyBatis 适合于需要复杂 SQL 操作的项目,开发者可以对 SQL 语句进行精细控制。与其他 ORM 框架相比,MyBatis 更加轻量级和灵活,但需要开发者自己管理 SQL 逻辑。
3.5、框架专有名词
SpringBoot代码结构中启动类,controller控制器、service服务、repository数据访问、model模型,这些专有名称怎么理解?前端有没有类似的概念可以类比着理解?
以下是每个部分的简要说明,以及与前端的类比:
控制器 (controller
)
- 作用:控制器负责前端HTTP请求处理和路由,并调用相应的服务。
- 类比:可以类比于前端的路由组件(如 React Router 中的路由),负责处理用户的请求和展示对应的视图。
服务 (service
)
- 作用:包含业务逻辑,处理来自控制器的请求,进行数据处理,然后调用数据访问层(Repository)。
- 类比:类似于前端的服务模块(如 API 调用逻辑),负责处理请求的具体实现,比如数据获取和处理。
数据访问 (repository
)
- 作用:与数据库交互,负责执行 CRUD(创建、读取、更新、删除)操作。
- 类比:可以与前端的 API 请求管理工具(如 Axios 或 Fetch API)相对应,这些工具用于与后端接口交互并获取数据。
模型 (model
)
- 作用:定义数据模型,通常对应数据库表的结构,包含数据的属性和验证规则,包装数据的对象。
- 类比:类似于前端的状态管理(如 Redux 的
store
),用来定义和管理应用的数据结构。
启动类 (MyServiceApplication.java
)
- 作用:这是应用的入口,包含
main
方法,负责启动 Spring Boot 应用。 - 类比:类似于前端应用的入口文件(如
index.js
或main.js
),是应用加载的起点。
假设你有一个获取用户信息的请求:
- 前端向
/users/{id}
发送 GET 请求。 - 控制器的
getUserById
方法被调用。 - 控制器调用服务的
findById
方法,传入用户 ID。 - 服务通过调用仓储(Repository)的
findById
方法查询数据库。 - 仓储(Repository)返回用户信息给服务,服务处理后返回给控制器。
- 控制器将用户信息返回给前端。
这种设计遵循了分层架构的原则,各层关注不同的职责,使代码更加模块化和易于维护。
3.6、微服务
微服务相关知识有优秀系列博文分享给大家,我也看的这些:地址
3.7、Eureka
负责服务的注册与发现,使得服务能够找到彼此。
3.8、Fegin
Fegin提供了一种声明式的 HTTP 客户端调用方式,简化了 RESTful 服务的调用。使用 Feign 可以让你的代码更加简洁易读,并自动处理负载均衡和服务调用的细节。
3.8.1、有Eureka还需Fegin吗?
在 Spring Boot 中使用 Eureka 进行服务注册和发现,再使用 RestTemplate 或其他手动的 HTTP 客户端进行服务调用,可以解决服务启动和互相调用的问题。但 Feign 代替手动,实现服务的自动调用。
3.8.1.1、实例Demo
实例地址:微服务组件之OpenFeign服务调用组件的介绍及其使用方法
如上示例虽然写的很清楚,但是我对了七八遍还是没看懂。如下为AI帮我梳理的注解
当你通过浏览器或工具请求 http://localhost:7070/api/service/consumer/getUserInfo?username=root
时,执行过程如下:
3.8.2.1. 接收请求
- 请求首先被发送到
service-consumer
微服务。- 因为请求的 URL 匹配到
DemoController
的@GetMapping
注解,Spring 会调用getUserInfo
方法。3.8.2.2. 调用 Feign 客户端
- 在
DemoController
的getUserInfo
方法中,使用apiProviderFeign.getUserInfo(username)
调用 Feign 客户端。username
被传递为root
。3.8.2.3. 发送 Feign 请求
- Feign 客户端
ApiProviderFeign
收到请求,并将其转换为对service-provider
微服务的 HTTP POST 请求。- 请求的 URL 为
http://localhost:8081/api/service/provider/getUserInfo
(假设service-provider
在 8081 端口)。- 请求 Body 中包含参数
username=root
。3.8.2.4. 处理服务提供者请求
service-provider
微服务接收到请求并匹配到ApiController
的getUserInfo
方法。- 方法内部创建一个
Map
,并将username
和一些静态值放入其中。3.8.2.5. 返回结果
getUserInfo
方法返回一个包含用户信息的Map
(例如:{"id": "1001", "username": "root", "pass": "123456"}
)。- Feign 客户端将接收到的结果转换为一个
Map<String, String>
对象,并返回给DemoController
。3.8.2.6. 返回响应给客户端
DemoController
中的getUserInfo
方法将结果返回给最初发起请求的客户端(浏览器或 HTTP 客户端)。
3.8.1.2、Demo的接口
理解 ApiProviderFeign
需要明确几个概念:
3.8.3.1. 接口的作用
ApiProviderFeign
是一个声明式的 Feign 客户端接口,主要用于定义如何调用外部服务(即service-provider
)。它并不需要实现具体的方法,因为 Feign 会在运行时为这个接口创建代理对象。3.8.3.2. 实现和动态代理
尽管
ApiProviderFeign
是一个接口,但在 Spring Cloud 的上下文中,Feign 会根据这个接口自动生成一个实现类。所以,你不需要手动实现ApiProviderFeign
,也不需要看到其实现代码。
- 当你在代码中使用
@FeignClient
注解时,Feign 会扫描并创建一个实现了ApiProviderFeign
接口的动态代理对象。3.8.3.3. 如何调用接口
- 在
DemoController
中,apiProviderFeign
是通过 Spring 的依赖注入 (@Autowired
) 获取到的。其实是获取到了 Feign 代理对象,它实现了ApiProviderFeign
接口。- 调用
apiProviderFeign.getUserInfo(username)
实际上是调用这个动态代理对象的方法,而这个方法的逻辑是内部通过 HTTP 调用service-provider
的相应接口。3.8.3.4. 调用方式
接口本身并不能被调用,必须通过实现它的类或代理对象来进行调用。在这段代码中,Feign 创建的代理对象实现了
ApiProviderFeign
,使得调用apiProviderFeign.getUserInfo(username)
成为可能。总结
ApiProviderFeign
作为 Feign 客户端接口定义了如何与service-provider
对接。- Spring Cloud Feign 会在应用启动时为这个接口生成实现,并注入到使用它的类中。
apiProviderFeign
是通过依赖注入获得的代理对象,可以直接调用接口定义的方法。
3.8.1.3、接口注解理解?
在
service-consumer
子工程中,@PostMapping("/api/service/provider/getUserInfo")
注解的理解如下:1. 请求类型
@PostMapping
是一个组合注解,用于处理 HTTP POST 请求。它指示该方法会处理发往指定 URL 的 POST 请求。2. 路径定义
"/api/service/provider/getUserInfo"
是该方法处理请求的具体路径。这意味着当调用ApiProviderFeign.getUserInfo
方法时,将发起一个 POST 请求,路径为/api/service/provider/getUserInfo
。3. Feign 执行
当你在
DemoController
中通过apiProviderFeign.getUserInfo(username)
调用时,Feign 会:
- 生成一个对应的 HTTP POST 请求。
- 将请求的 URL 设置为
"/api/service/provider/getUserInfo"
。- 传递必要的参数(在这个例子中是
username
)。4. 服务交互
该注解确保了
service-consumer
能够正确地调用service-provider
提供的 GET 用户信息的功能。这个路径与service-provider
中ApiController
的处理路径相对应。总结
@PostMapping
注解表明这个方法处理 POST 请求。- 路径指定了请求的目标。
- 此注解与 Feign 客户端配合使用,使得
service-consumer
可以正确地发起请求并与service-provider
进行交互。
这里接口的@PostMapping注解指示服务提供者具体地址。
因此Demo中service-provider工程并未直接实现service-consumer工程的interface,所以需要service-provider和interface都需要明确写明@PostMapping路径。
但如果service-provider直接实现该interface,则service-provider无需再写@PostMapping,spiringboot可直接找到。此种情况下,service-consumer中@PostMapping中地址无实际意义可以随意填写(不能包含数字),只要不重复即可,但不写不行。
3.8.2、哪些调用需要Fegin?
包含启动类的文件夹可以视为独立服务,服务之间的调用需要通过 Feign 来实现。
没有启动类的文件夹仅用于存放文件,不需要通过 Feign 进行调用,其他服务可以直接访问这些文件。
3.9、实体类
实体类(Entity类)是与数据库表结构相对应的Java类,它映射了数据库表中的记录为Java对象。实体类通常包含属性(对应数据库表的字段)、getter和setter方法以及可能的构造函数等。
实体类是数据模型层(model)的组成部分,用于表示业务数据,而不是直接与数据库进行交互。
3.10、Mapper
Mapper是数据访问层(DAO层)的一种表现形式。
Mapper层通常用于定义与数据库交互的接口,这些接口中的方法对应着数据库中的CRUD(创建、读取、更新、删除)操作。在MyBatis等ORM框架中,Mapper接口并没有直接的实现类,取而代之的是一个XML文件,该文件中定义了SQL语句和Java接口的绑定关系。当调用Mapper接口的方法时,实际上是通过动态代理机制,根据接口方法与XML文件中的SQL语句进行匹配,从而执行相应的数据库操作。
Mapper和实体类在Java应用程序中扮演着不同的角色,Mapper负责数据访问和操作,而实体类则负责数据的表示和封装。
3.10.1、因mapper,不需要实体类了?
不完全正确。尽管 MyBatis 和 Mapper 提供了便捷的数据访问能力,但实体类仍然是必要的。原因如下:
-
对象映射:实体类用来表示数据库表的结构,每个实体类的属性对应数据库表中的字段。MyBatis 通过这些实体类将数据库记录映射为 Java 对象。
-
代码清晰性:实体类增进了代码的可读性和维护性,使得数据结构更清晰。
-
业务逻辑:在某些情况下,实体类不仅仅用于数据映射,它们可能还包含与业务相关的方法和逻辑,这样可以更好地组织代码。
因此,虽然 MyBatis 和 Mapper 简化了数据库操作,但实体类仍然是不可或缺的部分。在使用 MyBatis 时,你仍然需要定义实体类以便进行数据映射和操作。
3.11、实体类
实体类(entity)通常是用来表示数据库表对应的对象。
它们通常包含与数据库字段相对应的属性,并提供相应的 getter 和 setter 方法。实体类的主要目的是用于存储和传递数据。
待整理
1、model层(数据库表字段映射到实体类)
数据库实体层,也被称为entity层,pojo层。
一般数据库一张表对应一个实体类,类属性同表字段一一对应。
2、dao层(CURD方法)
dao层即数据持久层,也被称为mapper层。
dao层的作用为访问数据库,向数据库发送sql语句,完成数据的增删改查任务。
3、service层 (功能设计)
service层即业务逻辑层。
service层的作用为完成功能设计。
service层调用dao层接口,接收dao层返回的数据,完成项目的基本功能设计。
4、controller层(前后端传参)
controller层即控制层。
controller层的功能为请求和响应控制。
controller层负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端。
-----------------------------------------------------------------------------------
我还在学习中,伴随学习过程,随时总结