Java动态代理

代理模式

       在讲解动态代理之前,先来简单说一下代理模式。代理模式的作用是为某个对象提供一个代理以控制对这个对象的访问。代理对象和被代理对象需要实现相同的接口,调用者直接与代理对象交互,被代理对象对调用者来说是透明的。某些情况下,一个对象不希望或者不能被外部直接引用就可以考虑使用代理模式。

       代理模式涉及到三个部分:

抽象接口:代理对象和被代理对象的共有接口; 

代理对象:代理对象内部含有对真实对象的引用,从而可以操作真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 

被代理对象:最终被引用的真实对象。

动态代理

       动态代理是一种高级的代理模式,是代理模式的一种扩展形式,在基于AOP的框架中使用较多。

       通常情况下,代理模式中的代理类和被代理类都是固定的,一个代理类对应于一个被代理类,一旦代码完成代理关系就不会再变化。如果有新的类需要被代理,就需要对应的增加一个新的代理类,这将导致系统中的类个数急剧增加,而且导致代码不容易扩展。动态代理可以让系统根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实类。

实现方式

       目前Java开发包中包含了对动态代理的支持, 动态代理的实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类用来获取动态代理对象,InvocationHandler接口对代理接口进行处理。

      Proxy类提供了newProxyInstance方法,该方法用于返回一个动态创建的代理类的实例,方法声明如下:

      public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h)

     参数loader表示代理类的类加载器,参数interfaces表示代理类所实现的接口列表,第三个参数h表示对应的调用处理程序类。

      InvocationHandler接口是代理处理程序接口,每一个代理类的实例都可以提供一个相关的具体调用处理程序。在该接口中声明了如下方法:

     public Object invoke(Objectproxy, Method method, Object[] args)

     当执行代理对象中的方法时,会调用对应的InvocationHandler中的invoke方法,参数proxy表示被代理的对象,参数method表示被代理的方法,最后一个参数args表示被代理方法的参数。

优点与不足

       那么使用动态代理有什么好处呢?

       首先,可以减少编码工作量,假如需要实现多种代理处理逻辑,无需每种方式都写一个代理类,只需写多个代理处理器。另外,通过动态代理,可以实现方法的增强,在不修改源码的情况下,在方法添加你想做的事。          

       虽然有上面提到的这些好处,但是动态代理却只能代理接口,不能代理类,也就是说只有实现了某个接口的类可以使用Java动态代理机制。

实例

     下面来看一个动态代理的例子。

     首先定义两个接口和对应于两个接口的实现类。

 

public interface IEat {
    void eat(String food);
}

public interface IDrive {
    void drive(String car);
}

public class EatImpl implements IEat{
    @Override
    public void eat(String food) {
        L.d("dproxy------" + "eat " + food);
    }
}

public class DriveImpl implements IDrive{
    @Override
    public void drive(String car) {
        L.d("dproxy------" + "drive " + car);
    }
}


     然后实现InvocationHandler接口,在它的invoke方法中通过反射调用传入的代理对象的方法,并且在方法执行前后添加想要执行的其他方法。这里我们只是在方法执行前后打印日志。

 

public class CustomInvocationHandler implements InvocationHandler {
    private Object object;

    public CustomInvocationHandler() {
    }

    public CustomInvocationHandler(Object ob) {
        object = ob;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeInvoke();
        Object result = method.invoke(object, args); 
        afterInvoke();
        return result;
    }

    public void beforeInvoke() {
        L.d("dproxy------" + "被代理代码开始执行");
    }

