深入浅出面向对象设计模式(Java)

news2024/12/24 2:56:59

设计模式是什么

设计模式是面向对象的一种思想。

设计模式的基本原则?

在这里插入图片描述

  • 单一职责原则
  • 开放封闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖翻转原则

基本分类和为什么分为3类?

创建型(怎么优雅创建对象)
结构性(对象的结构)
行为型(运行效果)(主要使用多态)

工厂模式

基本思想:我需要一辆汽车,我不用关心汽车是怎么创造出来的,只需要去工厂提货。
分类:创造型

public class CarFactory {

    public Car getCar(int type) {
        if (type == 1) {
            return new BenzCar("奔驰", 7);
        }
        if (type == 2) {
            return new BmwCar("宝马", 7);
        }
        throw new IllegalArgumentException("类型错误");
    }
}
public interface Car {

    void run();
}
public class BenzCar implements Car {

    String name;
    Integer total;

    public BenzCar(String name, Integer total) {
        this.name = name;
        this.total = total;
    }

    @Override
    public void run() {
        System.out.println("Benz run fast .. ");
    }

    @Override
    public String toString() {
        return "BenzCar{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }
}
public class BmwCar implements Car {

    String name;
    Integer total;

    public BmwCar(String name, Integer total) {
        this.name = name;
        this.total = total;
    }


    @Override
    public void run() {
        System.out.println("Bmw run fast too ..");
    }

    @Override
    public String toString() {
        return "BmwCar{" +
                "name='" + name + '\'' +
                ", total=" + total +
                '}';
    }
}

我需要汽车的时候只需要去CarFactory 里面告诉他们类型就可以得到对应的汽车。

JDK 中的工厂设计模式

java.util.Calendar

策略模式

策略是小处替换

为什么要使用策略模式?

答:消除业务逻辑中非常多的if else。具体业务看下面这个例子。

public String getCheckResult(String type) { 
  if ("校验1".equals(type)) { 
    return "执行业务逻辑1"; 
  } else if ("校验2".equals(type)) { 
    return "执行业务逻辑2"; 
  } else if ("校验3".equals(type)) { 
    return "执行业务逻辑3"; 
  } else if ("校验4".equals(type)) { 
    return "执行业务逻辑4"; 
  } else if ("校验5".equals(type)) { 
    return "执行业务逻辑5"; 
  } else if ("校验6".equals(type)) { 
    return "执行业务逻辑6"; 
  } else if ("校验7".equals(type)) { 
    return "执行业务逻辑7"; 
  } else if ("校验8".equals(type)) { 
    return "执行业务逻辑8"; 
  } else if ("校验9".equals(type)) { 
    return "执行业务逻辑9"; 
  } 
  return "不在处理的逻辑中返回业务错误"; 
} 

最简单的实现?

阿里规范对于多if-else的说明?

在这里插入图片描述

卫语句

Replace Nested Conditional with Guard Clauses

示例:来自规范嵩山版

    public void findBoyfriend(Man man) {
        if (man.isUgly()) {
            System.out.println("本姑娘是外貌协会的资深会员");
            return;
        }
        if (man.isPoor()) {
            System.out.println("贫贱夫妻百事哀");
            return;
        }
        if (man.isBadTemper()) {
            System.out.println("银河有多远,你就给我滚多远");
            return;
        }
        System.out.println("可以先交往一段时间看看");
    }

在这里插入图片描述

在这里插入图片描述

策略模式在 JDK 中的应用?

compare()、ThreadPoolExecutor 拒绝策略RejectedExecutionHandler

单例模式

定义

jvm中只有一个实例创建。

饿汉式

怎么理解什么是饿汉式,我已经饿的受不了了,我必须立即吃饭。
定义:在类加载的时候就会创建单例对象。
优点:简单,无线程安全问题。
缺点:浪费空间,不够灵活。

public class Hungry {

    public final static Hungry INSTANCE = new Hungry();

