设计模式学习记录
一:代理模式
JDK动态代理,代码实现案例
public interface MethodInterface { /** * 实现的方法 * @param age 年龄 * @param name 姓名 */ void implementsMethod(int age,String name); }
public class OrderService implements MethodInterface { @Override public void implementsMethod(int age,String name) { System.out.println("这是Order 服务执行的内容" + "name:" + name + ",age:" + age); } }
public class ShopService implements MethodInterface { @Override public void implementsMethod(int age,String name) { System.out.println("这是shop 服务执行的内容" + "name:"+name +",age:"+age); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @desc 代理类需要实现InvocationHandler 接口 * @date 2020/10/6 0:39 */ public class ProxyInvokerEnhance implements InvocationHandler { private Object target; public ProxyInvokerEnhance(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeDeal(); Object invoke = method.invoke(this.target, args); afterDeal(); return invoke; } private void afterDeal() { System.out.println("在代理之前处理一下数据,记录信息"); } private void beforeDeal() { System.out.println("在代理之后处理一下数据,收尾工作"); } // 通过反射获取新生成类的对象,新生成的类实现了原有类的所有接口 public Object getInstance(Object obj) { this.target = obj; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } }
public class ProxyTest { public static void main(String[] args) { OrderService orderService = new OrderService(); ProxyInvokerEnhance invokerEnhance = new ProxyInvokerEnhance(orderService); //保存JDK动态生成的代码位置 // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); /* 此处只能是接口强转,由于新生成的对象实现了原有类的接口,但不能直接转为接口的实现类*/ MethodInterface proxyOrderService = (MethodInterface) invokerEnhance.getInstance(orderService); proxyOrderService.implementsMethod(5,"11"); } }
// 这个类是代理过程中生成的类,无关运行,由ProxyTest 测试类中保存下来 // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.example.designPattern.proxy.MethodInterface; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements MethodInterface { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void implementsMethod(int var1, String var2) throws { try { super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.example.designPattern.proxy.MethodInterface").getMethod("implementsMethod", Integer.TYPE, Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
案例中共5个类:
MethodInterface
OrderService
ProxyInvokerEnhance
ProxyTest
ShopService代理的主要作用:对原有的类功能进行增强,注重对过程的干预,在原本处理的逻辑前后,进行额外的操作。
要求:被代理的对象需要实现接口,代理的过程中需要用到实现的接口
代理实现过程:
1)拿到被代理被的引用,并且通过反射获取它的所有接口
2)JDK Proxy类重新生成一个新的类,实现了被代理对象的所有接口方法
- 动态生成Java 代码,把增强的逻辑写到新生成的代码中
4)编译生成新的class文件
5)加载并运行新的class文件
关于JDK动态代理常见的问题:(
1.动态代理解决了什么问题?
答:首先它是一个代理机制,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,通过代理,可以提供更加友善的界面;还可以通过代理,做一个全局的拦截器。
2.动态代理和反射的关系是什么?
答:反射可以用来实现动态代理,但动态代理还有其他的实现方式,比如 ASM(一个短小精悍的字节码操作框架)、cglib 等。
3.以下描述错误的是?
A:cglib 的性能更高
B:Spring 中有使用 cglib 来实现动态代理
C:Spring 中有使用 JDK 原生的动态代理
D:JDK 原生动态代理性能更高
答:D
题目解析:Spring 动态代理的实现方式有两种:cglib 和 JDK 原生动态代理。
4.请补全以下代码?
class MyReflect { // 私有方法 private void privateMd() { System.out.println("Private Method"); } } class ReflectTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { Class myClass = Class.forName("MyReflect"); Object object = myClass.newInstance(); // 补充此行代码 method.setAccessible(true); method.invoke(object); } }
答:Method method = myClass.getDeclaredMethod(“privateMd”);
题目解析:此题主要考的是私有方法的获取,私有方法的获取并不是通过 getMethod() 方式,而是通过 getDeclaredMethod() 获取的。
5.cglib 可以代理任何类这句话对吗?为什么?
答:不完全对,因为 cglib 只能代理可以有子类的普通类,对于像最终类(final),cglib 是不能实现动态代理的,因为 cglib 的底层是通过继承代理类的子类来实现动态代理的,所以不能被继承类无法使用 cglib。
6.JDK 原生动态代理和 cglib 有什么区别?
答:JDK 原生动态代理和 cglib 区别如下:
JDK 原生动态代理是基于接口实现的,不需要添加任何依赖,可以平滑的支持 JDK 版本的升级;
cglib 不需要实现接口,可以直接代理普通类,需要添加依赖包,性能更高。
7.为什么 JDK 原生的动态代理必须要通过接口来完成?
答:这是由于 JDK 原生设计的原因,原本的设计中需要通过接口获取,动态代理的实现方法 newProxyInstance() 的源码如下:
/** * ...... * @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class to implement * ...... */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
8.JDK 动态代理中,目标对象调用自己的另一个方法,会经过代理对象么?
答:内部调用方法使用的对象是目标对象本身,被调用的方法不会经过代理对象。
2 cglib代理
需要引入依赖:
<!--cglib 代理需要的jar依赖,版本自选-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
代码如下:
public class UserService {
public void getUserName(String name){
System.out.println("cglib 模拟获取用户姓名");
}
}
package com.example.designPattern.proxy.cglibproxy;
import com.example.designPattern.proxy.jdkproxy.MethodInterface;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib代理依赖于继承关系,通过动态生成被代理的对象的子类实现
* 要求:被代理的对象不能被 final 修饰
* @auther djy
* @date 2020/10/6 3:09
*/
public class CglibProxyMethodIntercepte implements MethodInterceptor {
/**
* 设置代理对象,与jdk动态代理不同,此处传入的是class 类
* @param clazz 被代理的字节码对象
* @return 增强后的对象
* @throws Exception
*/
public Object getInstance(Class<?> clazz) throws Exception{
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 {
beforeDeal();
Object o1 = methodProxy.invokeSuper(o, objects);
afterDeal();
return o1;
}
private void afterDeal() {
System.out.println("在代理之前处理一下数据,记录信息");
}
private void beforeDeal() {
System.out.println("在代理之后处理一下数据,收尾工作");
}
}
public class CglibTest {
public static void main(String[] args) {
try {
UserService userService = (UserService)new CglibProxyMethodIntercepte().getInstance(UserService.class);
userService.getUserName("hehe");
} catch (Exception e) {
e.printStackTrace();
}
}
}
JDK 动态代理与cglib 代理主要区别:
jdk 被代理的类必须实现接口,cglib代理的类,不能为最终类,需要可以被继承
两者效率比较:在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了(此处并没有进行实际验证,参考别人验证的数据)