动态代理的几种实现方式及优缺点

动态代理:是使用反射和字节码,在运行期间创建指定接口或类的子类以及它的实例对象的一项技术,通过这个技术可以对代码进行无侵入式的增强。

源文件生成实例对象的过程如下:

在这里插入图片描述
关于动态代理的底层原理,在另一篇文章中已经介绍过了。 你必须要学会的动态代理

java的动态代理技术的实现主要有两种方式:

  • JDK原生动态代理
  • CGLIB动态代理

JDK原生动态代理:

使用到一个类Proxy和一个接口InvocationHandler。Proxy是所有动态类的父类,它提供一个静态方法来创建动态代理的class对象和实例,这个方法就是newProxyInstance(),

InvocationHandler:每个动态代理实例都有一个相关联的InvocationHandler方法,在代理实例调用时,方法调用将被转发到InvocationHandler的invole方法。

代码实验:

User类有两个属性,name和age,代码省略

UserService:

public interface UserService {
    public void addUser(User user);
}

UserServiceImpl:

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(User user) {
        System.out.println("新增用户成功,数据为:"+user.toString());
    }
}

UserServiceProxy:实现InvocationHandler,重写invoke方法,做前置增强,判断用户年龄是否大于0,如果小于等于0就报运行时异常

public class UserServiceProxy implements InvocationHandler {

    private Object realObj;
    private static Logger logger = Logger.getLogger(UserServiceProxy.class.getName());

    public Object getRealObj() {
        return realObj;
    }

    public void setRealObj(Object realObj) {
        this.realObj = realObj;
    }

    public UserServiceProxy(Object realObj) {
        super();
        this.realObj = realObj;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        if(objects!=null && objects.length>0 && objects[0] instanceof User){
            User user = (User)objects[0];
            if(user.getAge()<=0){
                throw new RuntimeException("用户年龄不符合");
            }
        }
        Object ret = method.invoke(realObj,objects);
        logger.info("数据添加成功");
        return ret;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        User user  =new User();
        user.setName("张三");
        user.setAge(0);
        UserService us = new UserServiceImpl();
        UserServiceProxy usp = new UserServiceProxy(us);
        UserService proxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),usp);
        proxy.addUser(user);
    }
}

运行结果:

Exception in thread "main" java.lang.RuntimeException: 用户年龄不符合
	at DynamicProxy.JDK.UserServiceProxy.invoke(UserServiceProxy.java:30)
	at com.sun.proxy.$Proxy0.addUser(Unknown Source)
	at DynamicProxy.JDK.Client.main(Client.java:13)

看来过滤起了作用。将年龄改为大于0的数时,能够正常运行。

CGLIB动态代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理:

  • Enhancer:来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象的所有非final方法的调用都会转发给MethodInterceptor;
  • MethodInterceptor:动态代理对象的方法调用丢回转发到intercept方法进行增强

UserServiceImpl类:无需接口

public class UserServiceImpl {

    public void addUser(User user) {
        System.out.println("新增用户成功,数据为:"+user.toString());
    }
}

UserServiceProxy:逻辑几乎一样

public class UserServiceProxy implements MethodInterceptor {

    private static Logger logger = Logger.getLogger(UserServiceProxy.class.getName());

    public Object intercept(Object o, Method method, Object[] objects,MethodProxy proxy) throws Throwable {
        if(objects!=null && objects.length>0 && objects[0] instanceof User){
            User user = (User)objects[0];
            if(user.getAge()<=0){
                throw new RuntimeException("用户年龄不符合");
            }
        }
        Object ret = proxy.invokeSuper(realObj,objects);
        logger.info("数据添加成功");
        return ret;
    }
}

Client

public class Client {
    public static void main(String[] args) {
        User user  =new User();
        user.setName("张三");
        user.setAge(0);
        Enhancer enhancer = new EnHancer();
        enhancer.setSuperClass(UserServiceImpl.class);
        enhancer.setCallback(new UserServiceProxy());
        UserServiceImpl usi = (UserServiceImpl) enhancer.create();
        usi.addUser(user);
    }
}

