第四阶段:源码阅读与框架原理

#### **1. Spring框架核心原理**
- **知识点**:
- IoC容器。
- AOP实现原理。
- 事务管理。

### 1. IoC 容器(Inversion of Control,控制反转)

#### 详细说明
IoC 是一种设计原则,它将对象的创建和依赖关系的管理从代码中解耦出来,交给一个容器来完成。在传统的编程方式中,对象的创建和依赖关系的维护由代码本身负责,而在 IoC 模式下,控制权从代码转移到了容器,因此称为控制反转。

IoC 容器是实现 IoC 原则的具体实现,它负责对象的创建、初始化、依赖注入和生命周期管理等。常见的 IoC 容器有 Spring 框架中的 `ApplicationContext` 和 `BeanFactory`。

#### 示例
以下是一个简单的 Spring IoC 容器的示例:
```java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 定义一个服务接口
interface MessageService {
String getMessage();
}

// 实现服务接口
class EmailService implements MessageService {
@Override
public String getMessage() {
return "这是一封邮件消息";
}
}

// 配置类,用于定义 Bean
@Configuration
class AppConfig {
@Bean
public MessageService messageService() {
return new EmailService();
}
}

// 主类,使用 IoC 容器获取 Bean
public class IoCExample {
public static void main(String[] args) {
// 创建 ApplicationContext 容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从容器中获取 Bean
MessageService service = context.getBean(MessageService.class);
System.out.println(service.getMessage());
}
}
```
#### 代码解释
- `MessageService` 是一个服务接口,`EmailService` 是其具体实现类。
- `AppConfig` 是一个配置类,使用 `@Configuration` 注解标记,通过 `@Bean` 注解定义了一个 `MessageService` 类型的 Bean。
- 在 `main` 方法中,创建了 `AnnotationConfigApplicationContext` 容器,并传入配置类 `AppConfig.class`。然后通过 `getBean` 方法从容器中获取 `MessageService` 类型的 Bean,并调用其方法。

### 2. AOP 实现原理(Aspect-Oriented Programming,面向切面编程)

#### 详细说明
AOP 是一种编程范式,它允许我们在不修改原有代码的基础上,对程序的某些特定部分进行增强。AOP 的核心概念包括切面(Aspect)、连接点(Join Point)、切入点(Pointcut)、通知(Advice)和织入(Weaving)。

AOP 的实现原理主要有两种:
- **静态代理**:在编译期将切面代码织入到目标类中,生成新的类文件。例如 AspectJ 就是使用这种方式。
- **动态代理**:在运行时通过代理对象来实现切面逻辑的织入。Spring AOP 使用的是动态代理,具体有 JDK 动态代理和 CGLIB 动态代理。
- **JDK 动态代理**:基于接口实现,要求目标对象必须实现接口。
- **CGLIB 动态代理**:基于继承实现,通过生成目标类的子类来实现代理。

#### 示例
以下是一个简单的 Spring AOP 示例:
```java
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

// 服务接口
interface UserService {
void createUser(String username);
}

// 服务实现类
class UserServiceImpl implements UserService {
@Override
public void createUser(String username) {
System.out.println("创建用户: " + username);
}
}

// 切面类
@Aspect
class LoggingAspect {
// 定义切入点
@Pointcut("execution(* com.example.UserService.createUser(..))")
public void createUserPointcut() {}

// 前置通知
@Before("createUserPointcut()")
public void beforeCreateUser() {
System.out.println("在创建用户之前记录日志");
}

// 后置通知
@After("createUserPointcut()")
public void afterCreateUser() {
System.out.println("在创建用户之后记录日志");
}
}

// 配置类
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.example")
class AppConfig {}

// 主类
public class AOPExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.createUser("Alice");
}
}
```
#### 代码解释
- `UserService` 是服务接口,`UserServiceImpl` 是其实现类。
- `LoggingAspect` 是切面类,使用 `@Aspect` 注解标记。通过 `@Pointcut` 注解定义切入点,`@Before` 和 `@After` 注解分别定义前置通知和后置通知。
- `AppConfig` 是配置类,使用 `@EnableAspectJAutoProxy` 注解开启 AOP 自动代理功能,`@ComponentScan` 注解扫描指定包下的组件。
- 在 `main` 方法中,创建 `AnnotationConfigApplicationContext` 容器,从容器中获取 `UserService` 类型的 Bean,并调用其 `createUser` 方法。在调用前后会自动执行切面类中的通知方法。

