jdk动态代理一些理解和使用

一. 基本概念

1. 什么是代理

代理模式是指为对象提供一个代理用来控制对对象的访问,代理分为静态代理和动态代理。

2. 静态代理

静态代理顾名思意是静态的,即由需要我们手动编写代理类的模式来控制对目标对象的访问。如下代码是一个静态代理的例子。

这是目标接口,即被代理的接口

package com.example.client.proxy.target;
import com.example.client.entity.User;
import java.util.List;

public interface UserService {

    /**
     * 用户查找方法
     * @param id
     * @return
     */
    List<User> findUser(String id);
}

这是目标的实现类

package com.example.client.proxy.target.impl;
import com.example.client.entity.User;
import com.example.client.proxy.target.UserService;
import java.util.ArrayList;
import java.util.List;

public class UserServiceImpl implements UserService {

    @Override
    public List<User> findUser(String id) {
        List<User> users=new ArrayList<>();
        User user=new User();
        user.setId(id);
        users.add(user);
        return users;
    }
}

这个是代理类

package com.example.client.proxy;
import com.example.client.entity.User;
import com.example.client.proxy.target.UserService;
import java.util.List;

public class UserProxy implements UserService {

    private UserService userService;

    public UserProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List<User> findUser(String id) {
        System.out.println("代理模式打印调用的出入参:"+id);
        List<User> users = userService.findUser(id);
        System.out.println("代理调用打印的出参:"+users.toString());
        return users;
    }
}

这个是关于静态代理的使用

    public static void staticProxy() {
        UserService userService = new UserServiceImpl();
        UserProxy proxy = new UserProxy(userService);
        proxy.findUser("01");
    }

静态代理的总结

从上边的例子可以看出,关于静态代理主要分为如下三个角色,(目标接口,目标实现,代理实现) 代理对象持有目标接口的引用,实现目标接口的方法,在目标接口调用对应的方法时,做一些逻辑,控制对目标的访问。

缺点:缺点也比较明显,一个方法对应代理里边的一个方法,一个类对应一个代理类代理需要手工编码,目标变动的时候,代理类也需要跟随变动(修改)。

3.动态代理

动态代理和静态代理相对,动态代理即在编译期确定了目标接口之后,对接口的方法做代理,代理类由jvm 生成,无需手工编写。这里我们只讨论jdk 的动态代理(只能代理接口

动态代理的角色和静态代理的角色一样,目标接口,目标实现(也可以没有),代理类(由jvm 生成,开发者只指定代理的接口,和代理的逻辑)。动态代理应用分两种方式应用:

1. 代理控制对目标实现的访问,例子(springAop),代理类只做代理

2.代理实现,即目标接口无实现,有代理类实现了目标接口,相当于目标接口的实现。应用示例:mybatis 动态代理DAO 的实现。

二. 动态代理的示例

以下代码为动态代理实现远程调用,以httpClient 为例,由代理类处理具体的通信细节,(包括通信过程中的使用哪些通信,http/rpc/socket, 通信过程的异常管理,熔断/异常处理....)业务人员只需编写远程调用的接口,无需实现。

如下是业务开发人员编写的远程调用接口

package spi;

import spi.httpClient.User;
import spi.proxy.annotation.SPI;
import java.util.List;

//业务开发人员编写的远程调用接口
public interface UserSPI {

    @SPI(value = "list")
    List<User> listUser();
}

如下是相关的代理实现使用FactoryBean 管控spring bean 的生成(一样的需要在spring 里边配置该bean) 

package spi.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class GeneralProxy implements FactoryBean {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private Class targetInterface;
    private InvocationHandler handler;

    public Object getObject() throws Exception {
        logger.info("代理实现bean...");
        return Proxy.newProxyInstance(targetInterface.getClassLoader(),new Class[]{targetInterface},handler);
    }

    public Class<?> getObjectType() {
        return targetInterface;
    }

    /**
     * todo 是当前GeneralProxy 单例 还是getObject 出来的是单例
     * @return
     */
    public boolean isSingleton() {
        return true;
    }

    public Class getTargetInterface() {
        return targetInterface;
    }

    public void setTargetInterface(Class targetInterface) {
        this.targetInterface = targetInterface;
    }

    public InvocationHandler getHandler() {
        return handler;
    }

    public void setHandler(InvocationHandler handler) {
        this.handler = handler;
    }
}

这个是代理的逻辑

package spi.proxy;

import com.google.gson.Gson;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spi.proxy.annotation.SPI;

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

public class MyInvocationHandler implements InvocationHandler {
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    private String host;
    public Gson GSON = new Gson();

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String url = getUrl(method);
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        CloseableHttpResponse response = httpClient.execute(httpGet);
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            String result = EntityUtils.toString(entity);
            LOGGER.info("result : " + result);
            Class<?> returnType = method.getReturnType();
            return GSON.fromJson(result, returnType);
        }
        return null;
    }

    private String getUrl(Method method) {
        SPI spi = method.getAnnotation(SPI.class);
        String value = spi.value();
        String url = host + value;
        return url;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }
}

配置

<!-- 测试远程调用代理-->
    <bean id="myInvocationHandler" class="spi.proxy.MyInvocationHandler">
        <property name="host" value="http://localhost:8800/"></property>
    </bean>

    <bean id="userSPI" class="spi.proxy.GeneralProxy">
        <property name="targetInterface" value="spi.UserSPI"></property>
        <property name="handler" ref="myInvocationHandler"></property>
    </bean>

以上代理将userSPI 组装到到spring 中,实现类为userSPI 的代理类。和我们普通的bean 无异了,解决的动态代理在运行期生成代理类导致的性能开销。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值