谈谈Java中的代理

综合编程 2018-05-16 阅读原文

摘要: 原创出处 https://peijie-sh.gitgub.io 欢迎转载,保留摘要,谢谢!

代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,实现对相关方法增强。比如加入事务、日志、报警发邮件等操作。


静态代理

静态代理,就是由程序员手动编写代理类或者用工具生成代理类的代码,再进行编译生成class文件,实现代理。比如简单工厂模式。

用法:

  • 代理类和目标类都实现相同接口。
  • 代理类持有目标类的引用。

缺点: 静态代理要为每个目标类创建一个代理类,当需要代理的对象太多,那么代理类也变得很多。代理类违背了可重复代理只写一次的原则。

动态代理

为了解决静态代理的缺点,于是引入了动态代理。 它有一个好处,那就是不用写很多代理类,生成的代理类数量是固定的。 一般动态代理分为2种:

JDK动态代理

JDK动态代理是JDK自带的,不依赖第三方框架。 它的实现原理,就是利用Java的反射机制,创建一个实现接口的代理类。

用法:

  • 被代理对象必须实现接口。
  • 代理对象由代理工厂自动生成。

下面贴个例子

接口类:

public interface Subject {   
  public void doSomething();   
}

实现类:

public class RealSubject implements Subject {   
  public void doSomething() {   
    System.out.println("do 了 some thing ...");   
  }   
}

代理工厂:

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

public class ProxyHandler implements InvocationHandler {
    private Object target;

    //绑定委托对象,并返回代理类
    public Object bind(Object target) {
        this.target = target;
        //绑定该类实现的所有接口,取得代理类 
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
	
	@Override
    public Object invoke(Object proxy , Method method , Object[] args)throws Throwable {
        Object result = null;
        //这里就可以进行所谓的AOP编程了
        //在调用具体函数方法前,执行功能处理
        result = method.invoke(target, args);
        //在调用具体函数方法后,执行功能处理
        return result;
    }
}

测试类:

public class TestProxy {
    public static void main(String args[]) {
           ProxyHandler proxy = new ProxyHandler();
           //绑定该类实现的所有接口
           Subject sub = (Subject) proxy.bind(new RealSubject());
           sub.doSomething();
    }
}

CGLIB代理

使用CGLIB代理需要引入CGLIB库,它使用字节码技术实现代理。

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
      
public class CGLibProxy implements MethodInterceptor {    
    
    private Object targetObject;// CGLib需要代理的目标对象    
    
    public Object createProxyObject(Object obj) {    
        this.targetObject = obj;    
        Enhancer enhancer = new Enhancer();    
        enhancer.setSuperclass(obj.getClass());    
        enhancer.setCallback(this);    
        Object proxyObj = enhancer.create();    
        return proxyObj;// 返回代理对象    
    }    
    
    public Object intercept(Object proxy, Method method, Object[] args,    
            MethodProxy methodProxy) throws Throwable {    
        Object obj = null;    
        if ("addUser".equals(method.getName())) {// 过滤方法    
            checkPopedom();// 检查权限    
        }    
        obj = method.invoke(targetObject, args);    
        return obj;    
    }    
    
    private void checkPopedom() {    
        System.out.println("检查权限  checkPopedom()!");    
    }    
}
public class Test {
    
    public static void main(String[] args) {
    
		Subject sub = (Subject) new CGLibProxy().createProxyObject(new RealSubject());
		sub.doSomething();
}

2种动态代理的区别

JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 而CGLIB动态代理是利用asm开源包,加载代理对象类的class文件,修改其字节码生成子类来处理。

在 Spring 中,

  • 如果目标对象实现了接口,默认情况下会采用JDK动态代理实现AOP
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  • 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?

  • 添加CGLIB依赖
  • 在Spring配置文件中加入
  • 如果是SpringBoot,在配置文件设置 spring.aop.proxy-target-class=true

JDK动态代理和CGLIB字节码生成的区别?

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对未实现接口的类
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
  • 因为是继承,所以该类或方法最好不要声明成final
稀土掘金

责编内容by:稀土掘金阅读原文】。感谢您的支持!

您可能感兴趣的

SpringAOP-JDK 动态代理和 CGLIB 代理 在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类。 1.JDK 动态代理 那么接口(UserServiceBo)、目标对象(被代理对象 UserServiceImpl)、...
Opening the Oracle JDK At Java One in Oct 2017 Mark Cavage announced that Oracle will be open-sourcing the proprietary features of the Oracle JDK . This got a big round ...
macOS 中快速在多个不同 Jdk 版本间切换 一般,我们的机器上会同时安装多个版本的 Jdk ,默认的,macOS 会选择最高版本 Jdk 作为默认 Jdk,这样带来一些问题,比如说,有些中间件不支持最新的 Jdk 如 Jdk10,我们需要切换到低版本去,怎样最灵活的实现在不同版本 Jdk 之间快速切换呢,可以使用 shell 来搞定。 实...
Linux安装JDK详细步骤 本文主要介绍的是如何是Linux环境下安装JDK 一、安装环境 操作系统:阿里云centos7.3 JDK版本1.8 工具:Xshell5、Xftp5 二、安装步骤 第一步:下载安装包 下载Linux环境下的jdk1.8,请去( 官网 )中下载jdk的安装文件; ...
Oracle JDK builds vs. OpenJDK builds: Understandin... At last year’s Java One, Mark Cavage announced that Oracle will open source Oracle JDK’s proprietary features . Shortly before the conference, Donald...