一. 基本概念
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 无异了,解决的动态代理在运行期生成代理类导致的性能开销。