    private Hungry() {
    }
}

饿汉式 静态代码块

public class HungryStatic {

    public final static HungryStatic INSTANCE;

    static {
        INSTANCE = new HungryStatic("例如从配置文件读入");
    }

    private HungryStatic(String config) {
    }

    private String config;

    public String getConfig() {
        return config;
    }

    public void setConfig(String config) {
        this.config = config;
    }
}

枚举

优点:线程安全,不会被反射破坏。

public enum Enum {
    
    INSTANCE;
    
}

懒汉式 (线程不安全)

定义:在使用的时候判断如果没有创建过则创建。
优点:节约空间。
缺点:存在线程安全问题。

public class Lazy {

    private static Lazy instance;

    private Lazy() {
    }

    public static Lazy getInstance() {
        if (instance == null) {
            instance = new Lazy();
        }
        return instance;
    }
}

多个线程同时进行 instacne == null 判断,会导致多个线程同时创建对象。

懒汉式 双检锁 DCL

public class LazySafe {

    private static volatile LazySafe instance;

    private LazySafe() {
    }

    public static LazySafe getInstance() {
        if (instance == null) {
            synchronized (LazySafe.class) {
                if (instance == null) {
                    instance = new LazySafe();
                }
            }
        }
        return instance;

    }
}

为什么需要第二次判断

假设有10个线程同时进来争抢锁,线程一拿到锁,其他9个线程进入阻塞状态等待锁释放继续争抢,如果没有第二次判断,每个线程拿到锁后都会创建一个新的对象,加入第二次判断防止后来的线程创建。

为什么要加 volatile

由于指令重排序,new 对象的过程可能会发生先指向空对象但是没有完成初始化的情况。

一句话总结就是防止拿到 instance == null 的 对象。

反编译一条 new 指令可以拿到以下的结果。
在这里插入图片描述
步骤4 初始化对象,步骤7 指针指向该对象,由于 cpu存在指令重排序,先完成指针指向的话就会产生null对象的情况,所以需要禁止指令重排序保证对象初始化之后在完成指向的动作。

懒汉式 静态内部类

原理 : 静态内部类的延迟初始化,且类只会加载一次。

public class LazyInner {

    private LazyInner() {
    }

    private static class Inner {
        private static final LazyInner INSTANCE = new LazyInner();
    }

    public static LazyInner getInstance() {
        return Inner.INSTANCE;
    }

}

The “Double-Checked Locking is Broken” Declaration

如何破坏单例?

多线程破坏单例

在普通的懒汉式单例中可以被多线程破坏。

在这里插入图片描述

错误示例

将获取实例的锁升级到方法级别。

可以保证线程安全,但是方法性能太低,任意读请求都需要排队依次加锁获取对象。

典型的锁粒度过粗的问题。

在这里插入图片描述

正确示例

使用双检锁,或者内部类的形式保证线程安全。

指令重排序破坏单例

双检锁不使用 volatile 关键字修饰,就会产生重排序情况的产生。

克隆破坏单例

反序列化破坏单例

反射破坏单例

结论 只有枚举可以防止通过反射来破坏。

破坏实例

我们以Runtime类为例来演示使用反射破坏单例。

  		Class<Runtime> aClass = Runtime.class;
        Constructor<Runtime> cons = aClass.getDeclaredConstructor();
        cons.setAccessible(true);
        Runtime runtime = cons.newInstance();
        Runtime runtime2 = cons.newInstance();
        System.out.println(runtime);
        System.out.println(runtime2);
        ----
java.lang.Runtime@568db2f2
java.lang.Runtime@378bf509

解决方案

优先使用枚举来解决反射破坏单例的问题。
在这里插入图片描述

我们来获取一下枚举类中的构造器

