JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

Java动态代理的原生实现和Spring AOP的实现

wys521 2024-11-19 12:55:36 精选教程 21 ℃ 0 评论

Java中的动态代理

动态代理是动态地创建代理并动态地处理对所代理方法的调用。

实现动态代理需要实现InvocationHandler接口,实现其invoke(object, method, args[])函数,传递的是一个代理实例(Proxy类库的$Proxy0)、方法和参数。

Java动态代理的创建

动态代理对象是用静态方法Proxy.newProxyInstance()方法创建的:

  1. 第一个参数是希望代理的接口的类加载器,
  2. 第二个参数是希望该代理实现的接口列表(Class对象数组),
  3. 第三个参数是InvocationHandler接口的一个实现(Proxy.newProxyInstance()返回值可以强制转换成接口,不能强制转换成实现类,因此Java动态代理,只能代理接口,不能代理类【只能代理接口的意思是,创建代理后,只能通过接口的方式来调用代理的实现。】)。


动态代理代理的方法都会经过第三个参数的对象所实现的invoke方法来进行代理调用,因此通常会向调用处理器传递一个“实际对象”的引用(如下面的readObject对象),从而使得调用处理器可以将请求转发。如下图:

上图中:

  1. 代理类实例proxy的类名是:$Proxy0
  2. proxy中的属性有:m1, m0, m3, m4, m2
  3. proxy中的方法有:equals, toString, hashCode, doSomething, somethingElse
  4. proxy的父类是:class java.lang.reflect.Proxy
  5. proxy实现的接口是:opensource.Test$Interface

动态代理的作用

动态代理主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。还有一个有趣的作用是可以用作远程调用,比如现在有Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,其它比如hessian,各种webservice框架中的远程调用,大致都是这么实现的。见下面的例子:

package com.proxy.test;

public interface UserLogin {
    public void login(String userName);
}


package com.proxy.test;

public class UserLoginImpl implements UserLogin {
    public void login(String userName) {
        System.out.println("欢迎 " + userName + " 登录系统");
    }
}


package com.proxy.test;

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

public class MyHandler implements InvocationHandler {
    private Object obj;

    public MyHandler(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeLogin(); // 登录前处理,更具自己需要来写
        Object result = method.invoke(obj, args); // 调用真正的方法
        afterLogin(); // 登录后处理,更具自己需要来写
        return result;
    }

    public void beforeLogin() {
        System.out.println("登录前处理");
    }

    public void afterLogin() {
        System.out.println("登录后处理");
    }
}


package com.proxy.test;

import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        UserLoginImpl user = new UserLoginImpl(); // 得到实例对象
        MyHandler handler = new MyHandler(user); // 将对象传入自己的处理器中

        UserLogin proxy = (UserLogin) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass()
                .getInterfaces(), handler); // 得到代理对象

        proxy.login("张三"); // 代理调用login方法
    }
}


Spring AOP动态代理

Spring AOP通过代理模式实现,目前支持两种代理:

  1. JDK动态代理。
  2. CGLIB代理来创建AOP代理。

Spring建议优先使用JDK动态代理。

JDK动态代理

使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口,然后对接口创建AOP代理。

CGLIB代理

CGLIB代理不仅能进行接口代理,也能进行类代理。

CGLIB代理需要注意以下问题:不能代理final方法,因为final方法不能被覆盖(CGLIB通过生成子类来创建代理);会产生两次构造器调用,第一次是目标类的构造器调用,第二次是CGLIB生成的代理类的构造器调用(CGLIB要求被代理的类要有默认的构造函数,因为子类实例化时会隐式调用super());如果需要CGLIB代理方法,请确保两次构造器调用不影响应用。


Spring AOP默认首先使用JDK动态代理来代理目标对象,如果目标对象没有实现任何接口将使用CGLIB代理,如果需要强制使用CGLIB代理,请使用如下方式指定:

  1. 对于Schema风格配置切面使用如下方式来指定使用CGLIB代理:<aop:config proxy-target-class="true"></aop:config>
  2. 而如果使用@AspectJ风格使用如下方式来指定使用CGLIB代理:<aop:aspectj-autoproxy proxy-target-class="true"/>


原创文章多多支持!欢迎关注!


好书推荐!Java界公认的权威书籍!坚持读完,轻松拿下阿里巴巴offer!下方链接直接京东自营购买!


本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表