Java 经典面试题解析:AOP 与 IOC 设计思想
Java 经典面试题解析:AOP 与 IOC 设计思想
一、IOC(控制反转)设计思想
1. 核心设计思想
IOC 的本质是将对象的创建权由开发者转移给框架,解决了传统开发中对象依赖关系复杂、耦合度高的问题。在传统开发中,开发者需要手动通过new关键字创建对象并维护依赖关系(如 Service 依赖 Dao、Controller 依赖 Service);而在 IOC 思想下,对象的创建、初始化、依赖注入等操作由容器(如 Spring 容器)统一管理,开发者只需关注对象的业务逻辑。
2. 实现原理
IOC 的实现依赖工厂模式和反射机制:
-
工厂模式:Spring 容器作为 “对象工厂”,根据配置文件(XML)或注解(如
@Component、@Service)中的元数据,通过反射动态创建对象。 -
依赖注入(DI):容器在创建对象时,自动将其依赖的其他对象注入(如通过
@Autowired注解),无需开发者手动关联。
3. 项目案例与应用场景
案例:电商订单系统的 Service 层依赖管理
在传统开发中,订单 Service 需要手动创建商品 Dao 和用户 Dao 的实例:
public class OrderService {
// 手动创建依赖对象,耦合度高
private ProductDao productDao = new ProductDao();
private UserDao userDao = new UserDao();
public void createOrder() {
productDao.checkStock();
userDao.verifyUser();
// 业务逻辑...
}
}
采用 IOC 思想后,依赖关系由 Spring 容器管理:
@Service
public class OrderService {
// 依赖由容器自动注入,无需手动创建
@Autowired
private ProductDao productDao;
@Autowired
private UserDao userDao;
public void createOrder() {
productDao.checkStock();
userDao.verifyUser();
// 业务逻辑...
}
}
经典应用场景:
- 对象生命周期管理:如数据库连接池对象的创建与销毁由容器统一控制,避免资源泄露。
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class DataSourceExample {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = context.getBean("dataSource", DataSource.class);
// 使用dataSource获取数据库连接进行操作,容器会管理其生命周期
}
}
在applicationContext.xml中配置:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
- 依赖解耦:在多层架构(Controller→Service→Dao)中,通过依赖注入减少层间耦合,便于单元测试(可通过 Mock 框架替换真实依赖)。
// Dao接口
public interface UserDao {
void saveUser();
}
// Dao实现类
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存用户到数据库");
}
}
// Service接口
public interface UserService {
void save();
}
// Service实现类
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 通过构造函数注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.saveUser();
}
}
// 测试类
public class Main {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserService userService = new UserServiceImpl(userDao);
userService.save();
}
}
- 配置集中管理:如数据源参数(URL、用户名、密码)通过配置文件注入,避免硬编码。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:database.properties")
public class DatabaseConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
public void printConfig() {
System.out.println("URL: " + url);
System.out.println("Username: " + username);
System.out.println("Password: " + password);
}
}
在database.properties中配置:
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456
测试:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DatabaseConfig config = context.getBean(DatabaseConfig.class);
config.printConfig();
}
}
二、AOP(面向切面编程)设计思想
1. 核心设计思想
AOP 的核心是将分散在多个业务逻辑中的通用功能(如日志、事务、权限校验)抽取为 “切面”,使业务逻辑与通用功能分离,实现 “关注点分离”。传统开发中,通用功能需重复编写在各业务方法中(如每个 Service 方法都添加日志打印);而 AOP 通过动态代理技术,在不修改原有代码的前提下,将通用功能 “织入” 目标方法的执行流程中。
2. 实现原理
AOP 基于动态代理实现,Spring 中分为两种代理方式:
-
JDK 动态代理:针对实现接口的类,通过
Proxy类动态生成代理对象,代理逻辑封装在InvocationHandler的invoke方法中。 -
CGLIB 动态代理:针对没有实现接口的类,通过继承目标类生成代理子类,重写方法实现代理逻辑。
AOP 的关键概念包括:
-
切面(Aspect):封装通用功能的类(如
LogAspect、TransactionAspect)。 -
连接点(JoinPoint):程序执行过程中的特定位置(如方法调用前、异常抛出后)。
-
切入点(Pointcut):通过表达式(如
execution(* com.service.*.*(..)))指定需要织入切面的连接点。 -
通知(Advice):切面的具体执行逻辑(如
@Before、@After、@Around)。
3. 项目案例与应用场景
案例:电商系统的日志记录与事务管理
在传统开发中,每个业务方法都需要手动添加日志打印和事务控制代码:
public class OrderService {
private OrderDao orderDao;
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void createOrder(Order order) {
// 手动添加日志打印
System.out.println("开始创建订单:" + order.getOrderId());
try {
// 手动开启事务
// 业务逻辑
orderDao.save(order);
// 手动提交事务
} catch (Exception e) {
// 手动回滚事务
System.out.println("创建订单失败:" + e.getMessage());
}
System.out.println("订单创建完成");
}
}
使用 AOP 后,将日志记录和事务管理抽取为切面:
定义切面类:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAndTransactionAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAndTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开始执行方法:" + joinPoint.getSignature().getName());
try {
// 执行目标方法
Object result = joinPoint.proceed();
System.out.println("方法执行成功");
return result;
} catch (Exception e) {
System.out.println("方法执行失败:" + e.getMessage());
throw e;
} finally {
System.out.println("方法执行结束");
}
}
}
业务类简化后代码:
public class OrderService {
private OrderDao orderDao;
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void createOrder(Order order) {
orderDao.save(order);
}
}
经典应用场景:
- 日志记录:在电商系统中,日志记录是 AOP 的重要应用场景之一。通过 AOP 技术,可以在不修改核心业务代码的情况下,对用户的关键操作如登录、下单、支付等进行日志记录。这些日志能够详细记录操作发生的时间、参与对象等信息,在系统出现问题时,可依据日志快速定位和排查问题,同时也有助于系统运行状态的实时监控。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Date;
@Aspect // 声明这是一个切面类
@Component // 将切面类纳入Spring容器管理
public class LogAspect {
// 定义切入点表达式,拦截com.example.service包下所有类的所有方法
@Before("execution(* com.example.service..*.*(..))")
public void logOperation(JoinPoint joinPoint) {
// 获取被拦截的方法名
String methodName = joinPoint.getSignature().getName();
// 获取当前时间作为日志记录时间
Date currentTime = new Date();
System.out.println("[" + currentTime + "] 执行方法:" + methodName + ",参数:" + java.util.Arrays.toString(joinPoint.getArgs()));
}
}
- 事务管理:银行转账系统中数据的一致性至关重要。利用 AOP 对涉及资金变动的方法添加事务切面,在执行转账操作时,可确保一系列相关操作要么全部成功提交,要么全部回滚,避免出现数据不一致的情况,保障资金安全和数据的完整性。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@Aspect
@Component
public class TransactionAspect {
private final TransactionTemplate transactionTemplate;
public TransactionAspect(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
// 定义切入点表达式,拦截com.example.bank.service包下所有类的所有方法
@Around("execution(* com.example.bank.service..*.*(..))")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
return transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
// 执行目标方法
joinPoint.proceed();
} catch (Exception e) {
// 发生异常时回滚事务
status.setRollbackOnly();
throw e;
}
}
});
}
}
- 权限校验:后台管理系统中的敏感操作需要严格的权限控制。通过 AOP 在需要权限控制的接口(如删除用户接口)添加权限切面,在请求到达接口业务逻辑之前进行拦截,判断请求者是否具备相应权限,只有授权用户的请求才能被放行,从而保障系统数据的安全性和操作的合法性。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Aspect
@Component
public class PermissionAspect {
// 模拟授权用户列表
private static final List<String> AUTHORIZED_USERS = Arrays.asList("admin", "manager");
// 定义切入点表达式,拦截com.example.admin.controller包下所有类的所有方法
@Around("execution(* com.example.admin.controller..*.*(..))")
public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
// 假设从方法参数中获取当前用户(实际应用中从请求上下文获取)
String currentUser = (String) joinPoint.getArgs()[0];
if (AUTHORIZED_USERS.contains(currentUser)) {
// 用户有权限,执行目标方法
return joinPoint.proceed();
} else {
throw new RuntimeException("无权限访问");
}
}
}
更多推荐


所有评论(0)