前言
在Java中,代理模式是一种常用的设计模式,用于为其他对象提供一种代理以控制对这个对象的访问。代理模式主要有两种实现方式:静态代理和动态代理。
一、静态代理
静态代理是由程序员手动创建或指定代理类,代理类在程序运行前就已经存在。静态代理通常用于业务层之间的解耦,使得委托类(被代理的类)和代理类之间不存在直接的依赖关系,而是通过共同的接口进行交互。
优点:
- 可以在不修改真实对象的前提下,增加额外的功能。
- 灵活性高,可以根据需要创建多个代理类。
缺点:
- 代理类需要手动编写,增加了代码的复杂性。
- 如果接口增加方法,所有实现接口的类都需要进行修改。
示例:
假设有一个接口Subject
和两个实现类RealSubject
(真实对象)和ProxySubject
(代理对象)。
Subject
接口:
package edu.etime.proxy.demo;
/**
* @Date 2024/7/21 9:51
* @Author liukang
**/
public interface Subject {
void request();
}
RealSubject
(真实对象):
package edu.etime.proxy.demo;
/**
* @Date 2024/7/21 9:52
* @Author liukang
**/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("处理请求");
}
}
ProxySubject
(代理对象):
package edu.etime.proxy.demo;
/**
* @Date 2024/7/21 9:52
* @Author liukang
**/
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject() {
this.realSubject = new RealSubject();
}
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("请求前的处理");
}
private void postRequest() {
System.out.println("请求后的处理");
}
}
测试调用:
package edu.etime.proxy.demo;
/**
* @Date 2024/7/21 9:54
* @Author liukang
**/
public class MyTest {
public static void main(String[] args) {
// 创建代理对象
ProxySubject proxySubject = new ProxySubject();
// 调用代理对象中的request方法
proxySubject.request();
}
}
二、动态代理
动态代理是在运行时动态生成代理类。动态代理主要依赖于Java的反射机制。Java提供了两种动态代理的实现方式:基于接口的JDK动态代理和基于类的CGLIB动态代理。
1.JDK动态代理
JDK动态代理:
JDK动态代理是面向接口的代理模式,它要求被代理对象必须实现一个接口。JDK动态代理的核心是InvocationHandler
接口和Proxy
类。
优点:
- 动态生成代理类,减少代码量。
- 代理类实现简单,只需实现
InvocationHandler
接口。
缺点:
- 只能代理实现了接口的类。
示例:
Subject
接口:
package edu.etime.proxy.demo2;
/**
* @Date 2024/7/21 9:51
* @Author liukang
**/
public interface Subject {
void request();
}
RealSubject
(真实对象):
package edu.etime.proxy.demo2;
/**
* @Date 2024/7/21 10:09
* @Author liukang
**/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("处理请求");
}
}
Dynamic
ProxyHandler
(代理对象):
package edu.etime.proxy.demo2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Date 2024/7/21 10:01
* @Author liukang
**/
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
// 实现InvocationHandler接口重写的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*方法参数解释
* Object proxy:代理的对象
* Method method:要运行的方法
* Object[] args:调用方法时传递的参数
* */
System.out.println("请求前的处理");
Object result = method.invoke(target, args);
System.out.println("请求后的处理");
return result;
}
// 创建代理对象的方法
public static <T> T getProxyInstance(T target, Class<T> interfaceClass) {
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),// 指定类加载器 此处就使用传入的接口的类加载器,也可以使用其他的
new Class<?>[]{interfaceClass},// 指定接口,这些接口用于指定生成的代理长什么样子,也就是有哪些方法
new DynamicProxyHandler(target)// 动态代理对象 DynamicProxyHandler里面重写invoke方法,添加逻辑,用于指定生成的代理对象要干什么事情
);
}
}
测试调用:
package edu.etime.proxy.demo2;
/**
* @Date 2024/7/21 10:06
* @Author liukang
**/
public class MyTest {
public static void main(String[] args) {
// 使用
Subject subject = (Subject) DynamicProxyHandler.getProxyInstance(new RealSubject(), Subject.class);
subject.request();
}
}
2.CGLIB动态代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB通过继承被代理类来创建代理对象,因此它不要求被代理类实现接口。
优点:
- 可以代理没有实现接口的类。
缺点:
- 性能上比JDK动态代理稍差。
- 使用复杂度较高。
CGLIB动态代理的关键类:
- Enhancer:CGLIB中最常用的类,用于创建代理对象。它允许你设置被代理类、回调接口等。
- MethodInterceptor:这是CGLIB中用于拦截方法调用的接口。你需要实现这个接口,并在其
intercept
方法中编写你的代理逻辑。
CGLIB动态代理的关键步骤:
- 创建MethodInterceptor实现类:在这个类中,你编写方法拦截逻辑。
- 使用Enhancer设置被代理类和回调:配置Enhancer实例,指定被代理的类和MethodInterceptor实现类。
- 创建代理对象:调用Enhancer的
create()
方法创建代理对象。 - 通过代理对象调用方法:代理对象会拦截所有调用,并将它们转发到你的MethodInterceptor实现类。
示例:
RealSubject
(真实对象):
package com.etime.proxy;
/**
* @Date 2024/7/21 10:32
* @Author liukang
**/
public class RealSubject {
public void request() {
System.out.println("处理请求");
}
}
Cglib
Proxy
(代理对象):
package com.etime.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Date 2024/7/21 10:32
* @Author liukang
**/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用父类(即被代理类)中的方法
System.out.println("After method " + method.getName());
return result;
}
public static <T> T getProxyInstance(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz); // 设置被代理类
enhancer.setCallback(new CglibProxy()); // 设置回调
return (T) enhancer.create(); // 创建代理对象
}
}
测试调用:
package com.etime.proxy;
/**
* @Date 2024/7/21 10:39
* @Author liukang
**/
public class MyTest {
public static void main(String[] args) {
RealSubject realSubject = CglibProxy.getProxyInstance(RealSubject.class);
realSubject.request(); // 这将调用代理对象的request方法,该方法会拦截并增强原始方法
}
}
注意:cglib动态代理需要导入对应的依赖或者jar包
maven依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
在这个示例中,RealSubject
是一个普通的Java类,没有实现任何接口。我们使用CGLIB的Enhancer
类来创建RealSubject
的代理对象。CglibProxy
类实现了MethodInterceptor
接口,并在其intercept
方法中编写了代理逻辑。当通过代理对象调用request
方法时,intercept
方法会被调用,并且我们可以在调用原始方法之前和之后执行额外的逻辑。
注意:由于CGLIB是通过继承来创建代理对象的,因此它不能代理final
类,也不能代理final
方法,因为final
类和方法不能被继承。此外,由于CGLIB依赖于Java的反射机制来生成代理类,因此在性能上可能略逊于JDK动态代理(特别是在处理大量代理对象时)。然而,对于没有实现接口的类,CGLIB是一个很好的选择。