设计模式学习记录
一:代理模式
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动态代理的速度快很多了(此处并没有进行实际验证,参考别人验证的数据)