    public void afterInvoke() {
        L.d("dproxy------" + "被代理代码执行结束");
    }
}

     最后通过Proxy代理类生成一个代理对象,然后利用这个代理对象去调用被代理对象的方法。

 

        InvocationHandler handler = null;

        IEat eatImpl = new EatImpl();
        handler = new CustomInvocationHandler(eatImpl);
        IEat proxy = null;
        proxy = (IEat) Proxy.newProxyInstance(IEat.class.getClassLoader(), new Class[]{IEat.class}, handler);
        proxy.eat("apple");


        IDrive driveImpl = new DriveImpl();
        handler = new CustomInvocationHandler(driveImpl);
        IDrive proxy_new = null;
        proxy_new = (IDrive)Proxy.newProxyInstance(IDrive.class.getClassLoader(), new Class[]{IDrive.class}, handler);
        proxy_new.drive("train");

     通过打印信息可以看到,调用代理对象的方法,实际执行了被代理对象的方法,而且还在执行前后添加了增强代码。

 


D/viclee  ( 7309): dproxy------被代理代码开始执行
D/viclee  ( 7309): dproxy------eat apple
D/viclee  ( 7309): dproxy------被代理代码执行结束
D/viclee  ( 7309): dproxy------被代理代码开始执行
D/viclee  ( 7309): dproxy------drive train
D/viclee  ( 7309): dproxy------被代理代码执行结束

       欢迎关注我的公众号一起交流学习

     

 

### Java 动态代理的实现原理与用法 #### 1. Java 动态代理概述 Java 动态代理是一种在运行时动态生成代理对象的技术,它允许开发者无需提前定义具体的代理类即可完成方法拦截和增强功能。这种机制广泛应用于 AOP(面向切面编程)、事务管理以及日志记录等领域[^2]。 #### 2. 动态代理的核心组件 动态代理主要依赖于 `java.lang.reflect.Proxy` 类和 `InvocationHandler` 接口来实现。以下是其核心组成部分: - **Proxy 类**: 提供了用于创建动态代理实例的方法。 - **InvocationHandler 接口**: 定义了一个处理方法调用的回调接口,通过该接口可以自定义代理行为。 当客户端调用代理对象上的某个方法时,实际执行的是由 InvocationHandler 处理逻辑所指定的操作[^3]。 #### 3. JDK 原生动态代理实现 JDK 动态代理基于反射技术,在运行期间为一组接口动态生成代理类及其实例。具体流程如下: - 创建一个实现了 `InvocationHandler` 接口的对象。 - 使用 `Proxy.newProxyInstance()` 方法传入目标类加载器、目标类实现的一组接口列表以及上述 handler 对象,从而获得代理实例。 下面是一个简单的示例代码展示如何利用 JDK 动态代理实现基本的日志打印功能: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 定义服务接口 interface GreetingService { void sayHello(String name); } // 实现服务接口的具体业务逻辑 class SimpleGreetingServiceImpl implements GreetingService { @Override public void sayHello(String name) { System.out.println("Hello, " + name); } } // 自定义 InvocationHandler 来拦截并扩展方法调用 class LoggingInvocationHandler implements InvocationHandler { private Object target; // 被代理的目标对象 public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[Before] Executing: " + method.getName()); // 执行原始方法 Object result = method.invoke(target, args); System.out.println("[After] Finished execution."); return result; } } public class JdkDynamicProxyDemo { public static void main(String[] args) { // 初始化真实的服务实现 GreetingService greetingService = new SimpleGreetingServiceImpl(); // 构建带有日志功能的代理对象 GreetingService proxyInstance = (GreetingService) Proxy.newProxyInstance( greetingService.getClass().getClassLoader(), greetingService.getClass().getInterfaces(), new LoggingInvocationHandler(greetingService)); // 测试代理效果 proxyInstance.sayHello("World"); } } ``` 此程序展示了如何通过动态代理增加额外的功能而修改原有代码结构[^1]。 #### 4. CGLib 动态代理实现 除了 JDK 内置支持外,还可以借助第三方库如 CGLib 进行动态代理操作。CGLib 主要针对那些未继承任何接口或者希望直接操控实体类场景下更为适用。它的内部工作机理涉及字节码层面操作,能够子类化任意非 final 的普通类,并重写其中的方法达到相同目的。 需要注意的是,由于两者设计初衷同,在性能表现上可能会有所差异;通常情况下,如果仅需对接口做封装,则推荐优先考虑更轻量级也更加直观易懂的 JDK 方案。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值