### 3. 事务管理

#### 详细说明
事务管理是指在数据库操作中,将一组操作作为一个不可分割的工作单元,要么全部成功执行,要么全部失败回滚。在 Java 中,常见的事务管理方式有编程式事务管理和声明式事务管理。

- **编程式事务管理**:通过编写代码来控制事务的开始、提交和回滚。这种方式比较灵活,但代码量较大,维护成本高。
- **声明式事务管理**:通过配置的方式来管理事务,将事务管理代码与业务逻辑代码分离。Spring 框架提供了声明式事务管理的支持,通过 `@Transactional` 注解可以方便地实现事务管理。

#### 示例
以下是一个简单的 Spring 声明式事务管理的示例:
```java
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;

// 服务类
@Service
class UserService {
// 模拟数据库操作
@Transactional
public void transferMoney() {
System.out.println("开始转账操作");
// 模拟转账过程中的异常
throw new RuntimeException("转账失败");
}
}

// 配置类
@Configuration
class AppConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

// 主类
public class TransactionExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
try {
userService.transferMoney();
} catch (Exception e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}
```
#### 代码解释
- `UserService` 是服务类,使用 `@Service` 注解标记。`transferMoney` 方法使用 `@Transactional` 注解标记,表示该方法在一个事务中执行。
- `AppConfig` 是配置类,定义了数据源和事务管理器。
- 在 `main` 方法中,创建 `AnnotationConfigApplicationContext` 容器,从容器中获取 `UserService` 类型的 Bean,并调用其 `transferMoney` 方法。由于方法中抛出了异常,事务会自动回滚。

 

 

#### **2. Spring Boot自动配置原理**
- **知识点**:
- `@EnableAutoConfiguration`。
- `spring.factories`文件。

### 1. `@EnableAutoConfiguration`

#### 详细说明
`@EnableAutoConfiguration` 是 Spring Boot 中的一个核心注解,它的主要作用是开启 Spring Boot 的自动配置功能。Spring Boot 的自动配置机制可以根据项目中添加的依赖以及配置信息,自动为应用程序配置合适的 Bean。

当你在 Spring Boot 应用的主类上添加 `@EnableAutoConfiguration` 注解后,Spring Boot 会根据类路径下的依赖和配置文件,自动猜测并配置应用程序所需的 Bean。例如,如果类路径下存在 `tomcat-embed-core` 依赖,Spring Boot 会自动配置一个嵌入式的 Tomcat 服务器;如果存在 `spring-boot-starter-jdbc` 依赖,会自动配置数据源和 JdbcTemplate 等。

`@EnableAutoConfiguration` 注解通常和 `@SpringBootApplication` 注解一起使用,因为 `@SpringBootApplication` 注解是一个组合注解,它包含了 `@EnableAutoConfiguration`、`@ComponentScan` 和 `@Configuration` 注解。

#### 示例
```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

// 手动组合注解实现类似 @SpringBootApplication 的功能
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class MySpringBootApp {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApp.class, args);
}
}
```
在这个示例中,`@EnableAutoConfiguration` 注解开启了自动配置功能。当应用启动时,Spring Boot 会根据类路径下的依赖自动配置相关的 Bean。例如,如果添加了 `spring-boot-starter-web` 依赖,Spring Boot 会自动配置嵌入式的 Web 服务器(如 Tomcat)和 Spring MVC 的相关组件。

