静态代理:
Java静态代理是设计模式中的一种,它通过创建一个代理类来代替原始类,从而提供额外的功能或控制原始类的访问。
如何使用Java静态代理
要创建一个静态代理,你需要有一个接口,一个实现该接口的目标类,以及一个代理类,该代理类也实现了该接口。代理类持有一个对目标对象的引用,并在其方法调用中调用目标对象的相应方法。
// 定义一个接口
interface Calculator {
int add(int a, int b);
}
// 目标类
class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
}
// 代理类
class CalculatorProxy implements Calculator {
private Calculator calculator;
public CalculatorProxy(Calculator calculator) {
this.calculator = calculator;
}
@Override
public int add(int a, int b) {
System.out.println("Before calculation");
int result = calculator.add(a, b);
System.out.println("After calculation");
return result;
}
}
// 测试类
public class ProxyExample {
public static void main(String[] args) {
Calculator target = new CalculatorImpl();
Calculator proxy = new CalculatorProxy(target);
int result = proxy.add(5, 3);
System.out.println("Result: " + result);
}
}
在这个例子中,Calculator
是一个接口,CalculatorImpl
是目标类,CalculatorProxy
是代理类。当我们调用代理类的add
方法时,它会首先打印"Before calculation",然后调用目标对象的add
方法,最后打印"After calculation"。
为什么使用静态代理
解耦:可以在不修改目标对象的情况下,通过代理对象添加额外的功能或控制访问。
保持干净:代理类可以为目标对象提供一个干净的接口,隐藏了目标对象的实现细节。
权限控制:可以在代理类中添加额外的权限检查,以控制对目标对象的访问。
缺点
代码冗余:每次添加一个新的功能都需要创建一个新的代理类。
静态:因为代理类是静态的,所以不能在运行时改变它们的行为。
总的来说,静态代理是一种简单且直接的方式来实现代理模式,它在很多情况下都是非常有用的,特别是当你需要对目标对象进行额外控制或添加额外功能时
动态代理:
Java动态代理是Java语言提供的一个强大的特性,它允许你在运行时创建代理类和代理实例。这使得你可以在运行时为一个接口创建一个代理实例,而不需要手动编写实现该接口的类。动态代理通常在需要添加额外逻辑(例如日志记录、事务管理等)到现有代码中而不修改现有代码的情况下使用。
- 是在内存中生成代理对象的一种技术
- 无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在JVM的运行区造一个代理对象,只需对需要修改的部分进行编辑。
编写流程:
- 准备一个目标类对象
- 使用jdk的API动态的生成代理对象
- 调用代理对象执行相应的方法
如何使用Java动态代理
要使用Java动态代理,你需要先创建一个接口,然后实现一个InvocationHandler
接口,最后使用Proxy.newProxyInstance()
方法创建代理实例
1.ClassLoader loader:
固定写法,指定目标类对象的类的加载器即可,用于加载目标类及其接口的字节码文件,通常使用目标类的字节码文件调用getClassLoader()方法可得到。
2.Class<?>[] interfaces:
固定写法,指定目标类的所以接口的字节码对象的数组,通常使用目标类的字节码文件调用getinterfaces()方法可得到。
3.InvocationHander h:
这个参数是一个接口,主要关注它里面的一个方法,它会在代理类调用方法时执行,也就是说,在代理类对象中调用的任何方法都会执行invoke()方法。所以在此方法中进行代码的扩展。
invoke()方法中参数的含义:
- proxy:就是代理类对象的一个引用也就是Proxy.newProxyInstance()方法的返回值;此引用几乎不会用到。
- method:对应的就是触发invoke执行的方法的Method对象。假如我们调用了Xxx方法,该方法触发了invoke执行,那么method就是Xxx方法对应的反射对象Method。
- args:代理对象调用方法时传入的实际参数
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public interface Hello {
void sayHello();
}
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello World!");
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
Hello hello = new HelloImpl();
Hello proxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
new MyInvocationHandler(hello)
);
proxy.sayHello();
}
}
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method execution.");
Object result = method.invoke(target, args);
System.out.println("After method execution.");
return result;
}
}
在上面的例子中,我们首先定义了一个
Hello
接口和一个实现该接口的HelloImpl
类。然后,我们创建了一个MyInvocationHandler
类,实现了InvocationHandler
接口,该类在方法调用前后打印日志。最后,在DynamicProxyExample
类中,我们使用Proxy.newProxyInstance()
方法创建了一个代理实例,并传入了一个MyInvocationHandler
实例
为什么使用动态代理
解耦:你可以将通用逻辑(如日志记录、事务管理等)与特定的业务逻辑分开。
代码复用:可以为多个对象提供相同的额外功能,而无需在每个类中重复相同的代码。
运行时改变行为:你可以在运行时改变对象的行为,而无需修改源代码。
注意事项
接口代理:动态代理只能为接口创建代理,不能为类创建代理。
性能开销:动态代理可能会带来一些性能开销,因为它涉及反射调用。
总的来说,Java动态代理是一个非常强大的特性,可以在很多场景中帮助你更加灵活地管理和组织你的