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
// Person.java
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()");
}

// Getter和Setter
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.forName("全限定类名") (最常用)
Class<?> clazz1 = Class.forName("com.example.Person");

// 方式二:类名.class
Class<Person> clazz2 = Person.class;

// 方式三:对象.getClass()
Person person = new Person();
Class<? extends Person> clazz3 = person.getClass();

System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}
}

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;

// 1. 使用 Constructor.newInstance()
Constructor<Person> publicNoArgConstructor = clazz.getConstructor();
Person p1 = publicNoArgConstructor.newInstance();

// 2. 获取有参公共构造器
Constructor<Person> publicArgConstructor = clazz.getConstructor(String.class, int.class);
Person p2 = publicArgConstructor.newInstance("Alice", 25);

// 3. 获取私有构造器
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);

// 调用Getter/Setter
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 {

// 定义切点:匹配PersonService的所有方法
@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);
}
}

// 使用JDK动态代理
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) { // 超过1秒记录警告
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 的核心概念、使用方法和实际应用:

  1. Java 反射:提供了在运行时动态获取和操作类信息的能力,是许多框架技术的基石
  2. Spring AOP:基于代理模式实现,通过切面编程将横切关注点与业务逻辑分离
  3. 结合应用:反射为 AOP 提供了底层支持,使得动态代理和注解处理成为可能
  4. 性能优化:通过缓存反射对象、合理使用切点表达式等方式提高性能
  5. 最佳实践:遵循面向切面编程的原则,保持切面的单一职责和高内聚

反射和 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对象的三种方式
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
// 1. 使用Class.newInstance()(已废弃)
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance();

// 2. 使用Constructor.newInstance()(推荐)
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
User user = (User) constructor.newInstance("Alice", 25);

// 3. 调用私有构造方法
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. 反射的性能问题如何优化?

优化策略

  1. 缓存反射对象(Class、Method、Field 等)
  2. 使用 setAccessible(true) 避免安全检查
  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 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 有哪几种通知类型?

答案

  1. @Before:方法执行前执行
  2. @AfterReturning:方法正常返回后执行
  3. @AfterThrowing:方法抛出异常后执行
  4. @After:方法结束后执行(无论正常或异常)
  5. @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 基于动态代理实现:

  1. JDK 动态代理:针对接口实现代理(默认)
  2. 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
// 匹配所有public方法
execution(public * *(..))

// 匹配指定包下的所有方法
execution(* com.example.service.*.*(..))

// 匹配Service结尾的类中的所有方法
execution(* *..*Service.*(..))

// 匹配带有@Transactional注解的方法
@annotation(org.springframework.transaction.annotation.Transactional)

// 匹配指定包及其子包
within(com.example.service..*)

// 匹配实现了UserService接口的类
this(com.example.service.UserService)

// 匹配参数带有@Valid注解的方法
@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; // 延迟注入,可能需要

// 或者通过AopContext获取当前代理
UserService proxy = (UserService) AopContext.currentProxy();

三、反射与 AOP 结合面试题

1. Spring 如何利用反射实现依赖注入?

答案
Spring 通过反射实现依赖注入:

  1. 扫描类路径,获取所有带有注解的类
  2. 通过反射创建 Bean 实例
  3. 通过反射注入依赖关系
  4. 调用初始化方法
1
2
3
4
5
6
7
8
9
10
11
12
// 模拟Spring的依赖注入
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
// 1. 定义自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

// 2. 定义切面
@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;
}
}

// 3. 使用注解
@Service
public class UserService {

@LogExecutionTime
public void processUsers() {
// 业务逻辑
}
}

四、高级面试题

1. Spring AOP 的事务管理是如何实现的?

答案
Spring 事务管理基于 AOP 实现:

  1. 使用 @Transactional 注解标记需要事务的方法
  2. Spring 创建事务管理的代理对象
  3. 在方法执行前开启事务
  4. 方法执行后提交或回滚事务
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 不会生效。

解决方案

  1. 将方法拆分到不同的类中
  2. 通过 AopContext 获取代理对象
  3. 使用 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() {
// 直接调用,AOP不生效
methodB();

// 通过代理调用,AOP生效
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
// 1. 定义数据源注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "master";
}

// 2. 定义切面
@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();
}
}

// 3. 使用注解
@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 是面试中的高频考点,需要重点掌握:

  1. 反射:理解原理、掌握 API、知道性能优化方法
  2. AOP:理解核心概念、掌握各种通知类型、会写切点表达式
  3. 结合应用:理解 Spring 如何利用反射实现 AOP 和依赖注入
  4. 实际场景:能够结合实际业务需求设计切面