java代理模式介绍
1、代理模式
代理模式是一种比较简单易懂的设计模式,通俗讲就是通过代理对象来代替真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能,扩展目标对象的功能。
代理模式的作用主要是扩展目标对象的功能,可以在调用目标对象的方法前后增加一些自定义的操作。
2、静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。
上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自定义的操作。
下面通过代码展示:
- 定义接口
1234
package
com.dodo.proxy;
public
interface
SendService {
String sendMsg(String message);
}
- 创建原目标类并实现上面的接口
12345678
package
com.dodo.proxy;
public
class
SendServiceImpl
implements
SendService {
@Override
public
String sendMsg(String message) {
System.out.println(
"发送短信:"
+ message);
return
message;
}
}
- 创建代理类并实现上面的接口,并将目标类注入到代理类种
12345678910111213141516171819
package
com.dodo.proxy;
public
class
SendServiceProxy
implements
SendService {
private
final
SendService sendService;
public
SendServiceProxy(SendService sendService) {
this
.sendService = sendService;
}
@Override
public
String sendMsg(String message) {
System.out.println(
"代理前操作……"
);
sendService.sendMsg(message);
System.out.println(
"代理后操作……"
);
return
message;
}
public
static
void
main(String[] args) {
SendService sendService =
new
SendServiceImpl();
SendServiceProxy proxy =
new
SendServiceProxy(sendService);
proxy.sendMsg(
"信息内容"
);
}
}
- 打印结果展示
123
代理前操作……
发送短信:信息内容
代理后操作……
3、动态代理
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。从JVM角度来说,动态代理是在运行过程中动态生成类字节码,然后加载到JVM中。
就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理、CGLIB 动态代理等等。
3.1、JDK动态代理
在JDK动态代理中InvocationHandler接口和Proxy类是最重要的两个核心。
jdk动态代理实现步骤:
- 定义一个接口及其实现类
- 自定义实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中调用目标对象的方法并做一些额外的操作。
- 定义一个类通过Proxy.newProxyInstance来创建代理对象。
下面通过代码展示:
- 定义一个接口及其实现类
1234
package
com.dodo.proxy;
public
interface
SendService {
String sendMsg(String message);
}
12345678package
com.dodo.proxy;
public
class
SendServiceImpl
implements
SendService {
@Override
public
String sendMsg(String message) {
System.out.println(
"发送短信:"
+ message);
return
message;
}
}
- 自定义实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中调用目标对象的方法并做一些额外的操作。
123456789101112131415161718
package
com.dodo.proxy.jdkProxy;
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;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
System.out.println(
"代理前操作……"
);
Object result = method.invoke(target, args);
System.out.println(
"代理后操作……"
);
return
result;
}
}
//invoke()
- 定义一个类通过Proxy.newProxyInstance来创建代理对象
12345678910111213141516171819202122232425package
com.dodo.proxy.jdkProxy;
import
com.dodo.proxy.SendService;
import
com.dodo.proxy.SendServiceImpl;
import
java.lang.reflect.Proxy;
public
class
JdkProxyfactory {
/**
* 主要通过Proxy.newProxyInstance()方法获取某个类的代理对象
*
* @param target
* @return
*/
public
static
Object getProxy(Object target) {
return
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new
MyInvocationHandler(target));
}
public
static
void
main(String[] args) {
SendService sendService = (SendService) JdkProxyfactory.getProxy(
new
SendServiceImpl());
sendService.sendMsg(
"Hello World"
);
}
}
- 打印结果展示
123
代理前操作……
发送短信:Hello World
代理后操作……
缺点:只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
3.2、CGLIB动态代理
在CGLIB动态代理中MethodInterceptor接口和Enhancer类是两个重要的核心。
CGLIB动态代理实现步骤:
- 定义一个类
- 自定义实现MethodInterceptor接口的类,并重写intercept方法,intercept用于拦截增强被代理类的方法;和JDK动态代理中的invoke方法类似;
- 通过Enhancer类的create()方法创建代理类
下面通过代码展示:
- 定义一个类
1234567
package
com.dodo.proxy.cglibProxy;
public
class
CglibSendMsgService {
public
String sendMsg(String message) {
System.out.println(
"发送短信:"
+ message);
return
message;
}
}
- 自定义实现MethodInterceptor接口的类,并重写intercept方法
123456789101112131415package
com.dodo.proxy.cglibProxy;
import
net.sf.cglib.proxy.MethodInterceptor;
import
net.sf.cglib.proxy.MethodProxy;
import
java.lang.reflect.Method;
public
class
MyInterceptor
implements
MethodInterceptor {
@Override
public
Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
throws
Throwable {
System.out.println(
"代理前操作……"
);
Object object = methodProxy.invokeSuper(o, args);
System.out.println(
"代理后操作……"
);
return
object;
}
}
- 通过Enhancer类的create()方法创建代理类
123456789101112131415161718package
com.dodo.proxy.cglibProxy;
import
net.sf.cglib.proxy.Enhancer;
public
class
CglibProxyFactory {
public
static
Object getProxy(Class clazz) {
Enhancer enhancer =
new
Enhancer();
enhancer.setClassLoader(clazz.getClassLoader());
enhancer.setSuperclass(clazz);
enhancer.setCallback(
new
MyInterceptor());
return
enhancer.create();
}
public
static
void
main(String[] args) {
CglibSendMsgService aliSendMsgService = (CglibSendMsgService) CglibProxyFactory.getProxy(CglibSendMsgService.
class
);
aliSendMsgService.sendMsg(
"Hello World"
);
}
}
- 打印结果展示
123
代理前操作……
发送短信:Hello World
代理后操作……
3.3、JDK动态代理和CGLIB动态代理对比
JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。
另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性