Java中反射与Spring AOP全面梳理
一、知识点详解
第一部分:Java 反射机制详解
一、反射概述
1. 什么是反射?
反射是 Java 提供的一种能够在程序运行时动态获取类的所有信息(如类名、方法、属性、构造器、注解等),并能动态操作类属性、调用类方法的机制。
2. 为什么需要反射?
- 动态性:在编译时无法确定要操作哪个类,只有在运行时才能确定
- 灵活性:可以突破访问权限的限制,访问和修改类的私有成员
- 框架开发:Spring、Hibernate、MyBatis 等主流框架的核心技术
3. 反射的优缺点
优点:
- 极大提高了程序的灵活性和扩展性
- 允许实现通用功能,如对象序列化、动态代理等
缺点:
- 性能较低:反射操作比直接代码慢
- 安全性问题:可以绕过权限检查
- 内部暴露:打破了封装性
4. 核心类与接口
反射的核心 API 位于 java.lang.reflect 包中:
Class:代表一个类的实体
Field:代表类的成员变量
Method:代表类的方法
Constructor:代表类的构造方法
Array:动态创建和访问数组
Modifier:解析访问修饰符
二、反射 API 详解与代码示例
首先定义一个简单的 Person 类用于演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class Person { private String name; private int age; public Person() { System.out.println("调用了公共无参构造"); } private Person(String name) { this.name = name; System.out.println("调用了私有有参构造(String)"); } public Person(String name, int age) { this.name = name; this.age = age; System.out.println("调用了公共有参构造(String, int)"); } public void publicMethod(String str) { System.out.println("调用了公共方法: publicMethod(), 参数: " + str); } private void privateMethod() { System.out.println("调用了私有方法: privateMethod()"); } public static void staticMethod() { System.out.println("调用了静态方法: staticMethod()"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
|
1. 获取 Class 对象的三种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ReflectionDemo { public static void main(String[] args) throws Exception { Class<?> clazz1 = Class.forName("com.example.Person"); Class<Person> clazz2 = Person.class; Person person = new Person(); Class<? extends Person> clazz3 = person.getClass(); System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3); } }
|
2. 通过反射创建对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class CreateObjectDemo { public static void main(String[] args) throws Exception { Class<Person> clazz = Person.class; Constructor<Person> publicNoArgConstructor = clazz.getConstructor(); Person p1 = publicNoArgConstructor.newInstance(); Constructor<Person> publicArgConstructor = clazz.getConstructor(String.class, int.class); Person p2 = publicArgConstructor.newInstance("Alice", 25); Constructor<Person> privateConstructor = clazz.getDeclaredConstructor(String.class); privateConstructor.setAccessible(true); Person p3 = privateConstructor.newInstance("Bob"); } }
|
3. 通过反射操作字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class FieldDemo { public static void main(String[] args) throws Exception { Class<Person> clazz = Person.class; Person person = clazz.getConstructor().newInstance(); Field nameField = clazz.getDeclaredField("name"); Field ageField = clazz.getDeclaredField("age"); nameField.setAccessible(true); ageField.setAccessible(true); nameField.set(person, "Charlie"); ageField.setInt(person, 30); System.out.println("Name: " + nameField.get(person)); System.out.println("Age: " + ageField.get(person)); } }
|
4. 通过反射调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class MethodDemo { public static void main(String[] args) throws Exception { Class<Person> clazz = Person.class; Person person = clazz.getConstructor().newInstance(); Method publicMethod = clazz.getMethod("publicMethod", String.class); publicMethod.invoke(person, "Hello Reflection!"); Method staticMethod = clazz.getMethod("staticMethod"); staticMethod.invoke(null); Method privateMethod = clazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(person); Method setNameMethod = clazz.getMethod("setName", String.class); Method getNameMethod = clazz.getMethod("getName"); setNameMethod.invoke(person, "David"); String name = (String) getNameMethod.invoke(person); System.out.println("Name: " + name); } }
|
第二部分:Spring AOP 详解
一、AOP 概述
1. 什么是 AOP?
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(如日志、事务、安全等)与业务逻辑分离,提高代码的模块化程度。
2. AOP 核心概念
- Aspect(切面):横切关注点的模块化
- Join Point(连接点):程序执行过程中的特定点(如方法调用)
- Pointcut(切点):匹配连接点的表达式
- Advice(通知):在特定连接点执行的动作
- Weaving(织入):将切面应用到目标对象的过程
3. Spring AOP 的实现方式
- JDK 动态代理:针对接口实现代理(默认)
- CGLIB 代理:针对类实现代理(需要无参构造函数)
二、Spring AOP 实战
1. 添加 Maven 依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
2. 定义切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.service.PersonService.*(..))") public void personServiceMethods() {} @Before("personServiceMethods()") public void logBefore(JoinPoint joinPoint) { System.out.println("方法执行前: " + joinPoint.getSignature().getName()); System.out.println("参数: " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(pointcut = "personServiceMethods()", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("方法执行后: " + joinPoint.getSignature().getName()); System.out.println("返回值: " + result); } @AfterThrowing(pointcut = "personServiceMethods()", throwing = "error") public void logAfterThrowing(JoinPoint joinPoint, Throwable error) { System.out.println("方法异常: " + joinPoint.getSignature().getName()); System.out.println("异常信息: " + error.getMessage()); } @Around("personServiceMethods()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕通知 - 方法执行前: " + joinPoint.getSignature().getName()); long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); long endTime = System.currentTimeMillis(); System.out.println("环绕通知 - 方法执行后: " + joinPoint.getSignature().getName()); System.out.println("方法执行时间: " + (endTime - startTime) + "ms"); return result; } }
|
3. 定义服务接口和实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public interface PersonService { Person createPerson(String name, int age); Person findPersonById(Long id); void deletePerson(Long id); }
@Service public class PersonServiceImpl implements PersonService { private Map<Long, Person> personMap = new ConcurrentHashMap<>(); private AtomicLong idGenerator = new AtomicLong(0); @Override public Person createPerson(String name, int age) { Long id = idGenerator.incrementAndGet(); Person person = new Person(name, age); personMap.put(id, person); return person; } @Override public Person findPersonById(Long id) { if (!personMap.containsKey(id)) { throw new RuntimeException("Person not found with id: " + id); } return personMap.get(id); } @Override public void deletePerson(Long id) { if (!personMap.containsKey(id)) { throw new RuntimeException("Person not found with id: " + id); } personMap.remove(id); } }
|
4. 测试控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @RestController @RequestMapping("/api/persons") public class PersonController { @Autowired private PersonService personService; @PostMapping public ResponseEntity<Person> createPerson(@RequestParam String name, @RequestParam int age) { Person person = personService.createPerson(name, age); return ResponseEntity.ok(person); } @GetMapping("/{id}") public ResponseEntity<Person> getPerson(@PathVariable Long id) { Person person = personService.findPersonById(id); return ResponseEntity.ok(person); } @DeleteMapping("/{id}") public ResponseEntity<Void> deletePerson(@PathVariable Long id) { personService.deletePerson(id); return ResponseEntity.ok().build(); } }
|
三、高级 AOP 应用
1. 自定义注解实现 AOP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogExecutionTime { }
@Aspect @Component public class ExecutionTimeAspect { @Around("@annotation(LogExecutionTime)") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object proceed = joinPoint.proceed(); long executionTime = System.currentTimeMillis() - start; System.out.println(joinPoint.getSignature() + " 执行时间: " + executionTime + "ms"); return proceed; } }
@Service public class AdvancedPersonService { @LogExecutionTime public void performLengthyOperation() { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
|
2. 使用反射获取方法信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Aspect @Component public class MethodInfoAspect { @Before("execution(* com.example.service.*.*(..))") public void logMethodInfo(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("方法名: " + method.getName()); System.out.println("返回类型: " + method.getReturnType().getSimpleName()); Parameter[] parameters = method.getParameters(); Annotation[][] paramAnnotations = method.getParameterAnnotations(); for (int i = 0; i < parameters.length; i++) { System.out.println("参数 " + i + ": " + parameters[i].getName()); for (Annotation annotation : paramAnnotations[i]) { System.out.println(" 注解: " + annotation.annotationType().getSimpleName()); } } Annotation[] methodAnnotations = method.getAnnotations(); for (Annotation annotation : methodAnnotations) { System.out.println("方法注解: " + annotation.annotationType().getSimpleName()); } } }
|
四、反射与 AOP 的结合应用
1. 动态代理实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| public interface UserService { void addUser(String name); void deleteUser(String name); }
public class UserServiceImpl implements UserService { @Override public void addUser(String name) { System.out.println("添加用户: " + name); } @Override public void deleteUser(String name) { System.out.println("删除用户: " + name); } }
public class DynamicProxyHandler implements InvocationHandler { private Object target; public DynamicProxyHandler(Object target) { this.target = target; } public static Object createProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DynamicProxyHandler(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前: " + method.getName()); System.out.println("参数: " + Arrays.toString(args)); Object result = method.invoke(target, args); System.out.println("方法调用后: " + method.getName()); return result; } }
public class ProxyDemo { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserService proxy = (UserService) DynamicProxyHandler.createProxy(userService); proxy.addUser("Alice"); proxy.deleteUser("Bob"); } }
|
2. 注解处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Secure { String[] roles() default {}; }
public class SecureService { @Secure(roles = {"ADMIN"}) public void adminOperation() { System.out.println("执行管理员操作"); } @Secure(roles = {"USER", "ADMIN"}) public void userOperation() { System.out.println("执行用户操作"); } public void publicOperation() { System.out.println("执行公共操作"); } }
public class SecurityAspect { public void checkSecurity(Object target, Method method, Object[] args) { if (method.isAnnotationPresent(Secure.class)) { Secure secure = method.getAnnotation(Secure.class); String[] requiredRoles = secure.roles(); String currentUserRole = "USER"; if (!Arrays.asList(requiredRoles).contains(currentUserRole)) { throw new SecurityException("用户没有执行该操作的权限"); } } } }
public class SecurityProxyHandler implements InvocationHandler { private Object target; private SecurityAspect securityAspect; public SecurityProxyHandler(Object target) { this.target = target; this.securityAspect = new SecurityAspect(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { securityAspect.checkSecurity(target, method, args); return method.invoke(target, args); } }
|
第三部分:性能优化与最佳实践
1. 反射性能优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class ReflectionOptimization { private static final Map<String, Method> methodCache = new ConcurrentHashMap<>(); private static final Map<String, Field> fieldCache = new ConcurrentHashMap<>(); public static Object invokeMethod(Object obj, String methodName, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { String key = obj.getClass().getName() + "." + methodName; Method method = methodCache.get(key); if (method == null) { Class<?>[] paramTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { paramTypes[i] = args[i].getClass(); } method = obj.getClass().getMethod(methodName, paramTypes); method.setAccessible(true); methodCache.put(key, method); } return method.invoke(obj, args); } public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException { String key = obj.getClass().getName() + "." + fieldName; Field field = fieldCache.get(key); if (field == null) { field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); fieldCache.put(key, field); } return field.get(obj); } }
|
2. AOP 最佳实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| @Aspect @Component @Order(1) public class BestPracticeAspect { private static final Logger logger = LoggerFactory.getLogger(BestPracticeAspect.class); @Pointcut("within(@org.springframework.stereotype.Service *)") public void serviceLayer() {} @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)") public void controllerLayer() {} @Pointcut("execution(public * *(..))") public void publicMethod() {} @Pointcut("serviceLayer() && publicMethod()") public void publicServiceMethod() {} @Around("publicServiceMethod()") public Object monitorServicePerformance(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); long startTime = System.currentTimeMillis(); try { Object result = joinPoint.proceed(); long executionTime = System.currentTimeMillis() - startTime; if (executionTime > 1000) { logger.warn("方法执行缓慢: {}.{}, 耗时: {}ms", className, methodName, executionTime); } else { logger.debug("方法执行: {}.{}, 耗时: {}ms", className, methodName, executionTime); } return result; } catch (Exception e) { logger.error("方法执行异常: {}.{}, 异常: {}", className, methodName, e.getMessage()); throw e; } } @AfterThrowing(pointcut = "controllerLayer()", throwing = "ex") public void handleControllerException(JoinPoint joinPoint, Exception ex) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); logger.error("控制器异常: {}.{}, 异常信息: {}", className, methodName, ex.getMessage()); if (ex instanceof IllegalArgumentException) { } else if (ex instanceof AuthenticationException) { } } }
|
总结
本文全面介绍了 Java 反射和 Spring AOP 的核心概念、使用方法和实际应用:
- Java 反射:提供了在运行时动态获取和操作类信息的能力,是许多框架技术的基石
- Spring AOP:基于代理模式实现,通过切面编程将横切关注点与业务逻辑分离
- 结合应用:反射为 AOP 提供了底层支持,使得动态代理和注解处理成为可能
- 性能优化:通过缓存反射对象、合理使用切点表达式等方式提高性能
- 最佳实践:遵循面向切面编程的原则,保持切面的单一职责和高内聚
反射和 AOP 是 Java 高级编程中的重要技术,正确使用它们可以大大提高代码的灵活性、可维护性和可扩展性。
二、面试题相关梳理
- 反射的定义与核心功能:介绍反射的基本概念和主要功能,包括运行时获取类信息、构造对象、调用方法等,使用文字描述和代码示例说明。
- 反射API与基本使用:详细说明Class对象的获取方式,以及通过反射操作字段、方法和构造函数的API使用方法,包含代码示例。
- 反射的应用场景:列举反射在框架开发、动态代理和工具类库中的典型应用场景,使用文字描述和举例说明。
- 反射的优缺点与性能优化:分析反射的优势和局限性,并提供性能优化的具体方案,使用分点列举方式说明。
- 反射机制相关面试题:整理常见的反射面试题目和参考答案,使用表格形式展示问题类型和考查要点。
- AOP概述与核心概念:解释AOP的基本思想和核心术语,包括切面、连接点、切入点等,使用文字描述和表格对比。
- Spring AOP的实现原理:阐述Spring AOP基于动态代理的实现机制,对比JDK动态代理和CGLIB代理的区别,使用文字描述和代码示例。
- AOP通知类型与使用:详细介绍五种通知类型的特点和使用场景,包含代码示例和执行顺序说明。
- 代理机制与选择策略:分析Spring AOP的代理选择策略和自调用问题解决方案,使用文字描述和代码示例。
- AOP的应用场景:列举AOP在事务管理、日志记录等方面的典型应用,使用分点列举方式说明。
- 常见问题与避坑指南:总结AOP实践中的常见问题和解决方案,包括切面不生效、性能优化等,使用文字描述和代码示例。
一、Java 反射面试题
1. 什么是反射?它的工作原理是什么?
答案:
反射是 Java 提供的一种在运行时动态获取类的信息并操作类属性和方法的机制。
工作原理:
- JVM 在加载类时会创建对应的 Class 对象
- 通过 Class 对象可以获取类的构造方法、字段、方法等信息
- 反射 API 允许在运行时动态创建对象、调用方法、访问字段
核心代码:
1 2 3 4
| Class<?> clazz1 = Class.forName("java.lang.String"); Class<String> clazz2 = String.class; Class<? extends String> clazz3 = "".getClass();
|
2. 反射能获取哪些类信息?
答案:
- 类名、包名、修饰符、父类、接口
- 构造方法、字段、方法、注解
- 泛型信息、数组类型等
3. 反射的优缺点是什么?
优点:
- 动态性:运行时才确定要操作的类
- 灵活性:可以突破访问权限限制
- 通用性:适合编写通用框架和工具
缺点:
- 性能开销:反射操作比直接调用慢
- 安全问题:可以绕过访问控制
- 维护困难:破坏封装性,代码难以理解
4. 如何通过反射创建对象?
答案:
1 2 3 4 5 6 7 8 9 10 11 12
| Class<?> clazz = Class.forName("com.example.User"); User user = (User) clazz.newInstance();
Constructor<?> constructor = clazz.getConstructor(String.class, int.class); User user = (User) constructor.newInstance("Alice", 25);
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class); privateConstructor.setAccessible(true); User user = (User) privateConstructor.newInstance("private");
|
5. 如何通过反射调用方法和访问字段?
答案:
1 2 3 4 5 6 7 8 9
| Method method = clazz.getMethod("setName", String.class); method.invoke(user, "Bob");
Field field = clazz.getDeclaredField("name"); field.setAccessible(true); field.set(user, "Charlie"); String name = (String) field.get(user);
|
6. 反射的性能问题如何优化?
优化策略:
- 缓存反射对象(Class、Method、Field 等)
- 使用
setAccessible(true) 避免安全检查
- 尽量使用直接方法调用而不是反射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ReflectionCache { private static final Map<String, Method> methodCache = new ConcurrentHashMap<>(); public static Object invokeMethod(Object obj, String methodName, Object... args) throws Exception { String key = obj.getClass().getName() + "." + methodName; Method method = methodCache.get(key); if (method == null) { Class<?>[] paramTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { paramTypes[i] = args[i].getClass(); } method = obj.getClass().getMethod(methodName, paramTypes); method.setAccessible(true); methodCache.put(key, method); } return method.invoke(obj, args); } }
|
二、Spring AOP 面试题
1. 什么是 AOP?它解决了什么问题?
答案:
AOP(Aspect-Oriented Programming)面向切面编程,将横切关注点(如日志、事务、安全等)与业务逻辑分离。
解决的问题:
- 代码重复:多个地方需要相同的横切逻辑
- 代码分散:横切逻辑分散在各个方法中
- 维护困难:修改横切逻辑需要修改多处代码
2. AOP 的核心概念有哪些?
答案:
- Aspect(切面):横切关注点的模块化
- Join Point(连接点):程序执行过程中的点(如方法调用)
- Pointcut(切点):匹配连接点的表达式
- Advice(通知):在连接点执行的动作
- Weaving(织入):将切面应用到目标对象的过程
3. Spring AOP 和 AspectJ 的区别?
答案:
| 特性 |
Spring AOP |
AspectJ |
| 实现方式 |
动态代理 |
字节码增强 |
| 织入时机 |
运行时 |
编译时/类加载时 |
| 性能 |
较低 |
较高 |
| 功能 |
仅支持方法级别的切面 |
支持字段、构造方法等 |
| 依赖 |
仅需Spring框架 |
需要AspectJ编译器 |
4. Spring AOP 有哪几种通知类型?
答案:
- @Before:方法执行前执行
- @AfterReturning:方法正常返回后执行
- @AfterThrowing:方法抛出异常后执行
- @After:方法结束后执行(无论正常或异常)
- @Around:环绕通知,最强大的通知类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("方法执行前: " + joinPoint.getSignature().getName()); } @Around("execution(* com.example.service.*.*(..))") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long time = System.currentTimeMillis() - start; System.out.println("方法执行时间: " + time + "ms"); return result; } }
|
5. Spring AOP 的实现原理是什么?
答案:
Spring AOP 基于动态代理实现:
- JDK 动态代理:针对接口实现代理(默认)
- CGLIB 代理:针对类实现代理(需要无参构造)
选择条件:
- 如果目标对象实现了接口,默认使用 JDK 动态代理
- 如果目标对象没有实现接口,使用 CGLIB 代理
- 可以通过配置强制使用 CGLIB:
@EnableAspectJAutoProxy(proxyTargetClass = true)
6. 如何定义切点表达式?
答案:
常用的切点表达式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| execution(public * *(..))
execution(* com.example.service.*.*(..))
execution(* *..*Service.*(..))
@annotation(org.springframework.transaction.annotation.Transactional)
within(com.example.service..*)
this(com.example.service.UserService)
@args(javax.validation.Valid)
|
7. AOP 中的代理对象是什么?
答案:
Spring AOP 创建的是代理对象,而不是原始对象。当调用被代理的方法时,实际上调用的是代理对象的方法,代理对象会执行相应的切面逻辑。
1 2 3 4 5 6 7 8 9 10 11
| @Autowired private UserService userService;
@Autowired @Lazy private UserService userService;
UserService proxy = (UserService) AopContext.currentProxy();
|
三、反射与 AOP 结合面试题
1. Spring 如何利用反射实现依赖注入?
答案:
Spring 通过反射实现依赖注入:
- 扫描类路径,获取所有带有注解的类
- 通过反射创建 Bean 实例
- 通过反射注入依赖关系
- 调用初始化方法
1 2 3 4 5 6 7 8 9 10 11 12
| Class<?> clazz = Class.forName("com.example.UserService"); Object instance = clazz.newInstance();
Field userDaoField = clazz.getDeclaredField("userDao"); userDaoField.setAccessible(true); userDaoField.set(instance, userDaoInstance);
Method initMethod = clazz.getMethod("init"); initMethod.invoke(instance);
|
2. 如何在 AOP 中使用反射?
答案:
在 AOP 中经常使用反射获取方法信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Aspect @Component public class ReflectionAspect { @Before("execution(* com.example.service.*.*(..))") public void inspectMethod(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("方法名: " + method.getName()); System.out.println("返回类型: " + method.getReturnType()); System.out.println("参数注解: " + Arrays.toString(method.getParameterAnnotations())); Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("注解: " + annotation.annotationType().getName()); } } }
|
3. 如何通过自定义注解实现 AOP?
答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogExecutionTime { }
@Aspect @Component public class ExecutionTimeAspect { @Around("@annotation(LogExecutionTime)") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - start; MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class); System.out.println(method.getName() + " 执行时间: " + duration + "ms"); return result; } }
@Service public class UserService { @LogExecutionTime public void processUsers() { } }
|
四、高级面试题
1. Spring AOP 的事务管理是如何实现的?
答案:
Spring 事务管理基于 AOP 实现:
- 使用
@Transactional 注解标记需要事务的方法
- Spring 创建事务管理的代理对象
- 在方法执行前开启事务
- 方法执行后提交或回滚事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Aspect @Component public class TransactionAspect { @Autowired private PlatformTransactionManager transactionManager; @Around("@annotation(transactional)") public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable { TransactionStatus status = transactionManager.getTransaction( new DefaultTransactionDefinition()); try { Object result = joinPoint.proceed(); transactionManager.commit(status); return result; } catch (Exception e) { transactionManager.rollback(status); throw e; } } }
|
2. 如何解决 AOP 中的自调用问题?
答案:
自调用问题:在同一个类中,一个方法调用另一个有 AOP 切面的方法时,AOP 不会生效。
解决方案:
- 将方法拆分到不同的类中
- 通过 AopContext 获取代理对象
- 使用 AspectJ 的编译时织入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Service public class UserService { @Autowired private UserService self; public void methodA() { methodB(); self.methodB(); } @Transactional public void methodB() { } }
@EnableAspectJAutoProxy(exposeProxy = true) public class AppConfig { }
|
3. 如何实现动态数据源切换?
答案:
通过 AOP 和反射实现动态数据源切换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default "master"; }
@Aspect @Component public class DataSourceAspect { @Before("@annotation(dataSource)") public void switchDataSource(JoinPoint joinPoint, DataSource dataSource) { String dsName = dataSource.value(); DynamicDataSourceContextHolder.setDataSource(dsName); } @After("@annotation(dataSource)") public void restoreDataSource(JoinPoint joinPoint, DataSource dataSource) { DynamicDataSourceContextHolder.clearDataSource(); } }
@Service public class UserService { @DataSource("slave") public User getUser(Long id) { return userDao.findById(id); } @DataSource("master") public void saveUser(User user) { userDao.save(user); } }
|
五、实际应用场景
1. 日志记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); @Pointcut("execution(* com.example.service.*.*(..))") public void serviceLayer() {} @Around("serviceLayer()") public Object logServiceMethod(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); logger.info("方法 {} 开始执行,参数: {}", methodName, Arrays.toString(args)); long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - start; logger.info("方法 {} 执行完成,耗时: {}ms,结果: {}", methodName, duration, result); return result; } }
|
2. 性能监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Aspect @Component public class PerformanceAspect { @Around("execution(* com.example.service.*.*(..))") public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable { String className = joinPoint.getTarget().getClass().getSimpleName(); String methodName = joinPoint.getSignature().getName(); String key = className + "." + methodName; long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - start; Metrics.recordTiming(key, duration); return result; } }
|
3. 权限控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Aspect @Component public class SecurityAspect { @Autowired private SecurityService securityService; @Before("@annotation(requiresPermission)") public void checkPermission(JoinPoint joinPoint, RequiresPermission requiresPermission) { String permission = requiresPermission.value(); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (!securityService.hasPermission(auth, permission)) { throw new AccessDeniedException("没有权限: " + permission); } } }
@Service public class AdminService { @RequiresPermission("ADMIN_DELETE") public void deleteUser(Long userId) { } }
|
总结
Java 反射和 Spring AOP 是面试中的高频考点,需要重点掌握:
- 反射:理解原理、掌握 API、知道性能优化方法
- AOP:理解核心概念、掌握各种通知类型、会写切点表达式
- 结合应用:理解 Spring 如何利用反射实现 AOP 和依赖注入
- 实际场景:能够结合实际业务需求设计切面