代理模式
学习目标
- 掌握代理模式的应用场景和实现原理。
- 应用场景
- 保护目标对象:客户并不知道对象是如何实现具体业务功能的,只要调用代理对象的接口就行了。
- 增强目标对象:在实现目标对象的业务功能之前或者之后做一些业务实现。
Spring-AOP
- 实现原理
- 静态代理:代理对象持有被代理对象的引用。由被代理对象实现目标业务。
- 动态代理:代理对象持有被代理对象的引用,代码运行产生一个新的
java
类,并被编译重新加载,然后在新的类中执行目标业务实现动态代理。
- 应用场景
- 了解静态代理和动态代理的区别。
- 静态代理:被代理对象在代码中被写死了,不易扩展,适用于简单的业务,代码复用性不高,违背开闭原则。
- 动态代理:只要是实现了同一个接口的被代理类,都可以代理实现,实现了业务扩展。代码复用,遵循开闭原则。
- 了解
CGLib和JDK-proxy
的区别。JDK-proxy
动态代理:实现了被代理对象的接口,生成新的class字节码,利用反射调用实现代理。无法代理不实现代理业务接口的方法。CGLib
动态代理:继承被代理对象,生成新的class字节码,通过fastclass
机制调用实现代理。无法代理被final关键字修饰的方法
代理模式的定义
为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和服务端起到中介的作用。是一种结构型设计模式。
静态代理
/**
* @description: 被代理对象需要实现的目标接口
* @author: lmc
* @create: 2019-06-12 15:38
**/
public interface ITarget {
/**
* @description: 被代理对象的行为
* @return void
* @date 2019/6/12 15:39
* @author lmc
*/
void behavior();
}
/**
* @description: 真实的被代理的目标对象
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl implements ITarget {
public void behavior() {
System.out.println("执行真实的被代理对象的行为。");
}
}
下面的类也是一个被代理的目标对象,但是没有实现ITarget
接口
/**
* @description: 真实的被代理的目标对象
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class Target{
public void behavior() {
System.out.println("执行被代理对象target的行为。");
}
}
/**
* @description: 静态代理类
* @author: lmc
* @create: 2019-06-12 15:45
**/
public class StaticProxy {
/**
* 持有被代理对象的引用
*/
private ITarget targetImpl;
/**
* 持有被代理对象的引用
*/
private Target target;//一个没有实现接口的类
/**
* 构造方法初始化值
* @param targetImpl
*/
public StaticProxy(ITarget targetImpl,Target target){
this.targetImpl=targetImpl;
this.target=target;
}
/**
* @description: 被代理之前的增强行为
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("执行代理之前需要做的一些事情。");
}
/**
* @description: 被代理之后的增强行为
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("执行代理之后需要做的一些事情。");
}
/**
* @description: 开始执行代理
* @date 2019/6/12 15:59
* @author lmc
*/
public void startProxy(){
beforeBehavior();
targetImpl.behavior();
target.behavior();
afterBehavior();
}
}
/**
* @description: 静态代理客户端 懒汉式单例
* @author: lmc
* @create: 2019-06-12 16:01
**/
public class StaticProxyClient implements Serializable {
private final static StaticProxyClient staticProxyClient=new StaticProxyClient();
private StaticProxyClient(){
if(null != staticProxyClient){
throw new RuntimeException("单例类,不允许被反射实例化");
}
};
public static StaticProxyClient getInstance(){
return staticProxyClient;
}
/**
* @description: 开始静态代理
* @date 2019/6/12 16:20
* @author lmc
*/
public void startStaticProxy(){
ITarget targetImpl=new TargetImpl();
Target target=new Target();
StaticProxy staticProxy=new StaticProxy(targetImpl,target);
staticProxy.startProxy();
}
/**
* @description: 重写readResolve,防止序列化破坏单例
* @return java.lang.Object
* @date 2019/6/12 16:18
* @author lmc
*/
private Object readResolve(){
return staticProxyClient;
}
}
/**
* @description: 静态代理测试
* @author: lmc
* @create: 2019-06-12 16:10
**/
public class StaticProxyTest {
public static void main(String[] args) {
StaticProxyClient.getInstance().startStaticProxy();
}
}
静态代理测试结果
只要代理对象持有被代理对象的引用就可以实现静态代理了。
JDK-proxy
动态代理
/**
* @description: 真实的被代理的目标对象
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl1 implements ITarget {
public void behavior() {
System.out.println("执行被代理对象1的行为。");
}
}
/**
* @description: 真实的被代理的目标对象
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl2 implements ITarget {
public void behavior() {
System.out.println("执行被代理对象2的行为。");
}
}
/**
* @description: 真实的被代理的目标对象
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl3 implements ITarget {
public void behavior() {
System.out.println("执行被代理对象3的行为。");
}
}
/**
* @description: JDK动态代理对象
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class JdkProxy implements InvocationHandler {
/**
* 被代理对象的引用
*/
private ITarget target;
/**
* @description: 获取代理之后的实例对象
* @param target 被代理对象
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public ITarget getProxyInstance(ITarget target){
this.target = target;
Class<?> clazz = target.getClass();
return (ITarget) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeBehavior();
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
Object obj = method.invoke(this.target,args);
afterBehavior();
return obj;
}
/**
* @description: 被代理之前的增强行为
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("执行代理之前需要做的一些事情。");
}
/**
* @description: 被代理之后的增强行为
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("执行代理之后需要做的一些事情。");
}
}
下面这个类是测试没有实现ITarget
接口的代理
/**
* @description: JDK动态代理对象
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class JdkProxy1 implements InvocationHandler {
/**
* 被代理对象的引用
*/
private Target target;
/**
* @description: 获取代理之后的实例对象
* @param target 被代理对象
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public Target getProxyInstance(Target target){
this.target = target;
Class<?> clazz = target.getClass();
return (Target) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeBehavior();
//通过反编译工具可以查看源代码
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
Object obj = method.invoke(this.target,args);
afterBehavior();
return obj;
}
/**
* @description: 被代理之前的增强行为
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("执行代理之前需要做的一些事情。");
}
/**
* @description: 被代理之后的增强行为
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("执行代理之后需要做的一些事情。");
}
}
下面这个类是Jdk-proxy
代理生成的class字节码反编译之后的java
类
public final class $Proxy0 extends Proxy
implements ITarget {
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[]{
obj
})).booleanValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return false;
}
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return null;
}
public final void behavior() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return 0;
}
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.lmc.gp12380.pattern.proxy.ITarget").getMethod("behavior", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
这里我们知道了这个反编译生成的java
类实现了ITarget
接口
/**
* @description: JDK代理实现测试
* @author: lmc
* @create: 2019-06-12 19:49
**/
public class JdkProxyTest {
public static void main(String[] args) {
ITarget target1 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl1());
target1.behavior();
ITarget target2 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl2());
target2.behavior();
ITarget target3 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl3());
target3.behavior();
/**
* Target 类没有实现接口 无法被jdkproxy代理
*/
ITarget target4 = (ITarget) new JdkProxy1().getProxyInstance(new Target());
target4.behavior();
}
}
JDK-proxy
动态代理测试结果
嘿嘿,可以看到target4
代理出错啦。原因就是Target
类没有实现ITarget
接口。
CGLib
动态代理
/**
* @description: CGLib动态代理对象
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class CGLibProxy implements MethodInterceptor {
/**
* @description: 被代理之前的增强行为
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("执行代理之前需要做的一些事情。");
}
/**
* @description: 被代理之后的增强行为
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("执行代理之后需要做的一些事情。");
}
/**
* @description: 获取代理之后的实例对象
* @param target 被代理对象
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public Object getProxyInstance(Object target){
Class<?> clazz = target.getClass();
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeBehavior();
/**
* 利用 cglib 的代理类可以将内存中的 class 文件写入本地磁盘
*/
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib_proxy_class/");
Object obj = methodProxy.invokeSuper(o,objects);
afterBehavior();
return obj;
}
}
/**
* @description: CGLib代理实现测试
* @author: lmc
* @create: 2019-06-12 20:26
**/
public class CGLibProxyTest {
public static void main(String[] args) {
ITarget target1= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl1());
target1.behavior();
ITarget target2= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl2());
target2.behavior();
ITarget target3= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl3());
target3.behavior();
Target target4= (Target) new CGLibProxy().getProxyInstance(new Target());
target4.behavior();
}
}
CGLib动态代理测试结果
嘿嘿,可以看到Target没有实现ITarget
接口,也能实现动态代理。
代码里面是可以生成CGLib
动态代理的字节码文件的,也可以反编译过来看看,因为我是看不懂,我就不展示了。
代理模式的优缺点
优点
- 代理模式能将代理对象与真实被调用的目标对象分离。
- 一定程度上降低了系统的耦合度,扩展性好。
- 可以起到保护目标对象的作用。
- 可以对目标对象的功能增强。
缺点
- 代理模式会造成系统设计中类的数量增加。
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
- 增加了系统的复杂度。