Skip to content

Commit 96005dc

Browse files
committed
aadd java-tips
1 parent 51b6497 commit 96005dc

1 file changed

Lines changed: 305 additions & 0 deletions

File tree

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
tags: ["Dev","Java"]
3+
title: "Goper直接撸java代码,真是操碎了心,tips总结①"
4+
linkTitle: "Java tips的总结"
5+
weight: 32
6+
description: >
7+
Java Tips总结
8+
---
9+
10+
{{% pageinfo %}}
11+
Java发展的这些年,为了复杂大型项目的工程实践能傻瓜落地,真是封了一层又一层啊,C/C++型选手转过来,会感觉各种看不透,总结了一些tips,尽量追根问底,分享给大家。
12+
{{% /pageinfo %}}
13+
14+
## @SpringBootApplication 注解
15+
16+
java spring项目,第一个看到的基本就是这个注解,只能加到main方法所在的主类上,标识这是一个Spring Boot应用程序的main入口类。
17+
18+
> @(注解)的本质是给代码打标签,告诉需要处理注解的工具或框架: 这里的代码需要特殊处理。
19+
20+
@SpringBootApplication”注解,是个组合注解,是表示要在程序启动运行阶段被特殊处理的(其实现完全依赖于Java的反射机制),是spring框架的核心机制,依靠Spring容器的运行时处理能力,体现了「约定优于配置」的设计思想。
21+
22+
> 没点儿Java经验,「约定优于配置」的设计思想,会让学习成本增加,但是在大型项目中,会让开发效率大大提高。
23+
> spring 容器的运行时处理能力,是实现「约定优于配置」的关键。
24+
25+
### Spring 容器
26+
27+
spring 容器是一个IoC容器(简单理解为一个对象工厂),用于管理应用程序中的Bean对象。
28+
29+
> Bean的生命周期包括:实例化、依赖注入、初始化、使用、销毁。
30+
> 依赖注入是指在运行时,通过容器自动将Bean的依赖对象注入到Bean中。
31+
> 初始化是指在Bean实例化后,调用Bean的初始化方法。
32+
> 使用是指在Bean初始化后,通过容器获取Bean实例,调用其方法。
33+
> 销毁是指在应用程序关闭时,调用Bean的销毁方法。
34+
35+
简单来说:
36+
- 你写好代码,贴上标签(注解)
37+
- 容器帮你管理这些代码(Bean)
38+
- 你需要什么,容器就给你什么(依赖注入)
39+
40+
### 为什么要使用 Spring 容器?
41+
42+
> 理由有一堆,还都是高大上的那种....
43+
44+
拿用户注册功能对比,举例说明,对比「不用Spring容器」和「用Spring容器」的代码差异,直观展示Spring容器的价值:
45+
46+
**场景:用户注册功能(需要3层结构)**
47+
1. Controller :接收注册请求
48+
2. Service :处理注册业务逻辑(密码加密、保存用户)
49+
3. Repository :操作数据库(保存用户信息)
50+
51+
**方式1:不用Spring容器 → 传统Java开发**
52+
53+
```java
54+
// 1. Repository层:操作数据库
55+
public class UserRepository {
56+
public void save(User user) {
57+
System.out.println("保存用户到数据库:" + user.getName());
58+
}
59+
}
60+
61+
// 2. Service层:业务逻辑
62+
public class UserService {
63+
// 必须手动创建依赖对象 → 「自己new」,初始化省略..
64+
private UserRepository userRepository = new UserRepository();
65+
66+
public void registerUser(User user) {
67+
// 密码加密(业务逻辑)
68+
user.setPassword(encryptPassword(user.getPassword()));
69+
// 调用Repository保存
70+
userRepository.save(user);
71+
}
72+
73+
private String encryptPassword(String password) {
74+
return "加密后的密码:" + password;
75+
}
76+
}
77+
78+
// 3. Controller层:接收请求
79+
public class UserController {
80+
// 必须手动创建依赖对象 → 「自己new」
81+
private UserService userService = new UserService();
82+
83+
// 可以改进为接收参数
84+
public void handleRegisterRequest(String name, String password) {
85+
User user = new User();
86+
user.setName(name);
87+
user.setPassword(password);
88+
userService.registerUser(user);
89+
}
90+
}
91+
92+
// 4. 启动类:手动创建所有对象
93+
public class App {
94+
public static void main(String[] args) {
95+
// 必须手动创建所有层的对象 → 「自己管理依赖」
96+
UserController controller = new UserController();
97+
// 调用注册接口
98+
controller.handleRegisterRequest("张三", "123456");
99+
}
100+
}
101+
```
102+
103+
有点儿偏面向过程的开发思路,需要自己管理各层依赖对象的创建初始化 :
104+
- 必须手动 new 所有对象,代码冗余
105+
- 依赖关系硬编码(UserService直接依赖UserRepository的具体实现)
106+
- 修改依赖时有可能需要修改所有相关代码(比如把UserRepository换成Redis实现)
107+
- 无法集中管理对象生命周期
108+
109+
**方式2:用Spring容器 → 依赖注入开发**
110+
111+
```java
112+
// 1. Repository层:用@Repository标记,告诉容器「UserRepository类是要管理的Bean对象」
113+
@Repository
114+
public class UserRepository {
115+
public void save(User user) {
116+
System.out.println("保存用户到数据库:" + user.getName());
117+
}
118+
}
119+
120+
// 2. Service层:用@Service标记,@Autowired让容器自动注入依赖
121+
@Service
122+
public class UserService {
123+
// 容器自动注入UserRepository → 「不用自己new」,配置已初始化
124+
@Autowired
125+
private UserRepository userRepository;
126+
127+
public void registerUser(User user) {
128+
// 专注于业务逻辑(密码加密)
129+
user.setPassword(encryptPassword(user.getPassword()));
130+
userRepository.save(user);
131+
}
132+
133+
private String encryptPassword(String password) {
134+
return "加密后的密码:" + password;
135+
}
136+
}
137+
138+
// 3. Controller层:用@RestController标记,@Autowired自动注入Service
139+
@RestController
140+
public class UserController {
141+
// 容器自动注入UserService → 「自动解决依赖」
142+
@Autowired
143+
private UserService userService;
144+
145+
@PostMapping("/register")
146+
public String registerUser(@RequestBody User user) {
147+
// 专注于处理请求,不用关心Service怎么来的
148+
userService.registerUser(user);
149+
return "注册成功";
150+
}
151+
}
152+
153+
// 4. 启动类:用@SpringBootApplication标记,容器自动创建所有对象
154+
@SpringBootApplication
155+
public class App {
156+
public static void main(String[] args) {
157+
// 一行代码启动 → 容器自动处理所有对象创建和依赖
158+
SpringApplication.run(App.class, args);
159+
}
160+
}
161+
```
162+
163+
**具体价值体现 1. 价值1:不用自己new对象**
164+
1. 价值1:不用自己new对象
165+
- 传统方式 :每个类都要手动 new 依赖对象,并自己管理参数初始化
166+
- Spring方式 :只需要用 @Service / @Repository 标记,容器自动创建,并配合其他注解可自动配置参数。
167+
- 例子 : UserService 不用写 new UserRepository() ,容器会自动创建并注入
168+
169+
2. 价值2:自动解决依赖
170+
- 传统方式 :依赖关系硬编码,修改时要改所有相关代码
171+
- Spring方式 : @Autowired 自动找到并注入(嵌入)合适的对象
172+
- 例子 :如果把 UserRepository 换成Redis实现,只需要给Redis实现类加 @Repository ,不需要修改 UserService 代码
173+
174+
3. 价值3:专注业务逻辑
175+
- 传统方式 :花大量时间管理对象创建和依赖
176+
- Spring方式 :只需要关心业务逻辑(如密码加密、用户验证)
177+
- 例子 : UserService 只需要写 registerUser 的业务代码,不用关心 UserRepository 怎么来的
178+
179+
### spring 启动过程
180+
181+
回到开头提到的spring应用中常遇到的第一个注解,会出现在主类上,“@SpringBootApplication”注解,标识这是一个Spring Boot应用程序的main入口类。
182+
183+
@SpringBootApplication”注解,包含了以下3个注解:
184+
185+
1. @SpringBootConfiguration: 标识这是应用的主配置类(入口),是@Configuration的派生注解,提供了Spring Boot特定的配置语义,确保配置类能被正确识别和处理,优先级说明:默认配置 → 自动配置 → 外部配置(application.yml) → 手动配置(@Configuration)。
186+
187+
2. @EnableAutoConfiguration: 启用Spring Boot的自动配置机制。
188+
- 自动扫描 classpath 下所有 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 文件(Spring Boot 2.7+)
189+
- 根据pom中的配置的依赖包、条件配置类等信息,自动配置相应的Bean(比如数据源、Web MVC配置等)。
190+
- 另外会看application.yml配置文件中的属性,覆盖默认配置。举例:修改Tomcat默认端口为9090。
191+
192+
```yml
193+
# application.yml
194+
server:
195+
port: 9090 # 覆盖默认的 8080 端口
196+
197+
spring:
198+
datasource:
199+
url: jdbc:mysql://localhost:3306/yudao
200+
username: root
201+
password: 123456
202+
```
203+
204+
- Profile多环境支持
205+
- 可以根据不同的环境(如开发、测试、生产),加载不同的配置文件(如application-dev.yml、application-test.yml、application-prod.yml)。
206+
- 可以通过spring.profiles.active属性指定当前激活的环境,默认是default环境。
207+
208+
3. @ComponentScan: 开启组件扫描,Spring Boot会自动扫描主类所在的包及其子包,重点找到使用了@Controller、@Service、@Repository等注解的类,并将这些Bean对象注册到Spring容器中。
209+
- 支持通过basePackages参数设置,指定要扫描的包名,控制扫描范围,灵活组织应用的功能。
210+
211+
经过以上3个隐含注解的协同处理,程序启动完成,会进入运行状态,等待接收外部请求。
212+
213+
示例:
214+
```java
215+
/**
216+
* Spring Boot应用程序的主启动类
217+
*
218+
* @SpringBootApplication 是一个组合注解,包含以下三个核心功能:
219+
* 1. @SpringBootConfiguration: 标识这是一个Spring Boot应用的主配置类
220+
* 2. @EnableAutoConfiguration: 启用Spring Boot的自动配置机制
221+
* 3. @ComponentScan: 自动扫描当前包及其子包下的组件(如@Controller、@Service、@Repository等)
222+
*
223+
* 这个注解告诉Spring Boot框架:这是一个Spring Boot应用程序的入口点
224+
*/
225+
@SpringBootApplication
226+
public class ServerApplication {
227+
228+
/**
229+
* 应用程序的主入口方法
230+
*
231+
* 当JVM启动时,会首先执行这个main方法
232+
* 该方法负责启动整个Spring Boot应用程序
233+
*
234+
* @param args 命令行参数,可以在启动时传入配置参数
235+
* 例如:--server.port=8081 或 --spring.profiles.active=dev
236+
*/
237+
public static void main(String[] args) {
238+
/**
239+
* SpringApplication.run() 方法执行以下关键操作:
240+
* 1. 创建Spring应用上下文(ApplicationContext)
241+
* 2. 启用自动配置(根据classpath中的依赖自动配置Bean)
242+
* 3. 启动内嵌的Web服务器(如Tomcat、Jetty等)
243+
* 4. 扫描并注册所有使用Spring注解的组件
244+
* 5. 启动完成后,应用程序进入运行状态,等待HTTP请求
245+
*
246+
* 参数说明:
247+
* - ServerApplication.class: 主配置类,Spring Boot会从这个类所在的包开始扫描组件
248+
* - args: 命令行参数,用于覆盖默认配置
249+
*
250+
* 返回值:ConfigurableApplicationContext对象,代表整个Spring应用上下文
251+
* 可以用于获取Bean、关闭应用等操作
252+
*/
253+
SpringApplication.run(ServerApplication.class, args);
254+
}
255+
}
256+
```
257+
258+
这个过程体现了spring boot生态的核心价值 - 配置自动化,开发人员可以通过pom引入下依赖包(内部包也一样),然后直接改改yml配置,就可以轻松写出生产级的代码了。
259+
260+
> SpringBootApplication注解工作的机制中,还有不少细节,属于用到了可以现查资料的知识,此处先不展开。
261+
> spring框架还有很多AOP、事务管理等其他优势,此处先不展开。
262+
263+
## lombok
264+
解决Java样板代码的利器,通过**@注解**的方式,在编译期自动生成对应的代码,减少了样板代码的编写,提高了开发效率。
265+
266+
解决了哪些样板代码的编写? 比如:getter/setter、toString、equals、hashCode等。这些方法在大多数Java类中都需要,但是编写逻辑高度重复啰嗦,通过lombok注解,就可以自动生成这些代码,减少了开发人员的机械工作量。举例:
267+
268+
```java
269+
@Data // @Data组合注解,自动为该类生成getter/setter/toString/equals/hashCode等方法,节省了约50行左右的代码量。
270+
// 相当于以下注解组合
271+
// @Getter、@Setter、@ToString、@EqualsAndHashCode等
272+
public class User {
273+
private Long id;
274+
private String name;
275+
private Integer age;
276+
}
277+
```
278+
279+
**为什么每个Java类都需要这些方法 ?**
280+
281+
因为这些方法是Java类的基础(所有类都直接或间接的继承了Object类),没有它们,大多数Java类就不能正常工作。
282+
283+
```java
284+
// 所有类都继承 Object
285+
public class User { // 隐式继承 Object
286+
// 自动拥有 Object 的方法:toString(), equals(), hashCode() 等
287+
}
288+
```
289+
290+
比如:
291+
- 没有getter/setter方法,就不能通过对象的属性来访问和修改属性值,Java封装思想的具体实现。
292+
- 没有toString方法,就不能通过System.out.println()方法来打印对象的信息。
293+
- 没有 equals 方法,虽然仍然可以使用equals方法(继承自Object),只是比较的是引用相等性,不能用于比较对象内容是否相等。
294+
- 没有 hashCode 方法,就不能将对象存储在HashSet、HashMap等集合中。
295+
296+
```java
297+
Map<User, String> map = new HashMap<>();
298+
User user = new User();
299+
map.put(user, "value"); // 需要拥有正确的 hashCode() 和 equals()
300+
301+
Set<User> set = new HashSet<>();
302+
set.add(user); // 同样需要正确的 hashCode() 和 equals()
303+
```
304+
305+
确保了 Java 中所有对象都具有统一的基本行为,也是哈希集合(如 HashMap、HashSet)等功能的基础。

0 commit comments

Comments
 (0)