基于Java的动态代理的实现方式

1 什么是代理

代理是一种常见的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为被代理类预处理消息,过滤消息并转发消息,以及进行消息被被代理类执行后的后续逻辑处理。简单结构示意图如下:
动态代理结构示意图
为了保持行为的一致性,代理类和被代理类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

2 Java动态代理

2.1 基于JDK的动态代理

2.1.1 步骤

  1. 创建需要被代理方法的接口
  2. 创建实现了被代理方法的接口的实现类
  3. 创建一个实现了InvocationHandler的类,并实现invoke方法,其中需要定义一个实现了真实需要调用的类
  4. 通过Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
  5. 通过代理调用对应的方法

2.1.2 示例代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** 1. 创建需要被代理方法的接口*/
interface  Subject{
    void sayHello(String name);
}
/** 2. 创建实现了被代理方法的接口的实现类*/
class RealSubjectA implements Subject{
    @Override
    public void sayHello(String name) {
        System.out.println("RealSubjectA 被代理类方法执行 sayHello:"+name);
    }
}
/** 2. 创建实现了被代理方法的接口的实现类*/
class RealSubjectB implements Subject{
    @Override
    public void sayHello(String name) {
        System.out.println("RealSubjectB 被代理类方法执行 sayHello:"+name);
    }
}
/** 3. 创建一个实现了InvocationHandler的类,并实现invoke方法,其中需要定义一个实现了真实需要调用的类*/
class ProxySubject implements InvocationHandler{
    private Subject subject;
    public ProxySubject(Subject subject){
        this.subject = subject;
    }

	/**
	* proxy: 	当前代理实现类的对象
	* method:	需要执行的被代理类的方法
	* args: 	需要执行的被代理类的参数
	*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(String.format("代理执行参数:[method:%s,args:%s]",method.getName(),String.valueOf(args[0])));
        System.out.println("代理执行前");
        // 执行真实的被代理类的方法,此处传入的参数需注意
        Object invoke = method.invoke(subject, args);
        System.out.println("代理执行后");
        return invoke;
    }
}

public class JDKProxy {

    public static void main(String[] args) {
    	// 4. 通过Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
        Subject a = (Subject) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{Subject.class}, new ProxySubject(new RealSubjectA()));
        //5. 通过代理类执行对应的方法
        a.sayHello("RealSubjectA");
        System.out.println("==================================");
        Subject b = (Subject) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{Subject.class}, new ProxySubject(new RealSubjectB()));
        b.sayHello("RealSubjectB");
    }
}

执行结果如下:
在这里插入图片描述

2.2 基于Cglib 实现的动态代理

2.2.1 步骤

  1. 定义一个需要被代理的类对象及方法
  2. 创建一个继承自 MethodInterceptor 的类,实现intercept方法或者创建一个继承自net.sf.cglib.proxy.InvocationHandler的类,并实现其invoke方法
  3. 创建一个RealSubject,并设置对应代理的类
  4. 设置单个代理处理类
  5. 获取动态代理对象
  6. 执行被代理方法

2.2.2 示例代码

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/** 1. 定义一个需要被代理的类对象 */
class RealSubject{
    void sayHello(String name){
        System.out.println("sayHello:"+name);
    }
}

/** 2. 创建一个继承自 MethodInterceptor 的类,实现intercept方法 或者创建一个继承自net.sf.cglib.proxy.InvocationHandler的类,并实现其invoke方法*/
class CglibInterceptor implements MethodInterceptor{
    private Object target;
    public CglibInterceptor(Object target){
        this.target = target;
    }

    /**
     * @param o        代理后动态生成的代理类对象(CglibInterceptor)
     * @param method   代理的方法
     * @param objects  代理方法执行的参数
     * @param methodProxy 被代理的方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CglibInterceptor1 执行前操作");
        Object invoke = method.invoke(target, objects);
        System.out.println("CglibInterceptor1 执行后操作");
        return invoke;
    }
}

/** 2. 创建一个继承自 MethodInterceptor 的类,实现intercept方法 或者创建一个继承自net.sf.cglib.proxy.InvocationHandler的类,并实现其invoke方法*/
class CglibInvocationHandler implements InvocationHandler {
    private Object target;
    public CglibInvocationHandler(Object target){
        this.target = target;
    }

    /**
     *
     * @param o         代理类对象(CglibInvocationHandler)
     * @param method    被代理类执行的方法
     * @param objects   被代理类执行的参数
     */
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("CglibInterceptor1 执行前操作");
        Object invoke = method.invoke(target, objects);
        System.out.println("CglibInterceptor1 执行后操作");
        return invoke;
    }
}

public class CglibProxy {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        // 3. 创建一个RealSubject,并设置对应代理的类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        //4. 设置单个代理处理类
        enhancer.setCallback(new CglibInvocationHandler(realSubject));
        //5.获取动态代理对象
        RealSubject realSubjectProxy = (RealSubject) enhancer.create();
        //6. 执行被代理方法
        realSubjectProxy.sayHello("cglib proxy");
    }
}