        Class<Enum> aClass = Enum.class;
        Constructor<?>[] constructors = aClass.getDeclaredConstructors();
        // Constructor<Enum> cons = aClass.getDeclaredConstructor();
        Constructor<Enum> cons2 = aClass.getDeclaredConstructor(String.class, int.class);
        // cons.setAccessible(true);
        cons2.setAccessible(true);
        // Enum e1 = cons.newInstance();
        // Enum e2 = cons.newInstance();
        Enum e3 = cons2.newInstance();
        Enum e4 = cons2.newInstance();

可以看到里面会生成一个String , int 参数类型的构造器
在这里插入图片描述
idea 反编译结果跟预期明显不同。
在这里插入图片描述
javap 反编译 class 文件后发现也没有相应的构造方法
在这里插入图片描述
我们更换反编译工具,使用 jad 进行后续的测试

jad的安装和使用

jad下载地址

在这里插入图片描述
将 jad.exe 放在 jdk 的 bin 目录下即可
在这里插入图片描述

执行命令 jad xxxx.class文件得到反编译后的源代码。

jad 反编译后成功看到Enum类中 String 和 int 参数的构造函数。

在这里插入图片描述

单例在java的应用场景

Runtime

饿汉式实现单例。
在这里插入图片描述

代理模式

参考链接1

参考链接2

在这里插入图片描述

这里有一个接口 Msg

public interface Msg {

    void sendMsg(String msg);

    Msg work(String str);

}

这里有一个Msg的实现类

public class MsgImpl implements Msg {

    @Override
    public void sendMsg(String msg) {
        System.out.println(msg);
    }

    @Override
    public Msg work(String str) {
        System.out.println(str);
        return this;
    }
}

正常的调用过程如下

public class Client {
    public static void main(String[] args) {
        Msg msg = new MsgImpl();
        msg.sendMsg("abcdefg");
        msg.work("w1").work("w2");
    }
}

某一天我想统计一下每一个方法耗时,最基本的我直接修改实现类中的代码,如果这个类有一百个方法,那么。。。或者这个类是其他 jar 包提供的,我们就不能通过修改代码的形式来实现。

静态代理

我们直接把实现类的对象丢进来执行原来的方法,我们在增强我们想要的功能。

public class MsgProxy implements Msg {

    private Msg msg;

    public MsgProxy(Msg msg) {
        this.msg = msg;
    }

    @Override
    public void sendMsg(String str) {
        System.out.println("start ...");
        msg.sendMsg(str);
        System.out.println("end ...");
    }

    @Override
    public Msg work(String str) {
        return null;
    }
}

如果我们想统计 100 个类呢? 创建 100 个静态代理么,如果你不嫌麻烦的话当然可以。

那么有没有更加简单的方法呢?

动态代理

核心接口 InvocationHandler 用来控制代理对象的动作,也就是具体实现。
核心方法 Proxy.newProxyInstance 用来创建代理对象。

第一步定义处理器,代理对象的所有调用都会分发到这个方法中。

public class ActionInvocationHandle implements InvocationHandler {

    Object o;

    public ActionInvocationHandle(Object o) {
        this.o = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long st = System.currentTimeMillis();
        Object result = method.invoke(o, args);
        System.out.println(System.currentTimeMillis() - st);
        return result;
    }
}

第二步 创建代理对象

        Msg msg2 = new MsgImpl();
        Msg proxy2 = (Msg) Proxy.newProxyInstance(Msg.class.getClassLoader(),
                new Class[]{Msg.class}, 
                new ActionInvocationHandle(msg2));
        proxy2.sendMsg("sdadsa");

第三步 查看输出结果

sdadsa
1000

封装创建对象过程

在处理器中新增一段代码

public class ActionInvocationHandle implements InvocationHandler {

    Object o;

    public ActionInvocationHandle(Object o) {
        this.o = o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long st = System.currentTimeMillis();
        TimeUnit.SECONDS.sleep(1);
        Object result = method.invoke(o, args);
        System.out.println(System.currentTimeMillis() - st);
        return result;
    }

