Java--JDK动态代理(AOP)

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java--JDK动态代理(AOP)相关的知识,希望对你有一定的参考价值。

一、代理模式

百度百科的定义

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

Java中代理又分为静态代理和动态代理

二、静态代理

1、静态代理类是由程序员自己编写的,静态代理模式中的所有角色(代理对象、目标对象、目标对象的接口)等都是在编译期就确定好

如下案例

目标类和代理类都需要实现的接口类

/*
 目标类 和 代理类都实现了该接口
 */
public interface HelloService 
    public String say(String str);

目标类

/*
 目标对象
 */
public class TargetHello implements HelloService 
    @Override
    public String say(String str) 
        String string = "Hello," + str;
        System.out.println("目标类:" + string);
        return string;
    

 代理类

/*
 代理对象
 */
public class ProxyHello implements HelloService 
    private HelloService target;

    public ProxyHello(HelloService target)
        this.target = target;
    

    @Override
    public String say(String str) 
        String string = "代理类:"+ target.say(str);
        System.out.println(string);
        return string;
    

测试代码

public class TestStaticProxy 
    public static void main(String[] args) 
        //目标对象
        HelloService helloService = new TargetHello();
        //代理对象
        ProxyHello proxy = new ProxyHello(helloService);
        proxy.say("调用者");
    

 2、静态代理的用途

(1)控制真实对象的访问权限:通过代理对象控制真实对象的使用权限

(2)避免创建大对象:通过使用一个代理小对象来代表一个真实的大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度

(3)增强真实对象的功能:通过代理可以在调用真实对象的方法的前后增加额外功能

3、静态代理缺陷:

(1)当需要代理多个类时,代理对象要实现与目标对象一致的接口

        要么,只维护一个代理类来实现多个接口,但这样会导致代理类过于庞大

        要么,新建多个代理类,但这样会产生过多的代理类(当目标类增加了,代理类可能也需要成倍的增加;代理类数量过多)

(2)当接口需要增加、删除、修改方法时,目标对象与代理类都要同时修改(接口的实现类必须要实现接口中所有方法),不易维护

三、动态代理

为了解决静态代理的不足,于是便有了动态代理

动态代理是指代理类在程序运行时进行创建的代理方式。这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据Java代码中的“指示”动态生成的。(动态代理中的代理类并不要求在编译期就确定,而是可以在运行期动态生成,从而实现对目标对象的代理功能)

相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数

动态代理又分为两种方式

(1)JDK动态代理:

使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy

(2)Cglib动态代理:

一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。 通过继承目标类,在子类中重写父类同名方法,实现功能修改(重写的方法不能是final)

本文主要讲解JDK动态代理

JDK原生动态代理

JDK原生动态代理主要是使用Java中的反射机制,使用了反射包 java.lang.reflect下的三个类

InvocationHandler , Method, Proxy

1、InvocationHandler

InvocationHandler接口定义了如下方法:

/**
 InvocationHandler接口
 调用处理程序
    Object proxy:jdk创建的代理对象,无需赋值
    Method method:目标类中的方法,jdk提供的method对象
    Object[] args:目标类中方法的参数,jdk提供的
 */
public interface InvocationHandler  
    Object invoke(Object proxy, Method method, Object[] args); 

Object proxy:jdk创建的代理对象,无需赋值

Method method:目标类中的方法,jdk提供的method对象

Object[] args:目标类中方法的参数,jdk提供的 

 实现了该接口的中介类用做“调用处理器”。当调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了具体调用的是代理类的哪个方法,args为该方法的参数。这样对代理类中的所有方法的调用都会变为对invoke的调用,可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

Proxy类用于获取指定代理对象所关联的调用处理器

2、Method

目标类中的方法Method.invoke();

该invoke与上面的invoke不同,上方的invoke是接口方法 具体函数调用method.invoke(目标对象,方法的参数

3、Proxy

/*
    核心的对象,创建代理对象
    静态方法 newProxyInstance()
        ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader。比如类a,a.getCalss().getClassLoader(), 目标对象的类加载器
        Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的
        InvocationHandler h : 自己实现的,代理类要完成的功能
        返回值是代理类对象Object,得到一个目标对象
 */
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    
核心的对象,创建代理对象
静态方法 newProxyInstance()
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
    ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader。比如类a,a.getCalss().getClassLoader(), 目标对象的类加载器
    Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的
    InvocationHandler h : 自己实现的,代理类要完成的功能
    返回值是代理类对象Object,得到一个目标对象

4、JDK动态代理步骤

(1)创建接口,定义目标类要完成的功能

(2)创建目标类实现接口

(3)创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能(调用目标方法,增强功能)

(4)使用Proxy类的静态方法,创建代理对象。 并把返回值转为接口类型

创建目标对象 接口类 接口对象 =new 接口实现类();

创建InvocationHandler对象InvocationHandler handler = new MySellHandler(接口类对象);

创建代理对象接口类 proxy = (接口类) Proxy.newProxyInstance(xx.getClass().getClassLoader(), xx.getClass().getInterfaces(),handler);

通过代理执行方法proxy.方法();

目标类和代理类需要实现接口

/*
 目标类 和 代理类都实现了该接口
 */
public interface HelloService 
    public String say(String str);

 目标类

/*
 目标对象
 */
public class TargetHello implements HelloService 
    @Override
    public String say(String str) 
        String string = "Hello," + str;
        System.out.println("目标类:" + string);
        return string;
    

InvocationHandler实现类

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 
        Object res = method.invoke(target,args);
        String string = "代理类:"+ res;
        System.out.println(string);
        return string;
    

 测试代码

public class TestDynamicProxy 
    public static void main(String[] args) 
        HelloService helloService = new TargetHello();
        MyInvocationHandler handler = new MyInvocationHandler(helloService);
        HelloService proxy = (HelloService) Proxy.newProxyInstance(helloService.getClass().getClassLoader(),
                helloService.getClass().getInterfaces(),
                handler);
        proxy.say("调用者");
    

以上是关于Java--JDK动态代理(AOP)的主要内容,如果未能解决你的问题,请参考以下文章

转载Java JDK 动态代理(AOP)使用及实现原理分析

Java--Spring之AOP面向切面编程

spring-AOP动态代理

spring框架学习6:spring-aop的五种通知类型

AOP

动态代理实现aop