当前位置:首页 > CN2资讯 > 正文内容

java 静态代理 动态代理(jdk、cglib两种方式)java动态代理的两种方式

2天前CN2资讯


首先我们要理解,什么是代理,这可真是一个难题,我们这里要讲的代理,更像服务器的反向代理,我们访问反向代理,再由反向代理把我们的请求发送至真实服务器,由服务器返回结果。我们这要讲的代理,也是一个代理类,我们访问代理对象,代理对象不处理我们的请求,而是由实际对象(被代理对象)处理。
然而,这有什么好处呢?使用代理,我们可以在不修改类源码的情况下,(在代理中)对其功能进行增强。比如,我们常见的记录日志,权限控制,参数检测等。

1静态代理

静态代理是由程序员编写,实实在在存在的代理类,原理是代理类实现被代理类相同的接口,在代理类中的方法中,调用被代理类的同名方法。我们以图书馆借书为例:
定义接口

// 定义接口
public interface IBorrowBooks {
public void enter() throws Exception;
public void register(String book) throws Exception;
public void leave() throws Exception;
}

借阅者

// 借阅者
public class Borrower implements IBorrowBooks{
public String name;
public Borrower(String name){
=name;
}
@Override
public void enter() {
System.out.println(+"进入图书馆");
}

@Override
public void register(String book) {
System.out.println(+"借阅"+book);
}

@Override
public void leave() {
System.out.println(+"离开图书馆");
}
}

执行借书过程

public class Test {
public static void main(String[] args){
// 直接运行借书流程
Borrower borrower = new Borrower("张三");
borrower.enter();
borrower.register("《射雕英雄传》");
borrower.leave();
}
}

运行结果:
张三进入图书馆
张三借阅《射雕英雄传》
张三离开图书馆
过了一段时间,图书馆改造升级,安装了门禁,进出都需要刷借阅卡,借书也要刷卡登记,如果我们用代理实现,就可以在不改变原有业务代码的情况下,加上权限检测,当然这里举的例子有点牵强,因为我们改代理类也是改,改原来的代码也是改,那是因为我们这里只是代理了这一种对象,如果该接口有多个实现,那么我改代理类就比改原有业务代码划算了。
借阅者代理类:

// 借阅者代理
public class BorrowerProxy implements IBorrowBooks{

private Borrower borrower;
public BorrowerProxy(String name){
this.borrower = new Borrower(name);
}
@Override
public void enter() throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.enter();
}

@Override
public void register(String book) throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.register(book);
}

@Override
public void leave() throws Exception {
if(!this.checkPermissions()){
throw new Exception("没有权限");
}
this.borrower.leave();
}

public boolean checkPermissions(){
System.out.println("刷借阅卡,权限通过");
return true;
}
}

通过代理执行借书流程:

