Spring 对象组织,代理与AOP

本文最后更新于 2025年6月24日 下午

面向对象程序设计中的类型组织方式

在面向对象编程中,合理组织类型之间的关系是构建可维护、可扩展系统的关键。本文将探讨四种主要的类型组织方式:继承、组合、接口和代理,分析它们各自的特点、优势和适用场景。

1. 继承:建立”是什么”的关系

继承通过建立父子类关系来实现代码复用和多态性。子类继承父类的属性和方法,同时可以重写父类方法来实现特定行为。

1
2
3
4
5
6
    [Animal]      (父类: 抽象)
|
+----+----+
| |
[Cat] [Dog] (子类: 具体实现)

核心特点

  • 建立”is-a”关系(子类是父类的一种)
  • 支持多态:通过父类引用调用子类方法
  • 编译时确定方法签名,运行时动态绑定具体实现

代码示例

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
public abstract class Animal {
public abstract void makeSound();

public void sleep() {
System.out.println("Animal is sleeping");
}
}

class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow meow");
}
}

class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof woof");
}
}

// 使用示例
public class InheritanceDemo {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();

cat.makeSound(); // 输出: Meow meow
dog.makeSound(); // 输出: Woof woof
}
}

优势与局限

优势:

  • 代码复用性强
  • 支持多态,提高代码灵活性
  • 符合直观的分类思维

局限:

  • 单继承限制(Java不支持多重继承)
  • 强耦合,父类变化影响所有子类
  • 继承层次过深时难以维护

2. 组合:建立”有什么”的关系

组合通过将其他对象作为成员变量来实现功能复用,建立”has-a”关系。这种方式提供了比继承更大的灵活性。

1
2
3
4
5
6
[Cat]        [Dog]
| |
+------+ +------+
| Bark | | Bark |
+------+ +------+

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SoundMaker {
public void makeSound(String sound) {
System.out.println(sound);
}
}

class Cat {
private SoundMaker soundMaker = new SoundMaker();

public void makeSound() {
soundMaker.makeSound("Meow meow");
}
}

class Dog {
private SoundMaker soundMaker = new SoundMaker();

public void makeSound() {
soundMaker.makeSound("Woof woof");
}
}

优势

  • 避免继承的强耦合问题
  • 支持多重”拥有”关系
  • 运行时可以动态改变行为
  • 更好的封装性

3. 接口:定义”能做什么”的契约

接口定义了一组行为规范,实现类必须提供这些行为的具体实现。接口关注的是对象的能力而非身份。

1
2
3
4
5
6
 [Bark Interface]
|
+-----+-----+
| |
[Cat] [Dog]
(实现接口的具体类)

代码示例

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 interface SoundMakeable {
void makeSound();
}

public interface Moveable {
void move();
}

class Cat implements SoundMakeable, Moveable {
@Override
public void makeSound() {
System.out.println("Meow meow");
}

@Override
public void move() {
System.out.println("Cat is walking silently");
}
}

class Dog implements SoundMakeable, Moveable {
@Override
public void makeSound() {
System.out.println("Woof woof");
}

@Override
public void move() {
System.out.println("Dog is running");
}
}

// 使用示例
public class InterfaceDemo {
public static void main(String[] args) {
SoundMakeable[] animals = {new Cat(), new Dog()};

for (SoundMakeable animal : animals) {
animal.makeSound();
}
}
}

优势

  • 支持多重实现,解决单继承限制
  • 降低耦合,面向契约编程
  • 更好的可测试性和可扩展性

4. 代理:控制对象访问的中介

代理模式通过创建代理对象来控制对原对象的访问,可以在不改变原对象的情况下增加额外功能。

1
2
3
4
5
6
[Caller]
|
[Proxy]
|
[Real Object]

静态代理示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AnimalKeeper {
private Animal animal;

public AnimalKeeper(Animal animal) {
this.animal = animal;
}

public void makeSound() {
System.out.println("Keeper: Animal is about to make sound");
animal.makeSound();
System.out.println("Keeper: Animal finished making sound");
}
}

动态代理示例

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public interface Animal {
void makeSound();
}

class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow meow");
}
}

class AnimalProxy implements InvocationHandler {
private Animal target;

public AnimalProxy(Animal target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before calling " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After calling " + method.getName());
return result;
}

public Animal getProxy() {
return (Animal) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{Animal.class},
this
);
}
}

// 使用示例
public class ProxyDemo {
public static void main(String[] args) {
Animal cat = new AnimalProxy(new Cat()).getProxy();
cat.makeSound();
// 输出:
// Before calling makeSound
// Meow meow
// After calling makeSound
}
}

代理的优势

  • 可以在不修改原对象的情况下增加功能
  • 支持延迟加载、访问控制、日志记录等横切关注点
  • 动态代理提供了运行时的灵活性

5. 四种方式的比较与选择

方式 关系类型 耦合度 灵活性 适用场景
继承 is-a 明确的分类关系,稳定的层次结构
组合 has-a 功能复用,避免继承限制
接口 can-do 很弱 很高 行为规范,多重能力描述
代理 控制访问 中等 中等 访问控制,功能增强

6. 最佳实践建议

  1. 优先使用组合而非继承 :遵循”组合优于继承”原则
  2. 多使用接口 :面向接口编程,提高代码的可扩展性
  3. 合理运用代理 :在需要控制访问或增强功能时考虑代理模式
  4. 避免深层继承 :继承层次不宜过深,一般不超过3-4层

AOP实现

动态代理的CGLIB实现

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
public class TestCGLibProxy {
public void test(){
Enhancer enhancer = new Enhancer();
// 设置被代理的类
enhancer.setSuperclass(UserService.class);
// 设置处理类
enhancer.setCallback(new MyCallback(new UserService()));
// 生成的代理类,继承自UserService
Object o = enhancer.create();
}
}

public class MyCallback implements MethodInterceptor{
Object target ;
public MyCallback(Object target){
this.target = target
}

@Override
public Object intercept(Object obj ,Method method ,Object[] ar,MethodProxy proxy)throws Throwable{
System.out.println("前置");
proxy.invoke(target,args);
System.out.println("后置");
}
return null ;

}

与JDK动态代理的区别

JDK动态代理只能代理实现了接口的类 → 通过接口实现进行代理

而 CGlib 可以代理未实现任何接口的类 → 通过继承类的方式进行代理(不能将基类声明为final)

性能对比

加载阶段 JDK 动态代理只会生成一个代理类, 而GClib会生成多个相关代理类

调用阶段 method 通过反射调用, proxy 通过调用父类目标对象


Spring 对象组织,代理与AOP
http://gadoid.io/2025/06/24/Spring-对象组织,代理与AOP/
作者
Codfish
发布于
2025年6月24日
许可协议