    public static <T> T createProxy(T target, Class<T> targetClass) {
        if (!targetClass.isInterface()) {
            throw new IllegalStateException("targetInterface必须是接口类型!");
        } else if (!targetClass.isAssignableFrom(target.getClass())) {
            throw new IllegalStateException("target必须是targetInterface接口的实现类!");
        }
        ClassLoader classLoader = target.getClass().getClassLoader();
        return (T) Proxy.newProxyInstance(classLoader,
                target.getClass().getInterfaces(),
                new ActionInvocationHandle(target));
    }
}

创建的过程简化为下面这样

        Msg msg2 = new MsgImpl();
        Msg proxy3 = ActionInvocationHandle.createProxy(msg2, Msg.class);
        proxy3.sendMsg("xxxxxxxxx");

如何获取Proxy0.class文件

以jdk1.8为例

        //生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

获取到的源码如下所示

public final class $Proxy0 extends Proxy implements Msg {
    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 sendMsg(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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.miccommon.designpatterns.proxy.Msg").getMethod("sendMsg", 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());
        }
    }
}

Proxy 类

Proxy 重要方法

在这里插入图片描述

//获取处理器
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
//获取代理class
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
//判断是否
    public static boolean isProxyClass(Class<?> cl)
//创建代理对象
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

cglib

添加依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

jar包目录如下
在这里插入图片描述
我们重点看一下 proxy 模块

在这里插入图片描述
这有一个类 Service1

public class Service1 {
    public void m1() {
        System.out.println("我是m1方法");
    }

    public void m2() {
        System.out.println("我是m2方法");
    }
}

使用 cglib 来代理 Service1 这一个类

public class Cglib01 {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}
------
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service1.m2()
我是m2方法

如果我们将 setCallback 这一行注释掉会发生什么呢?

在这里插入图片描述
可以看到 Callback 参数是必须要传的。

那么重点就是 setCallback 这个地方,我们点进去
在这里插入图片描述
参数传入了一个 Callback 类型接口,我们点进去
在这里插入图片描述
Callback 主要子接口如下
在这里插入图片描述

Dispatcher

在这里插入图片描述

在这里插入图片描述

public class CglibExtend08 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((Dispatcher) Service1::new);
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}

FixedValue

在这里插入图片描述

public class Cglib03 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback((FixedValue) () -> "路人甲");
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
        System.out.println(proxy.toString());
    }
}

InvocationHandler

在这里插入图片描述

jdk
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
cglib
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

在这种类型下跟 jdk 动态代理一样

public class Cglib07 {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
            System.out.println("调用方法:" + method);
            return method.invoke(service1, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}

LazyLoader 延迟加载

在这里插入图片描述
案例

MethodInterceptor

在这里插入图片描述

	MethodInterceptor
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
	InvocationHandler
 	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

这里多了一个 MethodProxy proxy

NoOp

在这里插入图片描述

public class Cglib04 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback(NoOp.INSTANCE);
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}

效果等同于没有做任何增强

ProxyRefDispatcher

在这里插入图片描述

案例1:使用MethodInterceptor增强方法处理

public class Service1 {

    public void m1() {
        System.out.println("我是m1方法");
    }

    public void m2() {
        System.out.println("我是m2方法");
    }

}
public class Cglib01 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service1.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service1 proxy = (Service1) enhancer.create();
        proxy.m1();
        proxy.m2();
    }
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法

案例2:在一个方法内调用另一个方法是否会被拦截?

    public void m1() {
        System.out.println("我是m1方法");
        this.m2();
    }

    public void m2() {
        System.out.println("我是m2方法");
    }
public class Cglib02 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service2.class);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("调用方法:" + method);
            return methodProxy.invokeSuper(o, objects);
        });
        Service2 proxy = (Service2) enhancer.create();
        proxy.m1();
    }
}
----
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m1()
我是m1方法
调用方法:public void com.example.lurenjia.spring.c15.cglib.Service2.m2()
我是m2方法

结论,代理类中一个方法调用另一个方法仍然会进入方法拦截器。