public class Test {
public static void main(String[] args){
// 通过借阅者代理执行借书流程
BorrowerProxy bp = new BorrowerProxy("张三");
try{
bp.enter();
bp.register("《神雕侠侣》");
bp.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

运行结果:
刷借阅卡,权限通过
张三进入图书馆
刷借阅卡,权限通过
张三借阅《神雕侠侣》
刷借阅卡,权限通过
张三离开图书馆

静态代理,使我们的项目代码变的臃肿,并且,实现起来耗时耗力,所以就有了动态代理的出现。

2动态代理

2.1 jdk动态代理(基于接口)

java动态代理是利用反射机制生成一个实现被代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,所以,被代理类必须实现接口。

​​Proxy.newProxyInstance(classLoader,interfaces,invocationHandler)​​;

在上面静态代理的例子中,我们已经有了接口,和被代理类(借阅者),我们还少一个invocationHandler
创建一个InvocationHandler 其实就是个拦截器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("执行了"+method.getName()+"-----"+Arrays.toString(args));

Object res = method.invoke(target,args);

return res;

}
}

实现一个代理工厂类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyFactory {
public static Object createProxy(Object object){
ClassLoader classLoader = object.getClass().getClassLoader();
Class<?>[] interfaces = object.getClass().getInterfaces();
InvocationHandler invocationHandler = new MyInvocationHandler(object);
Object o = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
return o;
}
}

运行一下试试:

public class Test {
public static void main(String[] args){
// 通过jdk动态代理执行
try{
// 注意,这里强转要用接口的类型
IBorrowBooks borrower1 = (IBorrowBooks)ProxyFactory.createProxy(new Borrower("张三"));
borrower1.enter();
borrower1.register("《天龙八部》");
borrower1.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

运行结果:
执行了enter-----null
张三进入图书馆
执行了register-----[《天龙八部》]
张三借阅《天龙八部》
执行了leave-----null
张三离开图书馆

2.2 cglib动态代理(基于子类)

原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。由于是基于子类,所以对于final方法,无法进行代理。
首先通过maven安装cglib库(https://mvnrepository.com/artifact/cglib/cglib/)

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

在上面例子的基础上,我们只需重写拦截器和代理工厂类即可:
拦截器:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 自己实现一个拦截器
public class CglibInterceptor implements MethodInterceptor {
private Object target;
public CglibInterceptor(Object target){
this.target=target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib动态代理,监听开始!这就是切面");
//target 目标对象 objects 参数数组
Object invoke = method.invoke(target, objects);
System.out.println("Cglib动态代理,监听结束!这就是切面");
return invoke;
}
}

代理工厂类:

import net.sf.cglib.proxy.Enhancer;

public class ProxyCglibFactory {

public static Object createProxy(Object target){
Enhancer enhancer = new Enhancer();
// 从这里可以看出,cglib 代理实现的方式是创建了目标对象的子类,所以目标对象要能被继承
enhancer.setSuperclass(target.getClass());
// 设置回调,就是设置拦截器
enhancer.setCallback(new CglibInterceptor(target));
Object result = enhancer.create();
return result;
}
}

运行一下看看效果:

public class Test {
public static void main(String[] args){
// 通过cglib动态代理执行
try{
IBorrowBooks borrower2 = (IBorrowBooks) ProxyCglibFactory.createProxy(new Borrower("张三"));
borrower2.enter();
borrower2.register("《倚天屠龙记》");
borrower2.leave();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

运行结果:
Cglib动态代理,监听开始!这就是切面
张三进入图书馆
Cglib动态代理,监听结束!这就是切面

Cglib动态代理,监听开始!这就是切面
张三借阅《倚天屠龙记》
Cglib动态代理,监听结束!这就是切面

Cglib动态代理,监听开始!这就是切面

张三离开图书馆

Cglib动态代理,监听结束!这就是切面

注意:

这种方式要求被代理类有一个无参构造

如果报错Unable to make protected final java.lang.Class java.lang.ClassLoader.defineC,需要在idea中配置jvm启动参数

–add-opens java.base/java.lang=ALL-UNNAMED


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/25033.html

    分享给朋友:

    “java 静态代理 动态代理(jdk、cglib两种方式)java动态代理的两种方式” 的相关文章

    详解VPS中转教程:提升网络连接的速度与稳定性

    我想给大家介绍一下VPS中转技术。这是一种通过一台或多台服务器进行流量转发的技术,能有效提升网络连接的效率和稳定性。说白了,它就像是在你的网络旅途中增加了一些中转站,让你的数据在传输时更加顺畅和可靠。 在我使用VPS中转技术的过程中,我发现它的应用场景相当广泛。比如,在网络受限的环境中,VPS中转能...

    AkkoCloud评测:为中国用户打造的高性价比VPS与独立服务器解决方案

    AkkoCloud成立于2019年,作为一家具备国人运营背景的主机商,逐渐在海内外VPS和独立服务器市场中占据了一席之地。我的亲身体验让我感受到,AkkoCloud的设计初衷就是为中国大陆的用户提供一个稳健可靠的服务器解决方案。对于很多用户来说,它的出现无疑填补了国内市场的一部分空白。 回想起我探索...

    Ubuntu 多人远程办公的安装与配置指南

    安装和配置远程桌面软件 在远程办公和团队协作日益成为常态的今天,合理配置远程桌面软件显得尤为重要。在Ubuntu的环境下,安装和配置xrdp和vncviewer等工具,可以让多个用户方便地进行远程访问,提高工作效率。下面,我将一步步带你进行相关的软件安装和配置。 1. 安装xrdp 首先,我们要安装...

    WordPress reCAPTCHA插件:提升网站安全与用户体验的最佳解决方案

    reCAPTCHA插件概述 在今天的网络环境中,安全性愈发重要,尤其是对于使用WordPress的网站。WordPress reCAPTCHA插件成为了一种流行的解决方案,它借助Google强大的reCAPTCHA服务,帮助我们有效地区分真实用户和可能扰乱网站的机器程序。在我接触这个插件之后,发现它...

    香港 BGP VPS:提升网络连接稳定性与性能的最佳选择

    什么是 BGP VPS? 在了解香港 BGP VPS 之前,首先要知道“BGP”是什么。BGP,即边界网关协议(Border Gateway Protocol),它是一种用于交换不同网络之间路由信息的协议。简单来说,它负责互联网的连通性,确保数据在各个网络间顺畅传递。而 VSP(虚拟专用服务器,Vi...

    美国VPS服务综合讨论:如何选择最优性价比的解决方案

    美国VPS概述 在互联网时代,确实需要一些更灵活、更高效的工具来满足不同的需求。这时候,虚拟专用服务器(VPS)应运而生。那么,什么是VPS呢?简单来说,VPS可以理解为一台物理服务器的虚拟化版。多个用户共享一台服务器的资源,但每个用户都有自己独立的环境,就像在一栋大楼里各自拥有自己的公寓一样,拥有...