前言
Java代理分为静态代理与动态代理(jdk、cglib、javaassit等),它是整个java技术中最为重要的技术之一。其中,动态代理更是在很多java框架中得到广泛地使用,比如spring aop,mybatis,hibernate等等。
使用场景
代理模式是常用的java设计模式之一,它的理论依据就是java代理技术。
代理模式使用的场景很多,常见的有以下几种:
- 因安全原因屏蔽客户端直接访问真实对象;
- 在远程调用中使用代理类处理远程方法调用的技术细节(RMI);
- 为提升性能,对真实对象进行封装,从而达到延迟加载的目的;
- 为关键的业务类记录方法执行日志;
- 为频繁访问的数据增加缓存功能;
- 为系统中分散在各处的业务添加通用的逻辑。
代理的实现方式
静态代理
代理类和目标类需要实现相同的接口,对静态代理类方法的调用都是通过委托给目标对象实现的。
缺点:对不同的目标类,需要定义不同的静态代理类,会造成大量的样板式重复代码;目标接口新增方法,目标类与代理类都需要修改,增加了代码维护的复杂度。
基于上述原因,通常都采用动态代理进行解决。
/**
* 静态代理
* @author wxyh
* @date 2018/02/28
*/
public class StaticProxy implements HelloService {
/**
* 目标对象
*/
private final HelloService target;
public StaticProxy(HelloService target) {
super();
this.target = target;
}
@Override
public void sayHello(String name) {
System.out.println("Before execute StaticProxy.sayHello...");
if (new Random().nextBoolean()) {
// 控制对目标对象的访问
target.sayHello(name);
System.out.println("Say hello successfully!");
} else {
System.out.println(name + " absence. Say hello failed!");
}
System.out.println("After execute StaticProxy.sayHello...");
}
}
JDK动态代理
JDK自带的动态代理,不需要添加其他依赖,代理类和目标类实现同一个接口。
缺点:代理类继承自Proxy类,所以只能代理实现了接口的类;代理类通过反射机制调用目标对象方法,性能有一定损耗。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理工厂
* @author wxyh
* @date 2018/02/28
*/
public class JdkProxyFactory {
/**
* 获取代理对象
* @param target 目标对象
* @return 代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T getProxyObject(Object target) {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new JdkDynamicProxy(target));
}
/**
* JDK动态代理<br>
* @author wxyh
* @date 2018/02/28
*/
public static class JdkDynamicProxy implements InvocationHandler {
/**
* 目标对象
*/
private final Object target;
public JdkDynamicProxy(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String fullMethodName = method.getDeclaringClass().getName() + "." + method.getName();
// 前置逻辑
System.out.println("JdkDynamicProxy::Before execute " + fullMethodName);
System.out.println("Jdk Proxy Object::" + proxy.getClass().getName());
// 反射调用目标对象方法
Object result = method.invoke(this.target, args);
// 后置逻辑
System.out.println("JdkDynamicProxy::After execute " + fullMethodName);
return result;
}
}
}
CGLIB动态代理
代理类对被代理类是否实现接口没有强制要求,适用范围更广,方法调用性能好。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* CGLIB动态代理工厂
* @author wxyh
* @date 2018/02/28
*/
public class CglibProxyFactory {
/**
* 获取代理对象
* @param target 目标对象
* @return 代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T getProxyObject(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibDynamicProxy(target));
return (T) enhancer.create();
}
/**
* CGLIB动态代理<br>
* 动态生成的代理类为目标类的子类,在代理类中植入增强
* @author wxyh
* @date 2018/02/28
*/
public static class CglibDynamicProxy implements MethodInterceptor {
/**
* 目标对象
*/
@SuppressWarnings("unused")
private final Object target;
public CglibDynamicProxy(Object target) {
super();
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String fullMethodName = method.getDeclaringClass().getName() + "." + method.getName();
// 前置逻辑
System.out.println("CglibDynamicProxy::Before execute " + fullMethodName);
System.out.println("Cglib Proxy Object::" + obj.getClass().getName());
// 调用代理对象方法
Object result = proxy.invokeSuper(obj, args);
// 后置逻辑
System.out.println("CglibDynamicProxy::After execute " + fullMethodName);
return result;
}
}
}
HelloService接口及其实现类
/**
* @author wxyh
* @date 2018/02/28
*/
public interface HelloService {
void sayHello(String name);
}
/**
* @author wxyh
* @date 2018/02/28
*/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println(String.format("Hello %s!", StringUtils.defaultIfBlank(name, "World")));
}
}
/**
* @author wxyh
* @date 2018/02/28
*/
public class HelloServiceImpl2 {
public void sayHello(String name) {
System.out.println(String.format("Hello %s!", StringUtils.defaultIfBlank(name, "World")));
}
}
测试代码
/**
* @author wxyh
* @date 2018/02/28
*/
public class ProxyTest {
/**
* 测试静态代理
*/
@Test
public void testStaticProxy() {
HelloService helloServcie = new StaticProxy(new HelloServiceImpl());
helloServcie.sayHello("xyz");
}
/**
* 测试JDK动态代理
*/
@Test
public void testJdkProxy() {
long start = System.currentTimeMillis();
// 目标对象实现HelloService接口
HelloService helloServcie = JdkProxyFactory.getProxyObject(new HelloServiceImpl());
System.out.println("Create jdkProxy took " + (System.currentTimeMillis() - start) + "ms.");
helloServcie.sayHello("xyz");
}
/**
* 测试CGLIB动态代理
*/
@Test
public void testCglibProxy() {
long start = System.currentTimeMillis();
// 目标对象实现HelloService接口
HelloService helloServcie = CglibProxyFactory.getProxyObject(new HelloServiceImpl());
System.out.println("Create cglibProxy took " + (System.currentTimeMillis() - start) + "ms.");
helloServcie.sayHello("xyz");
System.out.println();
// 目标对象没有实现接口
HelloServiceImpl2 helloServcie2 = CglibProxyFactory.getProxyObject(new HelloServiceImpl2());
helloServcie2.sayHello("xyz");
}
}
测试结果
Create jdkProxy took 1ms.
JdkDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloService.sayHello
Jdk Proxy Object::com.sun.proxy.$Proxy4
Hello xyz!
JdkDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloService.sayHello
Before execute StaticProxy.sayHello...
xyz absence. Say hello failed!
After execute StaticProxy.sayHello...
Create cglibProxy took 77ms.
CglibDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloServiceImpl.sayHello
Cglib Proxy Object::com.wxyh.springbootproject.common.service.HelloServiceImpl$$EnhancerByCGLIB$$3b897022
Hello xyz!
CglibDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloServiceImpl.sayHello
CglibDynamicProxy::Before execute com.wxyh.springbootproject.common.service.HelloServiceImpl2.sayHello
Cglib Proxy Object::com.wxyh.springbootproject.common.service.HelloServiceImpl2$$EnhancerByCGLIB$$967091cc
Hello xyz!
CglibDynamicProxy::After execute com.wxyh.springbootproject.common.service.HelloServiceImpl2.sayHello
从测试结果可知,JDK动态代理创建过程快,这是因为创建代理对象时,调用了Proxy$ProxyClassFactory.apply方法,该方法通过调用本地方法defineClass0创建的代理对象,故其性能要高于其他实现。但JDK动态代理类的函数调用是通过反射调用目标方法的,其性能不如CGLIB动态代理。