案例3:使用FixedValue拦截并返回固定值

public class Cglib03 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback((FixedValue) () -> "abcdefg");
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}
---
abcdefg
abcdefg

案例4:使用NoOp.INSTANCE不做增强处理

public class Service3 {
    public String m1() {
        System.out.println("我是m1方法");
        return "hello:m1";
    }

    public String m2() {
        System.out.println("我是m2方法");
        return "hello:m2";
    }
}
public class Cglib04 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service3.class);
        enhancer.setCallback(NoOp.INSTANCE);
        Service3 proxy = (Service3) enhancer.create();
        System.out.println(proxy.m1());
        System.out.println(proxy.m2());
    }
}
----
我是m1方法
hello:m1
我是m2方法
hello:m2

案例5:不同的方法使用不同的拦截器(CallbackFilter)

enhancer.setCallbackFilter 通过 Callback[] 索引位置来决定走哪一个处理器。

public class Service4 {
    public void insert1() {
        System.out.println("我是insert1");
    }

    public void insert2() {
        System.out.println("我是insert2");
    }

    public String get1() {
        System.out.println("我是get1");
        return "get1";
    }

    public String get2() {
        System.out.println("我是get2");
        return "get2";
    }
}
public class Cglib05 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Service4.class);
        Callback[] callbacks = {
                (MethodInterceptor) (o, method, objects, methodProxy) -> {
                    long starTime = System.nanoTime();
                    Object result = methodProxy.invokeSuper(o, objects);
                    long endTime = System.nanoTime();
                    System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
                    return result;
                },
                (FixedValue) () -> "fixedValueDefault"
        };
        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(method -> {
            String methodName = method.getName();
            return methodName.startsWith("insert") ? 0 : 1;
        });
        Service4 proxy = (Service4) enhancer.create();
        System.out.println("---------------");
        proxy.insert1();
        System.out.println("---------------");
        proxy.insert2();
        System.out.println("---------------");
        System.out.println(proxy.get1());
        System.out.println("---------------");
        System.out.println(proxy.get2());
    }
}
----
---------------
我是insert1
public void com.example.lurenjia.spring.c15.cglib.Service4.insert1(),耗时(纳秒):7290600
---------------
我是insert2
public void com.example.lurenjia.spring.c15.cglib.Service4.insert2(),耗时(纳秒):35500
---------------
fixedValueDefault
---------------
fixedValueDefault

案例6:使用CallbackHelper

public class Cglib06 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //第一个
        Callback costTimeCallback = (MethodInterceptor) (Object o, Method method, Object[] objects, MethodProxy methodProxy) -> {
            long starTime = System.nanoTime();
            Object result = methodProxy.invokeSuper(o, objects);
            long endTime = System.nanoTime();
            System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
            return result;
        };
        //第二个
        Callback fixdValueCallback = (FixedValue) () -> "路人甲Java";
        CallbackHelper callbackHelper = new CallbackHelper(Service4.class, null) {
            @Override
            protected Object getCallback(Method method) {
                return method.getName().startsWith("insert") ? costTimeCallback : fixdValueCallback;
            }
        };
        enhancer.setSuperclass(Service4.class);
        enhancer.setCallbacks(callbackHelper.getCallbacks());
        enhancer.setCallbackFilter(callbackHelper);
        Service4 proxy = (Service4) enhancer.create();
        System.out.println("---------------");
        proxy.insert1();
        System.out.println("---------------");
        proxy.insert2();
        System.out.println("---------------");
        System.out.println(proxy.get1());
        System.out.println("---------------");
        System.out.println(proxy.get2());
    }
}

案例7:实现通用的统计任意类方法耗时代理类

public class CostTimeProxy implements MethodInterceptor {
    //目标对象
    private Object target;

