springboot 笔记
1. 注解(Annotation)
- 是什么:代码里的特殊标记,像标签一样贴在类/方法/字段上
- 作用:用来给程序传递额外信息,比如
@NeedLogin
就是在说"这个方法需要登录" - 特点:以
@
符号开头,不会直接影响代码逻辑
2. 元注解(Meta-Annotation)
- 是什么:用来修饰其他注解的注解(注解的注解)
- 常见元注解:
@Target
:规定注解能贴在哪里(类/方法/字段等)@Retention
:规定注解在什么时候有效(源码/编译/运行时)@Documented
:控制是否出现在JavaDoc文档里
反射机制(Reflection)
- 是什么:程序运行时动态获取类信息的能力
- 怎么用:通过反射可以检测方法是否有
@NeedLogin
注解 - 典型代码:
method.isAnnotationPresent(NeedLogin.class) // 判断是否有注解
@Component
注解
作用
- 标记一个类为 Spring 组件,让 Spring 自动扫描并创建它的实例(Bean),纳入容器管理。
- 是
@Controller
、@Service
、@Repository
的通用父注解。
使用场景
- 当某个类不属于 Controller、Service、DAO 等特定层,但需要被 Spring 管理时使用。
- 例如:工具类、第三方库的适配器等。
@Component // 告诉Spring:这个类需要被管理
public class EmailValidator {public boolean isValid(String email) {return email.contains("@");}
}
- 其他派生注解(功能相同,但语义更明确):
@Service
:用于业务逻辑层@Repository
:用于数据访问层@Controller
:用于Web控制层
@Autowired
注解
作用
- 自动依赖注入:Spring 自动找到匹配的 Bean 并赋值给字段、构造器或方法参数。
- 是 Java 依赖注入(DI)的核心注解。
注入方式
- 字段注入(最常用)
@Service public class UserService {@Autowired // 自动注入UserRepositoryprivate UserRepository userRepository; }
构造器注入(推荐)
@Service
public class UserService {private final UserRepository userRepository;@Autowired // Spring 4.3+ 可省略public UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}
Setter 注入
@Service
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}
-
@Autowired
默认按类型(byType)匹配,如果有多个同类型 Bean,需配合@Qualifier
指定名称。 - 如果找不到 Bean,会抛出
NoSuchBeanDefinitionException
。可通过@Autowired(required=false)
设为可选依赖。
@Target(ElementType.METHOD)
- ElementType:Java定义的枚举,表示注解可以贴的位置类型
METHOD
:只能贴在方法上TYPE
:可以贴在类/接口上FIELD
:可以贴在字段上
- 作用:限制
@NeedLogin
只能用在方法前,不能乱贴
@Retention(RetentionPolicy.RUNTIME)
- RetentionPolicy:定义注解的生命周期
SOURCE
:仅在源码中有效(编译后消失)CLASS
:保留到编译后的class文件(运行时看不到)RUNTIME
:运行时仍然存在(可以通过反射读取)
- 为什么选RUNTIME:因为需要在程序运行时检查用户登录状态
@Around
注解详解
@Around
是 Spring AOP 中最强大的通知(Advice)类型,允许你完全控制目标方法的执行,可以在方法调用前后插入自定义逻辑,甚至阻止方法执行或修改返回值。
1. 核心特点
特性 | 说明 |
---|---|
完全控制 | 可以决定是否执行目标方法,以及如何修改参数和返回值 |
最灵活的通知 | 结合了 @Before 、@After 、@AfterReturning 、@AfterThrowing 的功能 |
必须明确调用目标方法 | 需通过 ProceedingJoinPoint.proceed() 手动触发目标方法执行 |
可以处理异常 | 能捕获并处理目标方法抛出的异常 |
2. 基本语法
@Aspect
@Component
public class LoggingAspect {// 定义切点(拦截哪些方法)@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}// Around 通知@Around("serviceMethods()")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 目标方法执行前的逻辑log.info("方法调用前: " + joinPoint.getSignature().getName());// 2. 执行目标方法(可跳过或修改参数)Object result = joinPoint.proceed(); // 3. 目标方法执行后的逻辑log.info("方法调用后,返回值: " + result);return result; // 可以修改返回值}
}
@Documented
- 作用:让这个注解在使用时,能显示在自动生成的JavaDoc文档中
- 对比:不加这个时,即使方法用了
@NeedLogin
,生成的API文档也不会显示这个标记
- 用
@interface
定义了一个注解@NeedLogin
- 用元注解
@Target
限制它只能贴到方法上 - 用
@Retention
保证运行时能通过反射检测到它 - 用
@Documented
让它出现在文档中 - 实际使用时,其他代码通过反射机制检测到这个注解,执行登录检查逻辑
@Bean
注解
- 作用:告诉 Spring 这个方法会返回一个对象,并交给 Spring 容器管理(类似 XML 配置中的
<bean>
)。 - 使用场景:常用于配置线程池、数据库连接池等需要全局管理的对象。
- 示例:
@Configuration
public class AppConfig {@Beanpublic Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);return executor;}
}
解释:这里定义了一个线程池 asyncExecutor
,Spring 会管理它的生命周期,其他地方可以直接 @Autowired
注入使用。
@Service
public class UserService {@Async // 该方法会异步执行(在另一个线程运行)public void sendEmail(String email) {log.info("发送邮件给: {}", email);}
}
@Async
注解
- 作用:让方法异步执行(不阻塞主线程,提高并发性能)。
- 使用条件:
- 必须配合
@EnableAsync
使用(在启动类或配置类上)。 - 方法不能是
private
或final
(因为 Spring 需要代理)。
- 必须配合
Spring AOP 详解
Spring AOP(Aspect-Oriented Programming,面向切面编程) 是 Spring 框架的核心模块之一,用于解耦横切关注点(如日志、事务、权限等),让业务逻辑更纯净。它通过动态代理技术,在运行时将额外逻辑(称为“通知”)织入到目标方法中。
1. 核心概念
术语 | 说明 |
---|---|
切面(Aspect) | 封装横切逻辑的模块(如 LoggingAspect ),包含通知和切点定义。 |
连接点(Join Point) | 程序执行的点(如方法调用、异常抛出),Spring AOP 仅支持方法级别的连接点。 |
通知(Advice) | 切面在特定连接点执行的动作(如 @Before 、@Around )。 |
切点(Pointcut) | 通过表达式匹配哪些连接点需要被拦截(如 execution(* com.example.*.*(..)) )。 |
目标对象(Target) | 被代理的原始对象(如 UserService )。 |
代理(Proxy) | Spring 生成的增强对象,在调用目标方法时插入切面逻辑。 |
2. AOP 的实现原理
Spring AOP 基于动态代理,具体分两种:
- JDK 动态代理(默认)
- 要求目标类必须实现接口(如
UserService
实现IUserService
)。 - 代理对象会实现相同接口,拦截方法调用。
- 要求目标类必须实现接口(如
- CGLIB 代理
- 通过继承目标类生成子类代理,适用于无接口的类。
- 需添加
@EnableAspectJAutoProxy(proxyTargetClass = true)
强制启用。
3. 通知(Advice)类型
注解 | 执行时机 | 示例用途 |
---|---|---|
@Before | 目标方法执行前 | 参数校验、日志记录 |
@After | 目标方法执行后(无论是否异常) | 资源清理 |
@AfterReturning | 目标方法成功返回后 | 记录返回值 |
@AfterThrowing | 目标方法抛出异常后 | 异常处理、告警通知 |
@Around | 完全控制目标方法执行 | 事务管理、性能监控 |
MDC(Mapped Diagnostic Context)核心总结
(1)MDC 是什么?
- 本质:一个线程绑定的键值对存储(基于
ThreadLocal
),用于在日志中携带上下文信息(如requestId
、userId
)。 - 典型用途:分布式系统日志追踪(如排查某个请求的所有日志)。
(2)MDC 的作用
场景 | 作用 |
---|---|
日志关联 | 让同一请求的所有日志自动带上 requestId ,方便排查问题 |
多线程环境 | 默认线程安全(每个线程独立存储) |
减少代码侵入 | 无需手动在每条日志中写 requestId ,通过 %X{key} 自动输出 |
// 存值
MDC.put("requestId", "123");
// 取值
String id = MDC.get("requestId");
// 清理(防止内存泄漏)
MDC.clear();
特殊情况:异步线程中 MDC 丢失怎么办?
(1)问题原因
-
ThreadLocal
的隔离性:子线程默认不继承父线程的 MDC(新线程拿不到父线程的requestId
)。@Bean public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setTaskDecorator(new MDCAwareTaskDecorator()); // 自动复制 MDCreturn executor; }
效果:子线程日志自动继承父线程的 MDC。
拦截器 vs 过滤器:
特性 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
所属层次 | Servlet 规范(底层,任何Web框架通用) | Spring MVC 提供(仅Spring生态可用) |
实现接口 | javax.servlet.Filter | org.springframework.web.servlet.HandlerInterceptor |
执行时机 | 在 Servlet 之前(请求进入DispatcherServlet前) | 在 Controller 方法前后(DispatcherServlet内) |
依赖注入 | 不支持Spring依赖注入(需手动获取Bean) | 支持Spring依赖注入(如 @Autowired ) |
获取上下文 | 只能获取Servlet API(Request/Response) | 可获取Spring上下文(如方法参数、注解信息) |
典型场景 | 全局日志、编码转换、跨域处理 | 权限校验、参数预处理、日志增强 |