1 什么是代理
代理是一种常见的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为被代理类预处理消息,过滤消息并转发消息,以及进行消息被被代理类执行后的后续逻辑处理。简单结构示意图如下:
为了保持行为的一致性,代理类和被代理类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
2 Java动态代理
2.1 基于JDK的动态代理
2.1.1 步骤
- 创建需要被代理方法的接口
- 创建实现了被代理方法的接口的实现类
- 创建一个实现了InvocationHandler的类,并实现invoke方法,其中需要定义一个实现了真实需要调用的类
- 通过Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
- 通过代理调用对应的方法
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 步骤
- 定义一个需要被代理的类对象及方法
- 创建一个继承自 MethodInterceptor 的类,实现intercept方法或者创建一个继承自net.sf.cglib.proxy.InvocationHandler的类,并实现其invoke方法
- 创建一个RealSubject,并设置对应代理的类
- 设置单个代理处理类
- 获取动态代理对象
- 执行被代理方法
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中的代理则是完全不需要原执行逻辑,而是完全地控制了行为的执行过程。
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>