    public CostTimeProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long starTime = System.nanoTime();
        //调用被代理对象(即target)的方法,获取结果
        Object result;
        result = method.invoke(target, objects);
        // result = methodProxy.invokeSuper(o, objects);
        long endTime = System.nanoTime();
        System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
        return result;
    }

    /**
     * 创建任意类的代理对象
     *
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T createProxy(T target) {
        CostTimeProxy costTimeProxy = new CostTimeProxy(target);
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(costTimeProxy);
        enhancer.setSuperclass(target.getClass());
        return (T) enhancer.create();
    }
}

常见问题

method.invoke 原理

jdk动态代理死循环

常见在将 proxy 代理对象传入到 method.invoke中
在这里插入图片描述
method.invoke 一路追踪到一个native方法
在这里插入图片描述

$Proxy0 中的方法

		try {
            super.h.invoke(this, m3, new Object[]{var1});
        } 

由于动态代理生成的对象每一个方法都会进入到 处理器的 invoke 方法,而 method.invoke 每一次执行过一遍这个 invoke 因此形成了死循环。

为什么 jdk 动态代理只能代理接口

public class JdkProxyClass {
    public static void main(String[] args) {
        InvocationHandler h = (proxy1, method, args1) -> {
            return "success";
        };
        //方式一
        A jdkProxy = (A) Proxy.newProxyInstance(A.class.getClassLoader(), new Class[]{A.class}, h);
    }
}

class A {
}
--------
Exception in thread "main" java.lang.IllegalArgumentException: com.example.lurenjia.spring.c15.A is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.example.lurenjia.spring.c15.JdkProxyClass.main(JdkProxyClass.java:18)

在这里插入图片描述
还记得我们获取过的 Proxy0.class 这个文件么,注意这个类继承了 Proxy类。

public final class $Proxy0 extends Proxy implements Msg 

众所周知,java是不支持多重继承的,所以 jdk 动态代理在实现上确定了只能处理接口。

那么在源码中是哪一步继承的 Proxy 呢 ?

为什么 jdk 动态代理生成的代理类要继承 Proxy

试想一下,如果不继承 Proxy 这个类会发生什么呢?

区别

jdk动态代理只能为接口创建代理。
Enhancer既能够代理普通的class,也能够代理接口。
都是在运行期生成字节码。
jdk通过反射机制,cglib通过fastclass机制。

在这里插入图片描述

模板方法

意图

在这里插入图片描述

强调步骤的顺序。

模式的特征

抽象类abstract 和需要子类重写的abstract 方法。

实例

喝茶跟喝咖啡抽象成四个方法,第一步和最后一步是所有子类相同方法。
中间的两个步骤是子类所独有的方法。

public abstract class drink {
/**
     * 封装四个步骤
     */
    final void ToDrink() {
        first();
        second();
        third();
        fourth();
    }

    /**
     * 
     */
    abstract void second();

    /**
     * 
     */
    abstract void third();

    void first() {
        System.out.println("第一步添水");
    }

    void fourth() {
        System.out.println("第四步清洗");
    }
}

喝咖啡独特步骤

public class Coffee extends drink {

    /**
     * 独特的步骤由自己进行重写
     */
    @Override
    void second() {
        System.out.println("第二步倒入咖啡");
    }

    /**
     * 独特的步骤自己重写
     */
    @Override
    void third() {
        System.out.println("第三步倒入白糖");
    }


}

喝茶独特步骤

public class Tea extends drink {

    @Override
    void second() {
        System.out.println("第二步倒入茶叶");
    }

    @Override
    void third() {
        System.out.println("第三步倒入茶杯");
    }
}

分别执行"喝"这一过程

public class Client {
    public static void main(String[] args) {
        drink d1 = new Coffee();
        d1.ToDrink();
        System.out.println("-----------");
        drink d2 = new Tea();
        d2.ToDrink();
    }
}

结果如下

第一步添水
第二步倒入咖啡
第三步倒入白糖
第四步清洗
-----------
第一步添水
第二步倒入茶叶
第三步倒入茶杯
第四步清洗