### 2. `spring.factories` 文件

#### 详细说明
`spring.factories` 是 Spring 框架中用于实现自动配置加载的一个机制,它是一个属性文件,通常位于 `META-INF` 目录下。在 Spring Boot 中,`spring.factories` 文件用于指定自动配置类、监听器、初始化器等。

文件的格式是键值对的形式,键是接口或抽象类的全限定名,值是实现类的全限定名,多个实现类之间用逗号分隔。Spring Boot 在启动时,会扫描类路径下所有的 `spring.factories` 文件,并根据键来加载对应的实现类。

例如,`org.springframework.boot.autoconfigure.EnableAutoConfiguration` 是一个常用的键,它对应的值是一系列的自动配置类,Spring Boot 会根据这些配置类来进行自动配置。

#### 示例
假设我们有一个自定义的自动配置模块,我们可以在 `META-INF/spring.factories` 文件中添加如下内容:
```properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
```
其中,`com.example.MyAutoConfiguration` 是我们自定义的自动配置类。

下面是自定义自动配置类的示例代码:
```java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 自定义自动配置类
@Configuration
public class MyAutoConfiguration {
@Bean
public MyService myService() {
return new MyService();
}
}

// 自定义服务类
class MyService {
public void doSomething() {
System.out.println("执行自定义服务的操作");
}
}
```
在这个示例中,我们定义了一个自定义的自动配置类 `MyAutoConfiguration`,并在其中定义了一个 `MyService` 的 Bean。当我们在 Spring Boot 项目中引入这个自定义的自动配置模块时,Spring Boot 会根据 `spring.factories` 文件加载 `MyAutoConfiguration` 类,并自动配置 `MyService` Bean。

```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MainApp.class, args);
MyService myService = context.getBean(MyService.class);
myService.doSomething();
}
}
```
在 `MainApp` 类中,我们启动 Spring Boot 应用,并从应用上下文中获取 `MyService` Bean,然后调用其方法。由于 `spring.factories` 文件的配置,`MyService` Bean 会被自动配置并可用。

#### **3. 常用开源框架源码阅读**
- **知识点**:
- Guava(`ImmutableList`、`Cache`)。
- Netty(NIO框架)。

 

### 1. Guava(`ImmutableList`、`Cache`)

#### 1.1 `ImmutableList`

##### 说明
`ImmutableList` 是 Google Guava 库提供的一个不可变列表实现。不可变对象一旦创建,其状态就不能被改变,这带来了很多好处,比如线程安全、可以被自由地共享、简化了程序的逻辑等。`ImmutableList` 提供了一种安全且高效的方式来创建和使用不可变的列表。

##### 示例
```java
import com.google.common.collect.ImmutableList;

import java.util.List;

public class ImmutableListExample {
public static void main(String[] args) {
// 创建一个 ImmutableList
ImmutableList<String> immutableList = ImmutableList.of("apple", "banana", "cherry");

// 尝试修改 ImmutableList 会抛出 UnsupportedOperationException
try {
immutableList.add("date");
} catch (UnsupportedOperationException e) {
System.out.println("无法修改 ImmutableList: " + e.getMessage());
}

// 遍历 ImmutableList
for (String fruit : immutableList) {
System.out.println(fruit);
}

// 可以安全地在多个线程间共享 ImmutableList
new Thread(() -> {
System.out.println("线程中访问 ImmutableList: " + immutableList);
}).start();
}
}
```
##### 代码解释
- `ImmutableList.of("apple", "banana", "cherry")`:使用 `of` 方法创建一个包含三个元素的不可变列表。
- 尝试调用 `add` 方法修改列表会抛出 `UnsupportedOperationException`,因为 `ImmutableList` 是不可变的。
- 可以像普通列表一样遍历 `ImmutableList`。
- 由于 `ImmutableList` 是线程安全的,可以在多个线程间安全地共享。

