定义
- 为其他对象提供一种代理,以控制对这个对象的访问。
- 代理对象在客户端和目标对象之间起到中介的作用。
优点
- 将代理对象与真实被调用的目标对象分离。
- 一定程度上降低了系统的耦合度,扩展性好。
- 保护了目标对象。
- 增强目标对象。
缺点
- 增加系统类的数目。
- 增加代理对象,会一定程度上影响处理速度。
- 增加系统复杂度。
主要实现
- 动态代理
- JDK代理(接口): JAVA提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例。
- 主要涉及到
java.lang.reflect
中的两个类:Proxy
和InvocationHandler
其中InvocationHandler
是一个接口,可以通过实现该接口定义逻辑,并通过反射机制调用该目标类的代码,动态的将逻辑和业务编制在一起。
- 主要涉及到
- CGLIB代理(继承,ASM字节码):使用底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。(注意
final
关键字的继承关系)
- JDK代理(接口): JAVA提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例。
- 静态代理
JDK代理比CGLIB代理大约快20%左右。
代码
我们就简单的用两种代理实现一个方法前后调用的拦截。
JDK代理
在使用JDK动态代理的时候,最主要的就是这个方法
java.lang.reflect.Proxy#newProxyInstance
:
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
-
ClassLoader loader,
:指定当前目标对象使用类加载器,获取加载器的方法是固定的 -
Class>[] interfaces,
:目标对象实现的接口的类型,使用泛型方式确认类型 -
InvocationHandler invocationHandler
:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
目标接口类(HelloWorld
)
我们都知道JDK代理核心是创建接口的代理实例,因此必须有一个代理目标的接口。
/**
* 目标接口类
*/
public interface HelloWorld {
void sayHello();
}
简单的sayHello
方法。
目标类实现HelloWorldImpl
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHello() {
System.out.println("Hello World~");
}
}
这里就没什么可说的 ,实现接口并实现方法。
创建代理类
public class HelloWorldProxy implements InvocationHandler {
//代理的目标类
private Object target;
public HelloWorldProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(proxy.getClass().getName(),method.getName());
method.invoke(target,args);
afterMethod(proxy.getClass().getName(),method.getName());
return null;
}
/**
* 调用前执行的方法
* @param clazz
* @param methodName
*/
private void beforeMethod(String clazz,String methodName) {
System.out.println("在 类: "+ clazz +"的方法:"+methodName+"前执行");
}
/**
* 调用后执行的方法
* @param clazz
* @param methodName
*/
private void afterMethod(String clazz,String methodName) {
System.out.println("在 类: "+ clazz +"的方法:"+methodName+"后执行");
}
}
JDK代理需要实现InvocationHandler
接口,同时重写invoke
方法,我们在该代理类中,增加了 方法调用前以及调用后的逻辑实现,简单的打印输出。
测试类JdkProxyTest
public class JdkProxyTest {
public static void main(String[] args) {
//目标类声明
HelloWorld helloWorld = new HelloWorldImpl();
//代理类声明
HelloWorldProxy helloWorldProxy = new HelloWorldProxy(helloWorld);
//通过接口实现代理目标类的实例
HelloWorld proxyHelloWorld = (HelloWorld) Proxy.newProxyInstance(
helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(),
helloWorldProxy
);
//调用方法
proxyHelloWorld.sayHello();
}
}
UML
我们可以看到使用动态代理,代理类与目标类对象并没有直接的关联,这就体现了动态代理低耦合灵活的特征。
测试结果
CGLIB代理
首先使用CGLIB需要引入相关的包,这里我使用的MAVEN工程,直接引入依赖即可。
cglib
cglib
2.2.2
目标类对象(CGlibHelloWorld
)
public class CGlibHelloWorld {
public void sayHello() {
System.out.println("Hello World CGLIB ");
}
}
简单的sayHello
方法。并且没有实现任何接口。
代理对象(CGlibHelloWorldPoxy
)
首先我们需要先了解一个基础的类:Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
具体细节可以参考一下 Cglib之Enhancer创建动态代理
public class CGlibHelloWorldPoxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeMethod(o.getClass().getSuperclass().getName(),method.getName());
methodProxy.invokeSuper(o,objects);
afterMethod(o.getClass().getSuperclass().getName(),method.getName());
return null;
}
/**
* 调用前执行的方法
* @param clazz
* @param methodName
*/
private void beforeMethod(String clazz,String methodName) {
System.out.println("在 类: "+ clazz +"的方法:"+methodName+"前执行");
}
/**
* 调用后执行的方法
* @param clazz
* @param methodName
*/
private void afterMethod(String clazz,String methodName) {
System.out.println("在 类: "+ clazz +"的方法:"+methodName+"后执行");
}
}
这里调用方法(intercept
)与JDK代理有明显的不同。我们使用的是父类的调用。
UML
这里由于就简单的类,并且没有任何关联,因此就不上图了。
测试类
public class CGlibTest {
public static void main(String[] args) {
CGlibHelloWorldPoxy poxy = new CGlibHelloWorldPoxy();
CGlibHelloWorld helloWorld = (CGlibHelloWorld) poxy.getProxy(CGlibHelloWorld.class);
helloWorld.sayHello();
}
}
测试结果
其他开源
Spring
org.springframework.aop.framework.ProxyFactory
/**
* Factory for AOP proxies for programmatic use, rather than via declarative
* setup in a bean factory. This class provides a simple way of obtaining
* and configuring AOP proxy instances in custom user code.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 14.03.2003
*/
@SuppressWarnings("serial")
public class ProxyFactory extends ProxyCreatorSupport {
/**
* Create a new ProxyFactory.
*/
public ProxyFactory() {
}
/**
* Create a new ProxyFactory.
* Will proxy all interfaces that the given target implements.
* @param target the target object to be proxied
*/
public ProxyFactory(Object target) {
setTarget(target);
setInterfaces(ClassUtils.getAllInterfaces(target));
}
/**
* Create a new ProxyFactory.
*
No target, only interfaces. Must add interceptors.
* @param proxyInterfaces the interfaces that the proxy should implement
*/
public ProxyFactory(Class>... proxyInterfaces) {
setInterfaces(proxyInterfaces);
}
/**
* Create a new ProxyFactory for the given interface and interceptor.
*
Convenience method for creating a proxy for a single interceptor,
* assuming that the interceptor handles all calls itself rather than
* delegating to a target, like in the case of remoting proxies.
* @param proxyInterface the interface that the proxy should implement
* @param interceptor the interceptor that the proxy should invoke
*/
public ProxyFactory(Class> proxyInterface, Interceptor interceptor) {
addInterface(proxyInterface);
addAdvice(interceptor);
}
/**
* Create a ProxyFactory for the specified {@code TargetSource},
* making the proxy implement the specified interface.
* @param proxyInterface the interface that the proxy should implement
* @param targetSource the TargetSource that the proxy should invoke
*/
public ProxyFactory(Class> proxyInterface, TargetSource targetSource) {
addInterface(proxyInterface);
setTargetSource(targetSource);
}
/**
* Create a new proxy according to the settings in this factory.
*
Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
*
Uses a default class loader: Usually, the thread context class loader
* (if necessary for proxy creation).
* @return the proxy object
*/
public Object getProxy() {
return createAopProxy().getProxy();
}
/**
* Create a new proxy according to the settings in this factory.
*
Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
*
Uses the given class loader (if necessary for proxy creation).
* @param classLoader the class loader to create the proxy with
* (or {@code null} for the low-level proxy facility's default)
* @return the proxy object
*/
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
/**
* Create a new proxy for the given interface and interceptor.
*
Convenience method for creating a proxy for a single interceptor,
* assuming that the interceptor handles all calls itself rather than
* delegating to a target, like in the case of remoting proxies.
* @param proxyInterface the interface that the proxy should implement
* @param interceptor the interceptor that the proxy should invoke
* @return the proxy object
* @see #ProxyFactory(Class, org.aopalliance.intercept.Interceptor)
*/
@SuppressWarnings("unchecked")
public static T getProxy(Class proxyInterface, Interceptor interceptor) {
return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
}
/**
* Create a proxy for the specified {@code TargetSource},
* implementing the specified interface.
* @param proxyInterface the interface that the proxy should implement
* @param targetSource the TargetSource that the proxy should invoke
* @return the proxy object
* @see #ProxyFactory(Class, org.springframework.aop.TargetSource)
*/
@SuppressWarnings("unchecked")
public static T getProxy(Class proxyInterface, TargetSource targetSource) {
return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();
}
/**
* Create a proxy for the specified {@code TargetSource} that extends
* the target class of the {@code TargetSource}.
* @param targetSource the TargetSource that the proxy should invoke
* @return the proxy object
*/
public static Object getProxy(TargetSource targetSource) {
if (targetSource.getTargetClass() == null) {
throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(targetSource);
proxyFactory.setProxyTargetClass(true);
return proxyFactory.getProxy();
}
}
这个代理工厂,实现了 JDK代理以及CGLIB的代理实现的工厂。
通过这个工厂类,来选择使用某个代理技术来创建代理对象。
小结
代理模式最突出的就是AOP的应用,如果感兴趣,可以深入的阅读以下spring的core包下的源码。