优点

  1. 封装不变部分,扩展可变部分。
  2. 把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。
  3. 提取公共部分代码,便于维护。
  4. 行为由父类控制,子类实现

缺点

  1. 算法骨架需要改变时需要修改抽象类。
  2. 按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设计模式的区别

策略模式和状态模式的区别?

策略和模板区别?

模板定义大的框架,策略用来定义定义小细节。

经典组合

模板+策略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

巧用Hibernate 完成多数据库的DDL脚本创建

巧用Hibernate 完成多数据库的DDL脚本创建 spring boot jpa 默认的orm框架就是Hibernate。 由hibernate完成数据库的读写也是主流的方式之一。但是不同数据库之间&#xff0c;建表、建索引的方言语句都有较多差别&#xff0c;很难做到一套SQL在所有数据库上进行执行。 那么Hibe…

C++11之线程库

文章目录一、thread二、mutex三、lock_guard 与 unique_lock1. lock_guard2. unique_lock四、atomic五、condition_variable在 C11 之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如 Windows 和 Linux 下各有自己的接口&#xff0c;这使得代码的…

PHP另类判断 - 数组是一维还是二维

之前有一个需求&#xff0c;需要判断一个数组是一维还是二维数组&#xff0c;如果是二维的话就要使用foreach循环来处理 在网上搜了一下给出来的都是下面所写的方式&#xff1a; if(count($updata) count($updata,1)) {// 一维 } else {// 二维 }首先我要说的是&#xff0c;上…

第三十七章 数论——博弈论(1)

第三十七章 数论——博弈论&#xff08;1&#xff09;一、Nim游戏1、题目2、结论3、结论验证4、代码二、集合——Nim游戏1、问题2、思路—SG()函数2、代码实现&#xff08;记忆化搜索&#xff09;一、Nim游戏 1、题目 2、结论 这里直接说结论&#xff1a; 假设有nnn堆石子&am…

【LeetCode每日一题】——275.H 指数 II

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 二分查找 二【题目难度】 中等 三【题目编号】 275.H 指数 II 四【题目描述】 给你一个整数数…

Jmeter分布式测试

因为jmeter本身的性能问题&#xff0c;有时候为了尽量模拟业务场景&#xff0c;需要模拟大量的并发请求&#xff0c;此时单台压力机就显得力不从心。针对这个情况&#xff0c;jmeter的解决方案是支持分布式压测&#xff0c;即将大量的模拟并发分配给多台压力机&#xff0c;来满…

三优两重政策解读

什么是三优两重&#xff1a; 优秀大数据产品、优秀大数据解决方案、优秀大数据应用案例和重点大数据企业、重点大数据资源&#xff1b; 1、申报主体 在山东省内注册登记&#xff0c;具备独立承担民事责任的能力&#xff0c;包括各类政府机关、企事业单位及社会组织。 ①.大数据…

【从零开始学习深度学习】33.语言模型的计算方式及循环神经网络RNN简介

目录1. 语言模型1.1 语言模型的计算1.2 nnn元语法的定义2. 循环神经网络RNN2.1 不含隐藏状态的神经网络2.2 含隐藏状态的循环神经网络2.3 应用&#xff1a;基于字符级循环神经网络的语言模型3. 总结1. 语言模型 语言模型&#xff08;language model&#xff09;是自然语言处理…

多媒体服务器核心实现(流管理)

多媒体服务器比较多&#xff0c;实现的功能也很复杂&#xff0c;但其核心就是是转协议&#xff0c;流管理&#xff0c;连接管理&#xff0c;就是一个时序状态机和信令结合的系统。现在的生态有很多现成的轮子&#xff0c;c/c go实现的均可以拿来就用&#xff0c;只需要按一定的…

插槽,依赖注入,动态组件,异步组件,内置组件

插槽&#xff1a;父组件和子组件内容的一个通信 子组件使用<slot>接收父组件传入的内容 如果内容有多个标签时&#xff0c;使用<template>包裹 默认插槽&#xff1a; <template v-slot:default><h2>标题</h2><p>插槽内容</p> <…

