JDK动态代理

Java动态代理

说到Java中的动态代理,我们则需要回顾一种常用的设计模式 – 代理模式,而对于代理,则又可以根据创建代理类的时间点,分为静态代理和动态代理。

一、代理模式

在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 或者去火车票代售点中介去买。

1、代理模式的主要优点:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

2、代理模式的主要缺点:

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

代理模式的结构

1、代理模式的主要角色如下。

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能

动态代理

代理模式的实现

1、抽象主题

interface Subject
{
    void Request();
}

2、真实主题

class RealSubject implements Subject
{
    public void Request()
    {
        System.out.println("访问真实主题方法...");
    }
}

3、代理

class Proxy implements Subject
{
    private RealSubject realSubject;
    public void Request()
    {
        if (realSubject==null)
        {
            realSubject=new RealSubject();
        }
        preRequest();
        realSubject.Request();
        postRequest();
    }
    public void preRequest()
    {
        System.out.println("访问真实主题之前的预处理。");
    }
    public void postRequest()
    {
        System.out.println("访问真实主题之后的后续处理。");
    }
}

4、主方法

package proxy;
public class ProxyTest
{
    public static void main(String[] args)
    {
        Proxy proxy=new Proxy();
        proxy.Request();
    }
}

5、程序运行的结果:

访问真实主题之前的预处理。
访问真实主题方法…
访问真实主题之后的后续处理。

二、静态代理

静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

三、动态代理

动态代理:代理类在程序运行时创建的代理方式被称为动态代理。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

1、动态代理简单实现

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。

2、创建一个动态代理对象步骤

1、创建一个InvocationHandler对象

InvocationHandler stuHandler = new MyInvocationHandler(stu);

2、使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass

Class stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class[] {Person.class});

3、获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor

Constructor constructor = PersonProxy.getConstructor(InvocationHandler.class);

4、通过构造器constructor来创建一个动态实例stuProxy

Person stuProxy = (Person) cons.newInstance(stuHandler);

5、到此,一个动态代理对象就创建完毕。然而上面四个步骤可以通过Proxy类的newProxyInstances方法来简化:

 //创建一个与代理对象相关联的InvocationHandler
 InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
 //创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
 Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

四、JDK动态代理

  • 基于JDK的动态代理两个主要类:1.InvocationHandler(接口)、2.Proxy(类)
  • JDK是基于接口的动态代理
  • java内部的反射机制来实现的

1、实际应用实例

1.第一步,创建一个接口

public interface Subject {
    void hello(String param);
}

2.第二步,实现接口

public class SubjectImpl implements Subject {
    @Override
    public void hello(String param) {
        System.out.println("hello  " + param);
    }
}

3.第三步,创建SubjectImpl的代理类

public class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("------------begin------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("------------end------------");
        return invoke;
    }
}

invoke方法的说明:


kang

4.编写代理类实际的调用,利用Proxy类创建代理之后的Subject类。

public class Main {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
        proxyInstance.hello("world");
    }

}

kang

5、程序输出:

————begin————
hello world
————end————

总结:==看这个结果,实际上在Subject类中只会输出一条hello world,但是在被代理之后,实际调用的方法是SubjectProxy的invoke方法,这样可以在不修改业务类的情况下对业务类增加一些日志等其他操作,甚至可以直接修改有返回值方法的返回值。==

五、CGLIB动态代理

  • 无需借助于接口实现,java普通类就可以被带代理,使用频次更广泛。
  • cglib动态代理底层则是借助asm来实现的(反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题))

1、导入Cglib的jar包

<dependencies>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>
</dependencies>

1.定义被代理的方法:

public class CGsubject {
    public void sayHello(){
        System.out.println("hello world");
    }
}

2.代理类

public class HelloInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin time -----> "+ System.currentTimeMillis());
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("end time -----> "+ System.currentTimeMillis());
        return o1;
    }
}

kang

3.运行类

public class Main {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CGsubject.class);
        enhancer.setCallback(new HelloInterceptor());
        CGsubject cGsubject = (CGsubject) enhancer.create();
        cGsubject.sayHello();
    }
}

4.运行结果

==begin time —–> 1534836443741
hello world
end time —–> 1534836443786==


   转载规则


《JDK动态代理》 kang 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录