总结:

  • JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口)
  • CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1. 可扩展架构的两种扩展方式包括垂直扩展和水平扩展。 - 垂直扩展指增加单个服务器的处理能力,如增加CPU、内存、硬盘等硬件资源,或者优化软件设计,提高单机性能。优点是对于系统整体结构不需要做太大的改变,但缺点是成本较高,受到单机硬件限制。 - 水平扩展指增加多台服务器来处理请求,将负载分布到多个服务器上。优点是可扩展性好、成本相对较低,但需要做出系统结构改变,需要考虑如何分配请求和协调多个服务器之间的通信。 2. 实现水平扩展中请求自动分配的方法有以下几种: - 轮询:将请求均匀地分配给所有服务器,适用于服务器性能基本相同的情况。 - 基于负载均衡算法的分配:根据不同的负载均衡算法(如轮询、加权轮询、随机、IP哈希、URL哈希等),将请求分配给不同的服务器。 - 基于服务发现的分配:使用服务注册与发现机制(如Zookeeper、Consul等),将请求发送到可用的服务节点。 3. 水平扩展中如何复用cache结果: - 使用分布式缓存:将缓存数据存储在分布式缓存中,多个服务器可以共享缓存数据,减少重复计算。 - 使用HTTP缓存:在Web应用中,可以使用HTTP缓存机制(如浏览器缓存、CDN缓存等),将请求结果缓存在客户端或CDN上,减少服务器压力。 - 使用本地缓存:在每个服务器上都可以使用本地缓存,但需要考虑缓存的一致性问题。 ### 回答2: 1. 可扩展架构的两种扩展方式包括垂直扩展和水平扩展。 - 垂直扩展:通过增加单个服务器的硬件资源来提高系统的性能和容量。优点是简单且易于实施,只需对现有服务器进行升级即可;缺点是成本较高且可能达到硬件极限,无法无限扩展。 - 水平扩展:通过增加服务器的数量来提高系统的性能和容量。优点是可以相对容易地实现无限横向扩展,以应对不断增长的负载;缺点是需要管理和协调多个服务器,并且需要适当的负载均衡策略来确保资源分配均衡。 2. 实现水平扩展中请求自动分配的方法包括: - 负载均衡器(Load Balancer):通过将传入请求分发到多个服务器上来平衡负载。可以基于不同的负载均衡算法(如轮询、最少连接等)来进行请求的分配。 - DNS轮询(DNS Round Robin):通过在DNS服务器上配置多个IP地址,每次客户端请求时,DNS服务器将IP地址以轮询方式返回给客户端,从而将请求平均分配到不同的服务器上。 - 反向代理服务器:在服务器和客户端之间增加一个反向代理层,客户端的请求先发送到反向代理服务器,再由代理服务器将请求转发到后端的多个服务器上。 3. 在水平扩展中复用cache结果的方法有: - 使用分布式缓存系统:将缓存数据存储在多个服务器上,以支持水平扩展和高可用性。当需要复用cache结果时,可以根据哈希算法将请求路由到存储有相同缓存数据的服务器上。 - 使用共享内存缓存:在多个服务器之间共享缓存数据的内存存储空间。多个服务器可以通过读取和写入共享内存来复用缓存结果,从而避免重复计算或查询外部缓存系统。 - 使用分布式缓存工具库:使用像Redis或Memcached等开源分布式缓存工具库,在多个服务器上配置相同的缓存配置,以实现共享缓存数据。当某个服务器需要复用缓存结果时,可以直接从缓存中获取相同的数据。 ### 回答3: 1. 可扩展架构的两种扩展方式是垂直扩展和水平扩展。 - 垂直扩展是通过增加硬件资源来提高系统性能,例如增加更强大的服务器、增加更多的内存或处理器核心等。优点是简单易行,不需要对系统架构做大的改变,可以立即生效。缺点是成本较高,性能提升有限,且存在瓶颈,难以进一步扩展。 - 水平扩展是通过增加服务器的数量来提高系统性能,例如将负载分散到多个服务器上。优点是可扩展性强,能够满足更大规模的流量和用户需求,并且可以实现高可用性。缺点是需要对系统进行改造和优化,需要考虑分布式架构、数据一致性等问题。 2. 在实现水平扩展中,常用的请求自动分配方法有: - 负载均衡器:通过将请求均匀地分发到多个服务器上,实现请求的自动分配。常见的负载均衡算法有轮询、随机、最少连接等。负载均衡器可以实现高可用性和性能的提升,但也会增加系统的复杂性和单点故障的风险。 - 反向代理:将请求发送到反向代理服务器,反向代理服务器根据一定的策略将请求转发到后端的服务器上。反向代理可以隐藏后端的真实服务器,并且可以根据不同的需求进行灵活的配置和策略控制。 3. 在水平扩展中,可以通过一些方法来复用cache结果: - 共享缓存服务:将缓存数据存储在共享的缓存服务器上,多个应用程序可以共享同一份缓存数据。例如使用分布式缓存系统如Redis、Memcached等,这样可以减少多次重复计算,提高系统性能和可扩展性。 - 分布式缓存:将缓存数据分布在多个服务器上,每个服务器负责一部分缓存数据。例如使用分布式缓存系统如Hazelcast、Ehcache等,这样可以实现横向扩展,在大规模负载的情况下提供高效的缓存服务。 以上是对问题的简要回答,可根据需要进一步展开和详细说明。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值