Windows——编写jar启动脚本和关闭脚本

文章目录前言启动脚本编写关闭脚本restart.bat 重启脚本前言 假设项目打包后&#xff0c;项目结构为&#xff1a; 此时如果需要再windows环境中进行项目的启动或关闭&#xff0c;需要频繁的手敲命令&#xff0c;很不方便。此时可以编写.bat脚本文件进行项目的控制。 启动脚本…

就业信息追踪|基于Springboot+Vue开发实现就业信息追踪系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

双向链表,添加,删除一个节点

文章目录前言一、创建双向链表&#xff08;重命名&#xff09;二、添加一个节点1.添加头指针&#xff1a;2.若 头指针为空3.若头指针非空三、删除一个节点1.找到某节点2.将节点从链表中删除四. 展示所有的节点五. 实验效果总结前言 链表有几种&#xff0c;大致分为&#xff1a…

小程序之会议OA项目--其他界面

目录一、tabs组件及会议管理布局1、tabs.js2、tabs.wxml3、tabs.wxss4、app.wxss5、list.js6、list.json7、list.wxml二、个人中心布局1、ucenter/index/index.js2、ucenter/index/index.wxml3、ucenter/index/index.wxss一、tabs组件及会议管理布局 1、tabs.js // component…

UDS - 15.2 RequestDownload (34) service

15.2 请求下载(34)服务 来自&#xff1a;ISO 14229-1-2020.pdf 15.2.1 服务描述 客户机使用requestDownload服务发起从客户机到服务器的数据传输(下载)。 在服务器接收到requestDownload请求消息之后&#xff0c;服务器应该在发送积极响应消息之前采取所有必要的操作来接收数据…

常用图像像素格式 NV12、NV2、I420、YV12、YUYV

文章目录目的RGBYUVYCrCb采样格式YUV 4:4:4 采样YUV 4:2:2 采样YUV 4:2:0 采样YUV 存储格式YUV422&#xff1a;YUYV、YVYU、UYVY、VYUYYUV420&#xff1a;I420、YV12、NV12,、NV21扩展目的 了解常用图像像素格式 RGB 和 YUV,像素格式描述了像素数据存储所用的格式&#xff0c;…

Spring MVC框架学习

前言:本篇博客将从三个方面来写我们要学习SpringMVC的什么: 连接:当用户在游览器中输入一个url之后,能将这个url请求映射到自己写的程序,也就是访问一个地址时,能够连接到门自己写的服务器. 获取参数:用户访问时如果带一些参数,我该怎样获取.返回数据:执行业务代码之后…

NVM实现一台电脑对node的多版本管理。

一、NVM&#xff1a;Node Version Management&#xff1b; 下载地址&#xff1a;Releases coreybutler/nvm-windows GitHubA node.js version management utility for Windows. Ironically written in Go. - Releases coreybutler/nvm-windowshttps://github.com/coreybutl…

JavaScript寒假系统学习之数组(一)

JavaScript寒假系统学习之数组&#xff08;一&#xff09;一、数组1.1 什么是数组1.2 数组创建的2种方式1.2.1 利用new创建数组1.2.2 利用数组字面量创建数组1.3 访问数组元素1.4 遍历数组1.5 数组实战训练1.5.1 计算数组的和以及平均值1.5.2 求数组中的最大值1.5.3 数组转化为…

使用Qemu在Windows上模拟arm平台并安装debian10 arm系统(cd镜像) 安装记录

参考&#xff1a;使用Qemu在Windows上模拟arm平台并安装国产化操作系统_viyon_blog的博客-CSDN博客_qemu windows 镜像&#xff1a;debian-10.12.0-arm64-xfce-CD-1.iso 环境&#xff1a;qemu虚拟机&#xff0c;宿主机win10,amd64 QEMU_EFI.fd: (298条消息) qemu虚拟机的bi…