2.3 JDK和CGLIB动态代理的区别

  • JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  • CGLIB代理使用字节码处理框架ASM,对代理对象类的class文件加载进来,通过修改字节码生成子类。
  • JDK创建代理对象效率较高,执行效率较低;CGLIB创建代理对象效率较低,执行效率高。
  • JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
  • CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。

3 动态代理的应用场景

3.1 SpringAop

3.1.1 底层实现(ProxyFactory)
  • SpringAop底层根据被代理类的类型进行动态判断
    如果是目标类是接口且设置了接口类型,则使用基于jdk的动态代理
    如果不存在接口,则使用基于Cglib的动态代理(Spring Aop 将cglib的相关代码拷贝到了自己的目录下)
//SpringAop获取动态代理方式的核心源码(DefaultAopProxyFactory)
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (!NativeDetector.inNativeImage() &&
			(config.isOptimize() 
			|| config.isProxyTargetClass() 
			//是否设置了interfaces属性
			|| hasNoUserSuppliedProxyInterfaces(config))) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		//原始对象传入的是接口类型(一般为false)
		if (targetClass.isInterface() 
		//原始基于Jdk的代理
		|| Proxy.isProxyClass(targetClass) 
		//是lambda表达式的类
		|| ClassUtils.isLambdaClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}
3.1.2 示例代码
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.reflect.Method;

interface SubjectInterface{
    void sayHello(String name);
}

// 含接口的动态代理
class RealSubjectInterface implements SubjectInterface{
    @Override
    public void sayHello(String name) {
        System.out.println("RealSubjectInterface 被代理类方法执行 sayHello:"+name);
    }
}
//不含接口的动态代理
class SubjectClass{
    public void sayHello(String name) {
        System.out.println("SubjectClass 被代理类方法执行 sayHello:"+name);
    }
}

class RoundInterceptor implements  MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("执行前");
        Object proceed = invocation.proceed();
        System.out.println("执行后");
        return proceed;
    }
}

class AfterReturningAdviceImpl implements AfterReturningAdvice{
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getName()+"执行完成");
    }
}


public class SpringProxy {
    public static void main(String[] args) {
        SpringProxy.testSubjectClass();
        System.out.println("=======================");
        SpringProxy.testSubjectInterface();
    }

    //测试不带接口的动态代理
    public static void testSubjectClass(){
        ProxyFactory proxyFactory = new ProxyFactory();
        //设置目标对象
        proxyFactory.setTarget(new SubjectClass());
        //设置代理执行类
        proxyFactory.addAdvice(new RoundInterceptor());
        proxyFactory.addAdvice(new AfterReturningAdviceImpl());
        SubjectClass subjectClass = (SubjectClass) proxyFactory.getProxy();
        subjectClass.sayHello("SubjectClass");
    }
    // 测试带接口的动态代理
    public static void testSubjectInterface(){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new RealSubjectInterface());
       	//设置了接口,则使用jdk动态代理,如注释掉此行,则使用cglib
        proxyFactory.setInterfaces(SubjectInterface.class);
        proxyFactory.addAdvice(new RoundInterceptor());
        proxyFactory.addAdvice(new AfterReturningAdviceImpl());
        SubjectInterface subjectClass = (SubjectInterface) proxyFactory.getProxy();
        subjectClass.sayHello("SubjectClass");
    }
}

执行结果如下:
执行结果

3.1.3 强制使用CGLIB实现AOP的方法
  • 添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
  • 在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
  • SpringBoot 设置proxyTargetClass为true :@EnableAspectJAutoProxy(proxyTargetClass=true)
3.1.4 基于SpringAop实现的其他场景
  • 事务(@Transaction)
  • 日志拦截(自定义切面实现拦截)
  • 权限校验(拦截请求,判断权限)

3.2 RPC框架

3.2.1 RPC

RPC是远程过程调用(Remote Procedure Call)的缩写形式。RPC中的代理则是完全不需要原执行逻辑,而是完全地控制了行为的执行过程
RPC实现之KW框架

3.2.2 代码示例
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.annotation.*;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