#### 1.2 `Cache`

##### 说明
Guava 的 `Cache` 是一个本地缓存实现,它提供了一种简单而强大的方式来缓存数据,减少对数据源(如数据库、网络服务)的频繁访问,从而提高应用程序的性能。`Cache` 支持设置缓存的大小、过期时间、移除监听器等。

##### 示例
```java
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import java.util.concurrent.TimeUnit;

public class CacheExample {
public static void main(String[] args) {
// 创建一个 Cache 实例,设置最大容量为 100,过期时间为 10 分钟
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();

// 向缓存中放入数据
cache.put("key1", "value1");

// 从缓存中获取数据
String value = cache.getIfPresent("key1");
System.out.println("从缓存中获取的值: " + value);

// 移除缓存中的数据
cache.invalidate("key1");
value = cache.getIfPresent("key1");
System.out.println("移除后从缓存中获取的值: " + value);
}
}
```
##### 代码解释
- `CacheBuilder.newBuilder()`:创建一个 `CacheBuilder` 实例,用于构建 `Cache`。
- `maximumSize(100)`:设置缓存的最大容量为 100 个条目。
- `expireAfterWrite(10, TimeUnit.MINUTES)`:设置缓存条目在写入 10 分钟后过期。
- `cache.put("key1", "value1")`:向缓存中放入一个键值对。
- `cache.getIfPresent("key1")`:从缓存中获取指定键的值,如果键不存在则返回 `null`。
- `cache.invalidate("key1")`:从缓存中移除指定键的条目。

### 2. Netty(NIO 框架)

#### 说明
Netty 是一个基于 Java NIO 的高性能网络编程框架,它简化了网络编程的复杂性,提供了异步、事件驱动的网络编程模型。Netty 广泛应用于构建各种网络应用,如服务器、客户端、中间件等。Netty 的核心组件包括 `Channel`、`EventLoop`、`ChannelPipeline` 等。

#### 示例:简单的 Netty 服务器

```java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyServer {
private static final int PORT = 8080;

public static void main(String[] args) throws InterruptedException {
// 创建 boss 线程组,用于处理客户端连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 创建 worker 线程组,用于处理网络读写
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
// 创建 ServerBootstrap 实例,用于启动服务器
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 添加字符串解码器
pipeline.addLast(new StringDecoder());
// 添加字符串编码器
pipeline.addLast(new StringEncoder());
// 添加自定义处理器
pipeline.addLast(new SimpleServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);

// 绑定端口并启动服务器
ChannelFuture f = b.bind(PORT).sync();

// 等待服务器关闭
f.channel().closeFuture().sync();
} finally {
// 优雅地关闭线程组
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String) msg;
System.out.println("收到客户端请求: " + request);
// 向客户端发送响应
ctx.writeAndFlush("服务器已收到请求: " + request);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}
```
#### 代码解释
- `NioEventLoopGroup`:创建两个线程组,`bossGroup` 用于处理客户端连接,`workerGroup` 用于处理网络读写。
- `ServerBootstrap`:用于启动服务器,配置线程组、通道类型、处理器等。
- `ChannelInitializer`:用于初始化通道的 `ChannelPipeline`,添加解码器、编码器和自定义处理器。
- `SimpleServerHandler`:自定义的处理器,继承自 `ChannelInboundHandlerAdapter`,处理客户端的请求并发送响应。
- `channelRead` 方法:当接收到客户端的请求时,打印请求内容并向客户端发送响应。
- `exceptionCaught` 方法:当发生异常时,打印异常信息并关闭通道。

通过以上示例,可以看到 Guava 提供了实用的集合和缓存工具,而 Netty 则是一个强大的网络编程框架,它们都能在 Java 开发中发挥重要作用。

 

posted @ 2025-02-20 16:57  皇问天  阅读(39)  评论(0)    收藏  举报