代理模式 JAVA
文章目录
- 涉及的JAVA语言特性
- 接口和转型
- 接口(Interface)
- 接口的特点:
- 示例代码:
- 转型(类型转换)
- 接口与转型的关系
- 多态与接口的结合
- 总结
- UML
- 代理模型
- 动态代理模式
- Springboot项目中遇到的代理模式
涉及的JAVA语言特性
-
接口(Interfaces):
- 在JDK动态代理中,
Subject
接口定义了代理类和真实类共同实现的方法。 InvocationHandler
接口是JDK动态代理机制的核心,用于定义代理对象的行为。- 在CGLIB中,
MethodInterceptor
接口用于定义拦截方法。
- 在JDK动态代理中,
-
反射(Reflection):
- JDK动态代理heavily依赖反射机制。
Method.invoke()
方法用于在运行时调用目标对象的方法。Proxy.newProxyInstance()
方法使用反射创建代理类的实例。
-
动态类生成:
- JDK动态代理在运行时动态生成代理类。
- CGLIB通过字节码生成技术在运行时创建目标类的子类。
-
泛型(Generics):
- 虽然在给定的代码中没有显式使用,但
Proxy.newProxyInstance()
方法内部使用了泛型。
- 虽然在给定的代码中没有显式使用,但
-
多态(Polymorphism):
- 代理对象可以被当作接口类型使用,展示了多态性。
-
内部类:
DynamicProxyHandler
和CglibProxyInterceptor
可以被实现为内部类,以更好地封装代理逻辑。
-
异常处理:
invoke
和intercept
方法都声明了throws Throwable
,表明它们使用了Java的异常处理机制。
-
面向对象编程(OOP):
- 整个设计展示了封装、继承和多态等OOP概念。
-
方法重写(Method Overriding):
DynamicProxyHandler
重写了invoke
方法。CglibProxyInterceptor
重写了intercept
方法。
-
类加载器(ClassLoader):
- JDK动态代理使用类加载器来加载动态生成的代理类。
-
注解(Annotations):
@Override
注解用于invoke
和intercept
方法,虽然在给定代码中没有显示。
-
可变参数(Varargs):
Method.invoke()
和MethodProxy.invokeSuper()
方法内部使用了可变参数。
-
字节码操作:
- CGLIB通过操作字节码来创建代理类,虽然这主要是库内部实现的细节。
-
回调(Callbacks):
- CGLIB使用回调机制来实现方法拦截。
接口和转型
在面向对象编程中,接口和类型转换(转型)是两个非常重要的概念。它们在Java等编程语言中密切相关,尤其是在实现多态性和解耦时。
接口(Interface)
接口是一种抽象类型,定义了一组没有实现的方法。这些方法必须在实现接口的类中提供具体实现。接口主要用于定义类应该具备的行为,而不关注这些行为的具体实现方式。
接口的特点:
- 方法无实现:接口中的方法都是抽象的(Java 8之后允许定义默认方法
default method
和静态方法)。 - 多重继承:一个类可以实现多个接口,这在Java中提供了一种方式来模拟多重继承。
- 解耦:接口允许程序设计者定义行为标准,不依赖具体的实现。这有助于降低代码的耦合度。
示例代码:
interface Animal {
void makeSound(); // 定义一个方法,没有实现
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // 输出: Dog barks
myCat.makeSound(); // 输出: Cat meows
}
}
解释:在上述代码中,Animal
是一个接口,定义了一个方法makeSound()
。Dog
和Cat
类实现了Animal
接口,并提供了各自的具体实现。通过接口引用,我们可以以统一的方式操作不同的对象(例如Dog
和Cat
),这是多态性的一个重要体现。
转型(类型转换)
转型指的是将一个对象的引用从一种类型转换为另一种类型。在与接口相关的转型中,主要涉及的是向上转型(Upcasting)和向下转型(Downcasting)。
接口与转型的关系
-
向上转型(Upcasting):
- 当一个类实现了某个接口时,可以将该类的对象转型为该接口的引用。这种转换是自动的,不需要强制类型转换。
Animal myDog = new Dog(); // 向上转型,自动进行
解释:这里
Dog
对象被赋给Animal
接口的引用。由于Dog
类实现了Animal
接口,所以这是一种安全的操作,并且是自动进行的。向上转型后,你只能通过Animal
接口调用接口中定义的方法,而无法调用Dog
类中的其他方法。 -
向下转型(Downcasting):
- 如果你需要访问子类中特有的方法,必须将接口类型的引用向下转型为具体的实现类类型。这种转换需要显式的强制类型转换。
Animal myAnimal = new Dog(); Dog myDog = (Dog) myAnimal; // 向下转型,需要强制转换 myDog.bark(); // 调用子类特有的方法
解释:在上面的例子中,
myAnimal
最初是一个Animal
接口类型的引用,但实际上它指向的是一个Dog
对象。如果需要调用Dog
类中特有的方法(如bark()
),必须将myAnimal
向下转型为Dog
类型。这种转换必须显式地进行,并且在运行时,如果myAnimal
不是Dog
对象,可能会抛出ClassCastException
异常。
多态与接口的结合
通过结合接口和转型,你可以实现多态性。多态性允许同一个接口以不同的形式表现出来,这使得代码更为灵活和可扩展。
public class Main {
public static void main(String[] args) {
Animal myAnimal = getAnimal(); // 假设这个方法返回Animal的某个实现
myAnimal.makeSound(); // 多态调用,根据具体类型执行对应方法
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal; // 向下转型
myDog.bark(); // 调用子类特有方法
}
}
public static Animal getAnimal() {
// 返回某个Animal的实现
return new Dog(); // 可能是Dog,Cat等任一实现
}
}
解释:在这个示例中,getAnimal()
方法返回一个Animal
接口的实现。根据返回的具体实现类型,可以使用向下转型来访问特有的方法。instanceof
操作符可以用来检查对象的真实类型,以确保向下转型的安全性。
总结
- 接口定义了行为的规范,而不关心具体实现。它们是实现多态性和解耦的重要工具。
- 向上转型是将子类对象赋给父类或接口类型引用的过程,是自动的。
- 向下转型是将父类或接口类型的引用强制转换为子类类型引用的过程,必须显式进行,并且存在类型安全风险。