← 返回首页
Spring IOC与AOP详解
Spring IOC与AOP详解
Spring框架是Java开发中最流行的框架之一,其核心特性IOC(控制反转)和AOP(面向切面编程)构成了Spring的基础。本文将深入解析这两个核心概念。
第一部分:IOC(控制反转)
什么是IOC?
IOC(Inversion of Control,控制反转)是Spring的核心思想,它将对象的创建、管理和依赖关系的维护交给Spring容器来处理,而不是由对象自己管理。
传统开发方式的问题
public class UserService {
private UserDao userDao;
public UserService() {
// 强依赖:直接创建依赖对象,耦合度高
this.userDao = new UserDaoImpl();
}
public void addUser(User user) {
userDao.save(user);
}
}
这种方式的问题:
- 耦合度高,难以替换实现
- 不便于单元测试
- 对象生命周期难以管理
IOC的解决方案
public class UserService {
private UserDao userDao;
// 通过构造函数注入依赖
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(User user) {
userDao.save(user);
}
}
IOC容器
Spring提供了两种IOC容器:
- BeanFactory:基础容器,提供基本的IOC功能
- ApplicationContext:高级容器,提供更多企业级特性
BeanFactory
// BeanFactory接口定义
public interface BeanFactory {
// 根据名称获取Bean
Object getBean(String name) throws BeansException;
// 根据类型获取Bean
<T> T getBean(Class<T> requiredType) throws BeansException;
// 判断是否包含指定Bean
boolean containsBean(String name);
// 判断Bean是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
}
ApplicationContext
// 常用的ApplicationContext实现
// 1. ClassPathXmlApplicationContext:从类路径加载配置
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. FileSystemXmlApplicationContext:从文件系统加载配置
ApplicationContext context = new FileSystemXmlApplicationContext("D:/config/applicationContext.xml");
// 3. AnnotationConfigApplicationContext:基于注解的配置
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 4. WebApplicationContext:Web应用的容器(在Spring MVC中使用)
依赖注入(DI)的三种方式
1. 构造函数注入(推荐)
@Service
public class UserService {
private final UserDao userDao;
private final EmailService emailService;
// 构造函数注入:保证依赖不可变,易于测试
public UserService(UserDao userDao, EmailService emailService) {
this.userDao = userDao;
this.emailService = emailService;
}
public void registerUser(User user) {
userDao.save(user);
emailService.sendWelcomeEmail(user.getEmail());
}
}
2. Setter方法注入
@Service
public class UserService {
private UserDao userDao;
private EmailService emailService;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
public void registerUser(User user) {
userDao.save(user);
emailService.sendWelcomeEmail(user.getEmail());
}
}
3. 字段注入(不推荐)
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private EmailService emailService;
public void registerUser(User user) {
userDao.save(user);
emailService.sendWelcomeEmail(user.getEmail());
}
}
为什么推荐构造函数注入?
- 保证依赖不可变(final)
- 明确表达类的依赖关系
- 便于单元测试(可以轻松mock依赖)
- 避免循环依赖
Bean的配置方式
XML配置方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描注解 -->
<context:component-scan base-package="com.example"/>
<!-- 定义Bean -->
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
<bean id="userService" class="com.example.service.UserService">
<!-- 构造函数注入 -->
<constructor-arg ref="userDao"/>
</bean>
</beans>
注解配置方式
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public UserService userService(UserDao userDao) {
return new UserService(userDao);
}
}
自动装配注解
// @Component:通用的组件注解
@Component
public class UserDaoImpl implements UserDao {
// 实现代码
}
// @Repository:数据访问层
@Repository
public class UserDaoImpl implements UserDao {
// 实现代码
}
// @Service:业务逻辑层
@Service
public class UserService {
// 实现代码
}
// @Controller:控制器层
@Controller
public class UserController {
// 实现代码
}
// @Autowired:自动装配
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
@Qualifier("userDaoImpl") // 指定Bean名称
private UserDao userDao2;
@Resource(name = "userDaoImpl") // JSR-250注解
private UserDao userDao3;
}
Bean的生命周期
Spring Bean的生命周期可以分为以下几个阶段:
@Component
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware,
InitializingBean, DisposableBean {
private String beanName;
// 1. 实例化Bean(构造函数)
public LifeCycleBean() {
System.out.println("1. 实例化Bean");
}
// 2. 设置属性值
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
System.out.println("2. 设置属性值");
this.userDao = userDao;
}
// 3. BeanNameAware接口回调
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware.setBeanName: " + name);
this.beanName = name;
}
// 4. BeanFactoryAware接口回调
@Override
public void setBeanFactory(BeanFactory beanFactory) {
System.out.println("4. BeanFactoryAware.setBeanFactory");
}
// 5. BeanPostProcessor - 前置处理
@PostConstruct
public void initMethod() {
System.out.println("5. @PostConstruct初始化方法");
}
// 6. InitializingBean接口回调
@Override
public void afterPropertiesSet() {
System.out.println("6. InitializingBean.afterPropertiesSet");
}
// 7. BeanPostProcessor - 后置处理
@Bean(initMethod = "customInit")
public void customInit() {
System.out.println("7. 自定义初始化方法");
}
// 8. Bean就绪,可以使用
// 9. 容器关闭时销毁Bean
@PreDestroy
public void preDestroy() {
System.out.println("9. @PreDestroy销毁方法");
}
@Override
public void destroy() {
System.out.println("10. DisposableBean.destroy");
}
}
Bean的作用域
// Singleton(默认):单例,整个容器中只有一个实例
@Service
@Scope("singleton")
public class SingletonBean {
// 实现代码
}
// Prototype:原型,每次请求都创建新实例
@Service
@Scope("prototype")
public class PrototypeBean {
// 实现代码
}
// Request:Web应用中,每个HTTP请求一个实例
@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
// 实现代码
}
// Session:Web应用中,每个HTTP Session一个实例
@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionBean {
// 实现代码
}
第二部分:AOP(面向切面编程)
什么是AOP?
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,提高代码的模块化程度。
AOP的核心概念
// 1. 切面(Aspect):横切关注点的模块化
@Aspect
@Component
public class LoggingAspect {
// 2. 连接点(Join Point):程序执行的特定点(如方法调用)
// 3. 切点(Pointcut):匹配连接点的表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 4. 通知(Advice):在切点执行的代码
// 前置通知:在目标方法执行前执行
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("执行方法: " + joinPoint.getSignature().getName());
}
// 后置通知:在目标方法执行后执行(无论成功或失败)
@After("serviceLayer()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
}
// 返回通知:在目标方法成功返回后执行
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("方法返回: " + result);
}
// 异常通知:在目标方法抛出异常时执行
@AfterThrowing(pointcut = "serviceLayer()", throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
System.out.println("方法异常: " + exception.getMessage());
}
// 环绕通知:最强大的通知类型,可以完全控制方法的执行
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("开始执行: " + joinPoint.getSignature().getName());
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("执行完成,耗时: " + (endTime - startTime) + "ms");
return result;
} catch (Exception e) {
System.out.println("执行异常: " + e.getMessage());
throw e;
}
}
}
切点表达式详解
execution表达式
// 语法:execution(修饰符 返回值类型 包名.类名.方法名(参数) 异常)
// 1. 匹配所有public方法
@Pointcut("execution(public * *(..))")
public void anyPublicMethod() {}
// 2. 匹配com.example.service包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePackage() {}
// 3. 匹配com.example.service包及其子包下所有类的所有方法
@Pointcut("execution(* com.example.service..*.*(..))")
public void serviceAndSubPackage() {}
// 4. 匹配所有以save开头的方法
@Pointcut("execution(* save*(..))")
public void saveMethods() {}
// 5. 匹配UserService接口的所有方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
// 6. 匹配第一个参数为String的方法
@Pointcut("execution(* *(String, ..))")
public void firstParamString() {}
// 7. 匹配返回值为void的方法
@Pointcut("execution(void com.example.service.*.*(..))")
public void voidMethods() {}
其他表达式类型
@Aspect
@Component
public class VariousPointcuts {
// within:限定类范围
@Pointcut("within(com.example.service.*)")
public void withinServicePackage() {}
// this:限定代理对象类型
@Pointcut("this(com.example.service.UserService)")
public void thisUserService() {}
// target:限定目标对象类型
@Pointcut("target(com.example.service.UserService)")
public void targetUserService() {}
// args:限定参数类型
@Pointcut("args(com.example.model.User)")
public void userArgument() {}
// @annotation:限定注解
@Pointcut("@annotation(com.example.annotation.Loggable)")
public void loggableMethod() {}
// @within:限定类上的注解
@Pointcut("@within(com.example.annotation.Service)")
public void serviceClass() {}
// @target:限定目标对象上的注解
@Pointcut("@target(com.example.annotation.RestController)")
public void restControllerClass() {}
// bean:限定Bean名称
@Pointcut("bean(userService)")
public void userServiceBean() {}
// 组合表达式
@Pointcut("servicePackage() && !voidMethods()")
public void serviceAndNotVoid() {}
@Pointcut("servicePackage() || controllerMethods()")
public void serviceOrController() {}
}
AOP的实际应用场景
1. 日志记录
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Around("@annotation(com.example.annotation.Loggable)")
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("{}.{}() - 开始执行,参数: {}", className, methodName, Arrays.toString(args));
long startTime = System.currentTimeMillis();
Object result;
try {
result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("{}.{}() - 执行成功,返回值: {}, 耗时: {}ms",
className, methodName, result, endTime - startTime);
return result;
} catch (Exception e) {
log.error("{}.{}() - 执行异常: {}", className, methodName, e.getMessage(), e);
throw e;
}
}
}
// 使用自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
String value() default "";
}
// 在方法上使用注解
@Service
public class UserService {
@Loggable("用户注册")
public void registerUser(User user) {
// 业务逻辑
}
}
2. 性能监控
@Aspect
@Component
public class PerformanceAspect {
private final Map<String, Long> methodStats = new ConcurrentHashMap<>();
@Around("execution(* com.example.service.*.*(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - startTime;
methodStats.merge(methodName, duration, Long::sum);
if (duration > 1000) {
System.out.println("警告: " + methodName + " 执行时间过长: " + duration + "ms");
}
}
}
@Scheduled(fixedRate = 60000) // 每分钟输出一次统计信息
public void printStatistics() {
methodStats.forEach((method, totalTime) ->
System.out.println(method + ": 总耗时 " + totalTime + "ms"));
}
}
3. 缓存
@Aspect
@Component
public class CacheAspect {
private final Map<String, Object> cache = new ConcurrentHashMap<>();
@Around("@annotation(cacheable)")
public Object cacheResult(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String cacheKey = generateKey(joinPoint, cacheable);
Object cachedResult = cache.get(cacheKey);
if (cachedResult != null) {
System.out.println("从缓存中获取: " + cacheKey);
return cachedResult;
}
Object result = joinPoint.proceed();
cache.put(cacheKey, result);
System.out.println("存入缓存: " + cacheKey);
return result;
}
private String generateKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) {
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(joinPoint.getSignature().toShortString());
for (Object arg : joinPoint.getArgs()) {
keyBuilder.append(":").append(arg);
}
return keyBuilder.toString();
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
String key() default "";
}
// 使用
@Service
public class UserService {
@Cacheable
public User findById(Long id) {
// 从数据库查询
return userRepository.findById(id);
}
}
4. 事务管理
@Aspect
@Component
public class TransactionAspect {
@Around("@annotation(transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
Connection connection = null;
try {
connection = getDatabaseConnection();
connection.setAutoCommit(false);
Object result = joinPoint.proceed();
connection.commit();
return result;
} catch (Exception e) {
if (connection != null) {
connection.rollback();
}
throw e;
} finally {
if (connection != null) {
connection.close();
}
}
}
}
// 使用
@Service
public class UserService {
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
// 转账逻辑
}
}
Spring AOP的实现原理
Spring AOP主要通过动态代理实现:
// JDK动态代理(基于接口)
public interface UserService {
void saveUser(User user);
}
public class UserServiceImpl implements UserService {
public void saveUser(User user) {
System.out.println("保存用户: " + user.getName());
}
}
// 代理实现
public class UserServiceProxy implements InvocationHandler {
private Object target;
public UserServiceProxy(Object target) {
this.target = target;
}
public Object getProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置通知: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("后置通知: " + method.getName());
return result;
}
}
// CGLIB代理(基于类)
public class UserServiceCglibProxy implements MethodInterceptor {
private Object target;
public UserServiceCglibProxy(Object target) {
this.target = target;
}
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置通知: " + method.getName());
Object result = proxy.invoke(target, args);
System.out.println("后置通知: " + method.getName());
return result;
}
}
IOC与AOP的综合应用
下面是一个完整的综合示例:
// 1. 定义实体类
@Data
public class User {
private Long id;
private String username;
private String email;
}
// 2. 定义DAO接口和实现
@Repository
public interface UserDao {
User save(User user);
User findById(Long id);
void delete(Long id);
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public User save(User user) {
// 数据库保存逻辑
System.out.println("保存用户到数据库");
return user;
}
@Override
public User findById(Long id) {
// 数据库查询逻辑
System.out.println("从数据库查询用户");
return new User();
}
@Override
public void delete(Long id) {
// 数据库删除逻辑
System.out.println("从数据库删除用户");
}
}
// 3. 定义Service层
@Service
public class UserService {
private final UserDao userDao;
private final EmailService emailService;
public UserService(UserDao userDao, EmailService emailService) {
this.userDao = userDao;
this.emailService = emailService;
}
@Loggable("用户注册")
@Transactional
public void registerUser(User user) {
userDao.save(user);
emailService.sendWelcomeEmail(user.getEmail());
}
@Cacheable
public User getUserById(Long id) {
return userDao.findById(id);
}
@Transactional
public void deleteUser(Long id) {
userDao.delete(id);
}
}
// 4. 定义切面
@Aspect
@Component
public class ApplicationAspect {
// 日志切面
@Around("@annotation(com.example.annotation.Loggable)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法开始执行: " + methodName);
try {
Object result = joinPoint.proceed();
System.out.println("方法执行成功: " + methodName);
return result;
} catch (Exception e) {
System.out.println("方法执行异常: " + methodName + ", " + e.getMessage());
throw e;
}
}
// 性能监控切面
@Around("execution(* com.example.service.*.*(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
if (duration > 100) {
System.out.println("性能警告: " + joinPoint.getSignature().toShortString()
+ " 执行时间: " + duration + "ms");
}
return result;
}
}
// 5. 配置类
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}
// 6. 启动类
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
userService.registerUser(user);
User foundUser = userService.getUserById(1L);
}
}
总结
IOC的优势
- 降低耦合:通过依赖注入降低组件之间的耦合度
- 便于测试:可以轻松mock依赖进行单元测试
- 集中管理:统一管理Bean的生命周期和配置
- 提高可维护性:代码结构清晰,易于理解和维护
AOP的优势
- 关注点分离:将横切关注点从业务逻辑中分离
- 代码复用:避免重复的横切逻辑代码
- 灵活配置:通过切点表达式灵活地定义切面
- 增强功能:在不修改原有代码的情况下增强功能
最佳实践
- 优先使用构造函数注入
- 合理使用注解,避免过度使用
- AOP切面保持简单,避免复杂逻辑
- 合理定义Bean的作用域
- 注意循环依赖问题
- 性能敏感的代码慎用AOP
学习建议
- 从简单的例子开始,逐步理解IOC和AOP的概念
- 阅读Spring源码,了解底层实现原理
- 在实际项目中应用,积累经验
- 学习设计模式,理解IOC和AOP的设计思想
参考资源
- Spring Framework官方文档
- 《Spring源码深度解析》
- 《Spring实战》
- 《Effective Java》
评论区