Java代理是Java编程语言提供的一种用于修改或增强对象行为的机制。
Java代理允许我们在不修改源代码的情况下,通过在运行时创建一个包装对象来拦截对真实对象的调用,从而添加额外的功能或行为。
Java代理主要分为静态代理和动态代理两种类型。
静态代理
静态代理是通过手动编写一个代理类来实现的。代理类和目标对象实现相同的接口,并在代理类中对接口声明的方法进行增强。
静态代理的步骤
步骤 1: 定义一个接口
首先,我们需要定义一个接口,真实对象和代理对象都将实现这个接口。
public interface IService {
void doSomething();
}
步骤 2: 实现真实对象
接着,我们创建一个实现IService
接口的真实对象,实现doSomething
方法。
public class RealService implements IService {
public void doSomething() {
System.out.println("RealService is doing something.");
}
}
步骤 3: 创建静态代理类
创建静态代理类StaticProxy
,它也需要实现IService
接口。
在这个代理类中,有一个IService
类型的属性realService
,通过构造方法注入真实的业务对象。
查看代码
public class StaticProxy implements IService {
private IService realService;
public StaticProxy(IService realService) {
this.realService = realService;
}
public void doSomething() {
System.out.println("Before method execution."); // 前置增强
realService.doSomething(); // 调用真实对象的方法
System.out.println("After method execution."); // 后置增强
}
}
步骤 4: 在业务中使用代理类
在Main
类的main
方法中,创建RealService
的实例,并将其作为参数传递给StaticProxy
的构造方法,创建代理对象。然后通过代理对象调用doSomething
方法。
public class Main {
public static void main(String[] args) {
IService service = new StaticProxy(new RealService());
service.doSomething();
}
}
当调用service.doSomething()
时,实际上调用的是代理对象的doSomething
方法,这个方法会先执行前置处理,然后调用真实对象的doSomething
方法,最后执行后置处理。
这展示了静态代理在真实对象的方法执行前后添加了额外的处理逻辑。通过这种方式,可以在不修改原有代码的情况下,增强或改变对象的行为。
静态代理的特点
静态代理在编译时就已经确定,代理类通常是由程序员手动编写的,实现与目标对象相同的接口,如果接口增加方法,那么代理类和目标对象都需要进行修改,不够灵活。
适用于代理类较少且稳定的场景,因为每增加一个目标类,都需要手动创建一个代理类。
比如说现在,假设我们又有了一个新的接口IAnotherService
和它的实现类AnotherService
:
public interface IAnotherService {
void doAnotherThing();
}
public class AnotherService implements IAnotherService {
public void doAnotherThing() {
System.out.println("AnotherService is doing another thing.");
}
}
如果我们想为AnotherService
也创建一个静态代理,我们还需要定义另一个代理类
查看代码
public class AnotherStaticProxy implements IAnotherService {
private IAnotherService anotherService;
public AnotherStaticProxy(IAnotherService anotherService) {
this.anotherService = anotherService;
}
public void doAnotherThing() {
System.out.println("Before method execution.");
anotherService.doAnotherThing();
System.out.println("After method execution.");
}
}
如果我们有很多这样的服务类,那么我们就需要为每个服务类都创建一个代理类。
这会导致代码重复,并且当接口改变时,所有代理类都需要更新。
动态代理
动态代理是在运行时动态创建的,不需要手动编写代理类。
Java中的动态代理主要有两种实现方式:JDK动态代理和CGLIB(Code Generation Library)。
Spring AOP默认使用JDK动态代理,如果目标对象实现了至少一个接口,Spring AOP会使用JDK动态代理。如果目标对象没有实现任何接口,Spring AOP会使用CGLIB代理。
JDK动态代理
JDK动态代理是基于接口的代理,要求被代理的类必须实现至少一个接口。它通过反射机制生成代理类,并在运行时创建代理对象。
实现原理
Java语言规定一个类只能继承一个类,而动态代理生成的代理类已经继承了java.lang.reflect.Proxy
类。因此,代理类无法再继承其他类,只能通过实现接口的方式来扩展其功能。
Proxy
类在JDK动态代理中充当工厂类的角色,提供静态方法用于生成代理实例。这些方法根据提供的类加载器、接口列表和InvocationHandler
实例动态生成代理类的字节码,并将其加载到JVM中创建代理对象。这些代理实例内部实现了目标对象的接口,并持有一个InvocationHandler
的引用。当代理方法被调用时,实际上是调用了InvocationHandler
的invoke
方法。
代理类继承Proxy
类的主要目的是为了获得一个有参构造器,通过这个构造器可以将InvocationHandler
实例传入新生成的代理类。这样,代理对象就能将方法调用转发给InvocationHandler
处理。
如果代理类直接引用InvocationHandler
而不继承Proxy
,则无法利用Proxy
类提供的工厂方法来动态生成代理类,也会失去JVM层面对动态生成类加载的支持。
Proxy
类不仅仅是为了继承而存在,它还提供了一系列的方法来支持动态代理机制,包括使用类加载器来加载新生成的代理类等。这些功能对于动态生成的代理类来说是非常必要的,直接引用InvocationHandler
而不继承Proxy
将失去这些支持。
主要接口
利用java.lang.reflect.Proxy
类和InvocationHandler
接口,通过反射机制在运行时生成一个实现了指定接口的代理类,并拦截方法调用,以实现额外的处理逻辑。
java.lang.reflect.Proxy
类:这个类提供了创建动态代理对象的方法。它有一个静态方法newProxyInstance
,用于创建一个代理实例。java.lang.reflect.InvocationHandler
接口:这个接口只有一个方法invoke
,每当代理对象的方法被调用时,都会转发到这个invoke
方法。在这个方法中可以定义额外的逻辑来处理方法调用。
使用步骤
- 定义一个接口:创建一个或多个接口,代理对象和真实对象都需要实现这些接口。
public interface MyInterface {
void doSomething();
}
- 实现接口:创建一个类实现该接口。
public class RealObject implements MyInterface {
public void doSomething() {
System.out.println("Doing something...");
}
}
- 实现
InvocationHandler
接口:创建一个类实现InvocationHandler
接口,并实现invoke
方法。
查看代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call.");
Object result = method.invoke(target, args);
System.out.println("After method call.");
return result;
}
}
- 创建代理实例:使用
Proxy
类的newProxyInstance
方法创建代理实例。
查看代码
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
MyInterface realObject = new RealObject();
MyInvocationHandler handler = new MyInvocationHandler(realObject);
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class<?>[] { MyInterface.class },
handler
);
proxy.doSomething();
}
}
CGLIB动态代理
CGLIB动态代理不依赖接口,它可以代理没有实现接口的普通类。CGLIB通过字节码技术生成目标类的子类并重写其中的方法来实现代理功能。
实现原理
使用字节码操作库ASM,通过生成目标类的子类,并在子类中拦截方法调用。因此,CGLIB可以代理没有实现接口的普通类,但不能代理final修饰的类或方法。
ASM是一个字节码操作工具,可以对Java字节码进行读取、分析和修改。
net.sf.cglib.proxy.Enhancer
类:这是CGLIB中的核心类,用于创建代理类。net.sf.cglib.proxy.MethodInterceptor
接口:这个接口类似于JDK动态代理中的InvocationHandler
,它有一个方法intercept
,用于拦截并处理方法调用。net.sf.cglib.proxy.MethodProxy
类:这个类是方法调用的代理,它提供了直接调用原始方法的能力。
使用步骤
- 引入CGLIB依赖:首先,需要在项目中引入CGLIB的依赖。
<!-- Maven依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>版本号</version>
</dependency>
- 创建目标类:创建一个目标类,它不需要实现任何接口。
public class TargetObject {
public void doSomething() {
System.out.println("Doing something...");
}
}
- 实现
MethodInterceptor
接口:创建一个类实现net.sf.cglib.proxy.MethodInterceptor
接口,并实现intercept
方法。
查看代码
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call.");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method call.");
return result;
}
}
- 创建代理实例:使用CGLIB的
Enhancer
类来创建代理实例。
查看代码
import net.sf.cglib.proxy.Enhancer;
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);
enhancer.setCallback(new MyMethodInterceptor());
TargetObject proxy = (TargetObject) enhancer.create();
proxy.doSomething();
}
}
总结
JDK 动态代理:
- 需要目标类实现接口。
- 使用
Proxy.newProxyInstance
方法创建代理对象。 - 通过
InvocationHandler
处理方法调用。
CGLIB:
- 不需要目标类实现接口。
- 使用
Enhancer
类创建代理对象。 - 通过
MethodInterceptor
处理方法调用。