核心概念
Spring框架的两大核心特性:
- IOC(Inversion of Control,控制反转):对象创建和依赖管理的控制权反转
- AOP(Aspect Oriented Programming,面向切面编程):横切关注点的模块化
这两个特性相辅相成,构成了Spring框架的基石。
IOC(控制反转)
什么是IOC
控制反转是一种设计思想,将对象的创建、配置和生命周期管理的控制权从应用程序代码转移到Spring容器。
传统方式:
public class UserService {
// 应用程序主动创建依赖
private UserDao userDao = new UserDaoImpl();
public User findUser(Long id) {
return userDao.findById(id);
}
}
IOC方式:
@Service
public class UserService {
// 容器注入依赖
@Autowired
private UserDao userDao;
public User findUser(Long id) {
return userDao.findById(id);
}
}
依赖注入(DI)
依赖注入是IOC的具体实现方式,Spring通过DI将依赖对象注入到目标对象中。
三种注入方式
@Service
public class OrderService {
// 1. 构造器注入(推荐)
private final OrderRepository orderRepository;
private final UserService userService;
@Autowired
public OrderService(OrderRepository orderRepository, UserService userService) {
this.orderRepository = orderRepository;
this.userService = userService;
}
// 2. Setter注入
private EmailService emailService;
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
// 3. 字段注入(不推荐)
@Autowired
private LogService logService;
}
推荐顺序:构造器注入 > Setter注入 > 字段注入
IOC容器
// BeanFactory - 基础容器
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = (User) factory.getBean("user");
// ApplicationContext - 增强容器(推荐)
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
ApplicationContext的增强功能:
- 国际化支持(MessageSource)
- 事件发布(ApplicationEventPublisher)
- 资源加载(ResourceLoader)
- AOP集成
- Web应用支持
Bean生命周期
实例化 → 属性填充 → Aware接口回调 →
初始化前处理 → 初始化 → 初始化后处理 →
使用 → 销毁
关键扩展点:
BeanPostProcessor:初始化前后处理InitializingBean:初始化回调DisposableBean:销毁回调@PostConstruct、@PreDestroy:JSR-250注解
AOP(面向切面编程)
什么是AOP
面向切面编程是一种编程范式,用于将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。
横切关注点:分散在多个模块中的通用功能,如:
- 日志记录
- 事务管理
- 权限控制
- 性能监控
- 异常处理
AOP核心概念
@Aspect
@Component
public class LoggingAspect {
// 切点:定义在哪里切入
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 前置通知:方法执行前
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature().getName());
}
// 后置通知:方法执行后
@After("serviceLayer()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature().getName());
}
// 返回通知:方法正常返回后
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("AfterReturning: " + result);
}
// 异常通知:方法抛出异常后
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("AfterThrowing: " + ex.getMessage());
}
// 环绕通知:完全控制方法执行
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = joinPoint.proceed(); // 执行目标方法
return result;
} finally {
long end = System.currentTimeMillis();
System.out.println("执行时间: " + (end - start) + "ms");
}
}
}
AOP术语
| 术语 | 说明 | 示例 |
|---|---|---|
| 切面(Aspect) | 横切关注点的模块化 | @Aspect类 |
| 连接点(Join Point) | 程序执行的某个点 | 方法调用、异常抛出 |
| 切点(Pointcut) | 匹配连接点的表达式 | execution(* service.*.*(..)) |
| 通知(Advice) | 在切点执行的动作 | @Before、@After等 |
| 目标对象(Target) | 被代理的对象 | 业务Service |
| 代理(Proxy) | AOP创建的对象 | JDK代理或CGLIB代理 |
| 织入(Weaving) | 将切面应用到目标对象 | 运行时织入 |
AOP实现原理
Spring AOP基于动态代理实现:
1. JDK动态代理(基于接口)
// 目标接口
public interface UserService {
void save(User user);
}
// 目标实现
@Service
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
System.out.println("保存用户: " + user.getName());
}
}
// Spring会创建JDK动态代理
UserService proxy = (UserService) Proxy.newProxyInstance(
classLoader,
new Class[]{UserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 前置处理
System.out.println("Before: " + method.getName());
// 调用目标方法
Object result = method.invoke(target, args);
// 后置处理
System.out.println("After: " + method.getName());
return result;
}
}
);
特点:
- 必须基于接口
- 使用Java标准库
- 性能较好
2. CGLIB代理(基于类)
// 目标类(没有接口)
@Service
public class OrderService {
public void createOrder(Order order) {
System.out.println("创建订单: " + order.getId());
}
}
// Spring会创建CGLIB代理(继承目标类)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
// 前置处理
System.out.println("Before: " + method.getName());
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
// 后置处理
System.out.println("After: " + method.getName());
return result;
}
});
OrderService proxy = (OrderService) enhancer.create();
特点:
- 不需要接口
- 通过继承实现
- 无法代理final类和final方法
代理选择策略
// Spring的代理选择逻辑
if (目标对象实现了接口 && proxyTargetClass=false) {
使用JDK动态代理
} else {
使用CGLIB代理
}
// 强制使用CGLIB
@EnableAspectJAutoProxy(proxyTargetClass = true)
切点表达式
@Aspect
@Component
public class PointcutExamples {
// 1. 执行任意公共方法
@Pointcut("execution(public * *(..))")
public void anyPublicMethod() {}
// 2. 执行以save开头的方法
@Pointcut("execution(* save*(..))")
public void anySave() {}
// 3. 执行service包下所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 4. 执行service包及子包下所有方法
@Pointcut("execution(* com.example.service..*.*(..))")
public void serviceLayerWithSubpackages() {}
// 5. 特定类的所有方法
@Pointcut("within(com.example.service.UserService)")
public void userServiceMethods() {}
// 6. 特定包下的所有类
@Pointcut("within(com.example.service..*)")
public void inServiceLayer() {}
// 7. 带有特定注解的方法
@Pointcut("@annotation(com.example.annotation.Log)")
public void methodsWithLogAnnotation() {}
// 8. 带有特定注解的类中的所有方法
@Pointcut("@within(org.springframework.stereotype.Service)")
public void serviceAnnotatedClasses() {}
// 9. 组合切点
@Pointcut("serviceLayer() && anyPublicMethod()")
public void publicServiceMethods() {}
}
IOC与AOP的关系
1. AOP依赖IOC
AOP的实现依赖于IOC容器:
// AOP的织入发生在Bean初始化后置处理阶段
public abstract class AbstractAutoProxyCreator implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判断是否需要代理
if (shouldProxy(bean, beanName)) {
// 创建代理对象
return createProxy(bean, beanName, specificInterceptors);
}
}
return bean;
}
}
时序:
1. IOC容器启动
2. 创建Bean(实例化、属性填充)
3. 初始化Bean
4. BeanPostProcessor后置处理 ← AOP在这里创建代理
5. 返回代理对象(或原始对象)
2. IOC管理AOP组件
// 切面本身也是Spring Bean
@Aspect
@Component // 由IOC管理
public class LoggingAspect {
@Autowired // 可以注入其他Bean
private LogService logService;
@Around("@annotation(log)")
public Object logAround(ProceedingJoinPoint joinPoint, Log log) {
logService.record(log.value()); // 使用注入的Bean
return joinPoint.proceed();
}
}
3. 典型应用:声明式事务
@Service
public class OrderService {
@Autowired // IOC注入
private OrderRepository orderRepository;
@Transactional // AOP实现事务管理
public void createOrder(Order order) {
orderRepository.save(order);
// 如果抛出异常,AOP会回滚事务
}
}
// @Transactional的AOP实现(简化版)
@Aspect
public class TransactionAspect {
@Around("@annotation(transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) {
TransactionStatus status = transactionManager.getTransaction(transactional);
try {
Object result = joinPoint.proceed();
transactionManager.commit(status); // 提交
return result;
} catch (Throwable ex) {
transactionManager.rollback(status); // 回滚
throw ex;
}
}
}
实战应用
应用1:统一日志记录
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
@Around("@annotation(com.example.annotation.Log)")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.info("调用方法: {}.{},参数: {}", className, methodName, Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
logger.info("方法执行成功,耗时: {}ms,返回值: {}", endTime - startTime, result);
return result;
} catch (Throwable e) {
logger.error("方法执行异常: {}.{}", className, methodName, e);
throw e;
}
}
}
// 使用
@Service
public class UserService {
@Log // 自动记录日志
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
应用2:权限控制
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String value(); // 需要的权限
}
// AOP切面
@Aspect
@Component
public class PermissionAspect {
@Autowired
private UserContext userContext;
@Before("@annotation(permission)")
public void checkPermission(RequirePermission permission) {
User currentUser = userContext.getCurrentUser();
if (currentUser == null) {
throw new UnauthorizedException("未登录");
}
if (!currentUser.hasPermission(permission.value())) {
throw new ForbiddenException("权限不足");
}
}
}
// 使用
@RestController
@RequestMapping("/api/admin")
public class AdminController {
@RequirePermission("ADMIN")
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.create(user);
}
}
应用3:性能监控
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service..*.*(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
StopWatch stopWatch = new StopWatch(methodName);
stopWatch.start();
try {
return joinPoint.proceed();
} finally {
stopWatch.stop();
if (stopWatch.getTotalTimeMillis() > 1000) {
System.out.println("慢方法警告: " + methodName +
" 耗时: " + stopWatch.getTotalTimeMillis() + "ms");
}
}
}
}
应用4:缓存管理
@Aspect
@Component
public class CacheAspect {
@Autowired
private CacheManager cacheManager;
@Around("@annotation(cacheable)")
public Object cache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String key = generateKey(joinPoint);
Cache cache = cacheManager.getCache(cacheable.value());
// 从缓存获取
Object cached = cache.get(key);
if (cached != null) {
return cached;
}
// 执行方法
Object result = joinPoint.proceed();
// 写入缓存
cache.put(key, result);
return result;
}
private String generateKey(ProceedingJoinPoint joinPoint) {
return joinPoint.getSignature().toShortString() +
Arrays.toString(joinPoint.getArgs());
}
}
应用5:异常统一处理
@Aspect
@Component
public class ExceptionHandlingAspect {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingAspect.class);
@AfterThrowing(pointcut = "execution(* com.example.service..*.*(..))",
throwing = "ex")
public void handleException(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
logger.error("方法执行异常: {},参数: {}", methodName, Arrays.toString(args), ex);
// 发送告警
alertService.sendAlert("方法执行异常", methodName, ex);
}
}
最佳实践
IOC最佳实践
- 优先使用构造器注入
@Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } } - 避免循环依赖 ```java // ❌ 坏设计 @Service public class ServiceA { @Autowired private ServiceB serviceB; }
@Service public class ServiceB { @Autowired private ServiceA serviceA; }
// ✅ 好设计:引入中间层 @Service public class ServiceFacade { @Autowired private ServiceA serviceA; @Autowired private ServiceB serviceB; }
3. **合理使用Bean作用域**
```java
@Service // 默认singleton,无状态
public class UserService {
// 不要有可变成员变量
}
@Component
@Scope("prototype") // 有状态
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
}
AOP最佳实践
- 切点粒度要合理 ```java // ❌ 太宽泛 @Pointcut(“execution(* .(..))”) // 所有方法
// ✅ 合适 @Pointcut(“execution(* com.example.service..(..))”) // 特定包
2. **使用自定义注解**
```java
// ✅ 更清晰、可控
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
}
@Around("@annotation(Auditable)")
public Object audit(ProceedingJoinPoint joinPoint) {
// ...
}
- 避免在切面中抛出未检查异常
@Around("serviceLayer()") public Object handle(ProceedingJoinPoint joinPoint) throws Throwable { try { return joinPoint.proceed(); } catch (Exception e) { // 记录日志 logger.error("Error", e); // 重新抛出,不要吞掉异常 throw e; } } -
注意代理的限制 ```java // ❌ 同类调用不会触发AOP @Service public class UserService {
@Transactional public void methodA() { // … }
public void methodB() { this.methodA(); // ❌ 不会触发事务,因为是内部调用 } }
// ✅ 通过代理调用 @Service public class UserService {
@Autowired
private UserService self; // 注入自己的代理
public void methodB() {
self.methodA(); // ✅ 触发事务
} } ```
面试总结
IOC核心要点
- 本质:控制权反转,由容器管理对象生命周期
- 实现:依赖注入(构造器、Setter、字段)
- 容器:BeanFactory(基础)、ApplicationContext(增强)
- 生命周期:实例化 → 属性填充 → 初始化 → 使用 → 销毁
- 扩展点:BeanPostProcessor、Aware接口等
AOP核心要点
- 目的:分离横切关注点,提高模块化
- 实现:动态代理(JDK、CGLIB)
- 核心概念:切面、切点、通知、连接点
- 通知类型:Before、After、AfterReturning、AfterThrowing、Around
- 应用场景:日志、事务、权限、缓存、监控
常见面试问题
Q1: IOC和DI的区别?
A: IOC是思想,DI是实现。IOC强调控制权反转,DI是具体的注入方式。
Q2: Spring AOP和AspectJ的区别?
A: Spring AOP基于动态代理,运行时织入,只支持方法级别;AspectJ基于字节码,编译时织入,功能更强大。
Q3: JDK动态代理和CGLIB的区别?
A: JDK基于接口,CGLIB基于继承。有接口优先用JDK,无接口用CGLIB。
Q4: AOP在什么时候创建代理?
A: Bean初始化后置处理阶段(BeanPostProcessor.postProcessAfterInitialization)。
Q5: @Transactional的实现原理?
A: 基于AOP,在方法执行前开启事务,执行后提交,异常时回滚。