// 模拟spring cloud Fegin的简版实现
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface FeignClient {
    String url() default "";
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface RequestMapping {
    String value() default "";
}

// 原始地址 https://whois.pconline.com.cn/ipJson.jsp?ip=192.168.1.1&json=true
@FeignClient(url = "https://whois.pconline.com.cn")
interface Remote{
    @RequestMapping("/ipJson.jsp")
    String queryIpAddr(String ip,boolean json);
}

class RemoteProxy implements MethodInterceptor {
    private Class service;

    public RemoteProxy(Class service){
        this.service = service;
    }

    public <T>T getRemoteProxy(){
        //基于spring的动态代理
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(service);
        proxyFactory.addAdvice(this);
        proxyFactory.setInterfaces(Remote.class);
        T proxyClass = (T) proxyFactory.getProxy();
        return proxyClass;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    	// FeignClient 可继续细化,比如设置service-name,则通过service-name 找到对应的ip地址实现负载均衡
        FeignClient feignClient = (FeignClient) service.getAnnotation(FeignClient.class);
        //获取原始请求域名
        String url = feignClient.url();
        RequestMapping requestMapping = invocation.getMethod().getAnnotation(RequestMapping.class);
       	//获取请求路径
        String path = requestMapping.value();
        //组装请求参数
        Parameter[] parameters = invocation.getMethod().getParameters();
        StringBuffer par = new StringBuffer();
        for (int i = 0; i < parameters.length; i++) {
            par.append("&"+parameters[i].getName()+"="+String.valueOf(invocation.getArguments()[i]));
        }
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url+path+"?1=1"+par.toString());
        CloseableHttpResponse response = httpClient.execute(httpGet);
        return EntityUtils.toString(response.getEntity());
    }
}

public class RPCDemo {
    public static void main(String[] args) {
        Remote remote = new RemoteProxy(Remote.class).getRemoteProxy();
        String ipAddr = remote.queryIpAddr("118.147.122.14", true);
        System.out.println(ipAddr);
    }
}
3.2.3 Fegin动态代理核心源码
//org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
<T> T getTarget() {
        FeignContext context = this.beanFactory != null ? (FeignContext)this.beanFactory.getBean(FeignContext.class) : (FeignContext)this.applicationContext.getBean(FeignContext.class);
        Builder builder = this.feign(context);
        //设置了Url,优先使用Url
        if (!StringUtils.hasText(this.url)) {
            if (LOG.isInfoEnabled()) {
                LOG.info("For '" + this.name + "' URL not provided. Will try picking an instance via load-balancing.");
            }
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            } else {
                this.url = this.name;
            }
            this.url = this.url + this.cleanPath();
            //负载均衡实现
            return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
        } else {
            if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
                this.url = "http://" + this.url;
            }
            String url = this.url + this.cleanPath();
            Client client = (Client)this.getOptional(context, Client.class);
            if (client != null) {
                if (client instanceof FeignBlockingLoadBalancerClient) {
                    client = ((FeignBlockingLoadBalancerClient)client).getDelegate();
                }
                if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
                    client = ((RetryableFeignBlockingLoadBalancerClient)client).getDelegate();
                }
                builder.client(client);
            }
            this.applyBuildCustomizers(context, builder);
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
        }
    }

3.3 Mybatis MapperProxyFactory

3.3.1 源码分析
public class MapperProxyFactory<T> {
  //被代理的接口类型
  private final Class<T> mapperInterface;
  //被代理接口的方法
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
  //构造器,传入原始的Mapper接口定义
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
	
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }

  //获取代理实例,基于JDK动态代理
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  //public class MapperProxy<T> implements InvocationHandler, Serializable
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

MapperProxy 的invoke方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   try {
     // 如果调用方法的定义类是Object的方法,则直接进行调用
     if (Object.class.equals(method.getDeclaringClass())) {
       return method.invoke(this, args);
     } else {
       //获取缓存中的MapperMethodInvoker方法进行调用
       return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
     }
   } catch (Throwable t) {
     throw ExceptionUtil.unwrapThrowable(t);
   }
 }
 private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
   try {
     return MapUtil.computeIfAbsent(methodCache, method, m -> {
       // 接口中的默认方法处理逻辑
       if (m.isDefault()) {
         try {
           if (privateLookupInMethod == null) {
             return new DefaultMethodInvoker(getMethodHandleJava8(method));
           } else {
             return new DefaultMethodInvoker(getMethodHandleJava9(method));
           }
         } catch (IllegalAccessException | InstantiationException | InvocationTargetException
             | NoSuchMethodException e) {
           throw new RuntimeException(e);
         }
       } else {
         //非默认方法处理逻辑
         //PlainMethodInvoker实现了MapperMethodInvoker接口,其invoke方法,最终调用的是MapperMethod的execute方法
         return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
       }
     });
   } catch (RuntimeException re) {
     Throwable cause = re.getCause();
     throw cause == null ? re : cause;
   }
 }

mybatis,底层进行具体sql执行的分发逻辑
org.apache.ibatis.binding.MapperMethod#execute

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

4 附录

4.1 maven依赖

<!-- cglib 依赖-->
<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.3.0</version>
</dependency>
<!-- spring-aop 依赖-->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>5.3.22</version>
 </dependency>
 <!-- apache httpclient-->
 <dependency>
   	<groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
 </dependency>

<build>
  <plugins>
  		<!-- maven编译器-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <!-- 如果不设置parameters,将获取不了方法的原始参数-->
                <parameters>true</parameters>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值