设计模式大全

使用设计模式的目的:

程序猿在编码的过程中面临着来自耦合性、内聚性、可维护性、可扩展性、重用性、灵活性等多方面的挑战。设计模式是为了让程序具有更好的:

1)重用性,即相同功能的代码编写一次即可,不用重复编写

2)可读性,即编程的规范性

3)可扩展性

4)可靠性

5)高内聚,低耦合

设计模式六大原则

单一职责原则

一个类,一个方法,只是负责一项职责

开闭原则(Open Close Principle)

开闭原则就是说对扩展开放(提供方),对修改关闭(使用方)。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何及基(父)类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

当父类中的方法改动时会影响到所有继承他的子类,继承时会让子类和父类之间的耦合性增强了,在继承时建议遵循里氏替换原则,即子类中尽量不要重写父类的方法。

依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

底层模块尽量都要有抽象类或者接口,变量的声明类型尽量是抽象类或者是接口,这样引用和实际对象之间就有一个缓冲层,便于程序的扩展和优化。

接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

一个类对另一个类的依赖应当建立在最小的接口上

迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

降低类与类之间的耦合性

个人理解:A模块中的功能需要调用B,而B又需要调用C来协助完成,则这时只需要B暴露在A模块中,C不需要暴露在A模块。A->(B->C)

合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

当需要A调用某个类B中的方法时,可以不采用继承的方式,而是创建一个成员变量B b作为A的属性

设计模式的分类

创建型模式

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

Ps:原型模式 spring中创建bean,配置文件中配置scope=”prototype”

结构型模式

适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

行为模式

策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

单例模式

单例基本概念

在当前Jvm中只会有一个该实例对象

单例应用场景

  1. Servlet对象默认就是单例

  2. 线程池、数据库连接池

  3. Spring中Bean对象默认就是单例

  4. 实现网站计数器

  5. Jvm内置缓存框架(定义单例HashMap)

  6. 定义枚举常量信息

单例优缺点

优点:能够节约当前堆内存,不需要频繁New对象,能够快速访问。

缺点:当多个线程访问同一个单例对象的时候可能会存在线程安全问题。

单例的(10种)写法

懒汉式线程不安全

public class Singleton01 {
    private static Singleton01 singleton01 = null;
​
    /**
     * 私有构造函数
     */
    private Singleton01() {
​
    }
​
    /**
     * 懒汉式 线程不安全
     *
     * @return
     */
    public static Singleton01 getInstance() {
        if (singleton01 == null) {
            singleton01 = new Singleton01();
        }
        return singleton01;
    }
​
    public static void main(String[] args) {
        Singleton01 instance1 = Singleton01.getInstance();
        Singleton01 instance2 = Singleton01.getInstance();
        System.out.println(instance1 == instance2);
    }
​
}

懒汉式:当真正需要获取对象的时候,才去创建该单例对象,该写法存在线程问题

懒汉式线程安全

public class Singleton02 {
    private static Singleton02 singleton02 = null;
​
    /**
     * 懒汉式线程安全 
     * 缺点:已经创建对象后,获取该单例对象的时候还需要上锁效率比较低
     *
     * @return
     */
    public static synchronized Singleton02 getInstance() {
        if (singleton02 == null) {
            singleton02 = new Singleton02();
        }
        return singleton02;
    }
​
    public static void main(String[] args) {
        Singleton02 instance1 = Singleton02.getInstance();
        Singleton02 instance2 = Singleton02.getInstance();
        System.out.println(instance2 == instance1);
    }
}

该写法能够保证线程安全问题,获取该单例对象的时候效率非常低

懒汉式双重检验锁

public class Singleton03 {
    private static Singleton03 singleton03;
​
    public static Singleton03 getInstance() {
        // 第一次检查
        if (singleton03 == null) {
            //第二次检查
            synchronized (Singleton03.class) {
                if (singleton03 == null) {
                    singleton03 = new Singleton03();
                }
            }
        }
        return singleton03;
    }
​
    public static void main(String[] args) {
        Singleton03 instance1 = Singleton03.getInstance();
        Singleton03 instance2 = Singleton03.getInstance();
        System.out.println(instance1==instance2);
    }
}

能够保证线程安全,只会创建该单例对象的时候上锁,获取该该单例对象不会上锁,效率比较高。

DDL 使用volatile

我们在单例模式中使用 volatile,主要是使用 volatile 可以禁止指令重排序,从而保证程序的正常运行。这里可能会有读者提出疑问,不是已经使用了 synchronized 来保证线程安全吗?那为什么还要再加 volatile 呢?看下面的代码:

public class Singleton {
    private Singleton() {}
    // 使用 volatile 禁止指令重排序
    private static volatile Singleton instance = null;
    public static Singleton getInstance() {
        if (instance == null) { // ①
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // ②
                }
            }
        }
        return instance;
    }
}

注意观察上述代码,我标记了第 ① 处和第 ② 处的两行代码。给私有变量加 volatile 主要是为了防止第 ② 处执行时,也就是“instance = new Singleton()”执行时的指令重排序的,这行代码看似只是一个创建对象的过程,然而它的实际执行却分为以下 3 步:

  1. 申请并创建内存空间。
  2. 在内存空间中初始化对象 Singleton。
  3. 将内存地址赋值给 instance 对象(执行了此步骤,instance 就不等于 null 了)。

试想一下,如果不加 volatile,那么线程 1 在执行到上述代码的第 ② 处时就可能会执行指令重排序,将原本是 1、2、3 的执行顺序,重排为 1、3、2。但是特殊情况下,线程 1 在执行完第 3 步之后,如果来了线程 2 执行到上述代码的第 ① 处,判断 instance 对象已经不为 null,但此时线程 1 还未将对象实例化完,那么线程 2 将会得到一个被实例化“一半”的对象,从而导致程序执行出错,这就是为什么要给私有变量添加 volatile 的原因了。

饿汉式(私有)

public class Singleton04 {
    /**
     * 提前创建单例对象,优点先天性 保证线程安全,比较占用内存
     */
    public static final Singleton04 singleton04 = new Singleton04();
​
    private Singleton04() {
​
    }
​
    private static Singleton04 getInstance() {
        return singleton04;
    }
​
    public static void main(String[] args) {
        Singleton04 instance1 = Singleton04.getInstance();
        Singleton04 instance2 = Singleton04.getInstance();
        System.out.println(instance1 == instance2);
    }
}

饿汉式(公有)

public class Singleton04 {
    /**
     * 提前创建单例对象,优点先天性 保证线程安全,比较占用内存
     */
    public static final Singleton04 singleton04 = new Singleton04();
​
    private Singleton04() {
​
    }
​
    private static Singleton04 getInstance() {
        return singleton04;
    }
​
    public static void main(String[] args) {
        Singleton04 instance1 = Singleton04.singleton04;
        Singleton04 instance2 = Singleton04.singleton04;
        System.out.println(instance1 == instance2);
    }
}

饿汉静态代码块

public class Singleton05 {
    private static Singleton05 singleton05;
​
    static {
        singleton05 = new Singleton05();
    }
​
    public static Singleton05 getInstance() {
        return singleton05;
    }
​
    public static void main(String[] args) {
        Singleton05 instance1 = Singleton05.getInstance();
        Singleton05 instance2 = Singleton05.getInstance();
        System.out.println(instance1 == instance2);
    }
}

饿汉静态内部类

public class Singleton06 {
    private Singleton06() {
        System.out.println(">>>Singleton06");
    }
​
    private static class SingletonHolder {
        private static final Singleton06 singleton06 = new Singleton06();
    }
​
    public static final Singleton06 getInstance() {
        return SingletonHolder.singleton06;
    }
​
    public static void main(String[] args) {
        Singleton06 instance1 = Singleton06.getInstance();
        Singleton06 instance2 = Singleton06.getInstance();
        System.out.println(instance1==instance2);
    }
}

枚举实现单例

public enum Singleton07 {
​
​
    INSTANCE;
​
    public void getInstance() {
        System.out.println("<<<getInstance>>>");
    }
}

枚举最安全,不可以被反射也不能被序列化 破解

多少种方式可以创建对象

\1. 直接new对象

\2. 采用克隆对象

\3. 使用反射创建对象

\4. 序列化与反序列化

单例如何被破解

如何防止被反射破解

private Singleton01() throws Exception {
    if (singleton01 != null) {
        throw new Exception("该对象已经创建");
    }
    System.out.println("无参构造函数");
}
​
​
Class<?> aClass = Class.forName("com.mayikt.Singleton01");
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton01 instance02 = Singleton01.getInstance();
Singleton01 singleton01 = (Singleton01) constructor.newInstance();
System.out.println(singleton01==instance02);
​

如何防止序列化破解

序列化概念:将对象转换成二进制的形式直接存放在本地

反序列化概念:从硬盘读取二进制变为对象

// 1.将对象序列化存入到本地文件中
FileOutputStream fos = new FileOutputStream("d:/code/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Singleton04 singleton1 = Singleton04.singleton04;
oos.writeObject(singleton1);
oos.close();
fos.close();
//2.从硬盘中反序列化对象到内存中
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));
Singleton04 singleton2 = (Singleton04) ois.readObject();
System.out.println(singleton1);
System.out.println(singleton2);
​
​
public class Singleton04 implements Serializable {
    /**
     * 提前创建单例对象,优点先天性 保证线程安全,比较占用内存
     */
    public static final Singleton04 singleton04 = new Singleton04();
​
    private Singleton04() {
        System.out.println("Singleton04");
    }
​
    private static Singleton04 getInstance() {
        return singleton04;
    }
​
    public static void main(String[] args) {
        Singleton04 instance1 = Singleton04.singleton04;
        Singleton04 instance2 = Singleton04.singleton04;
        System.out.println(instance1 == instance2);
    }
}
​

重写readResolve()该方法 指定返回的对象 防止序列化破解

private Object readResolve() throws ObjectStreamException {
    return singleton04;
}

注意:如果该类是Serializable类型的 则调用它第一个非Serializable父类的无参构造函数初始化该对象

反射是否可以破解单例

不可以被反射 也不可以被序列化破解

//1.将对象序列化存入到本地文件中
        FileOutputStream fos = new FileOutputStream("d:/code/a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Singleton07 singleton1 = Singleton07.INSTANCE;
        oos.writeObject(singleton1);
        oos.close();
        fos.close();
        //2.从硬盘中反序列化对象到内存中
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));
        Singleton07 singleton2 = (Singleton07) ois.readObject();
        System.out.println(singleton1==singleton2);

代理模式

什么是代理模式

代理模式主要对我们方法执行之前与之后实现增强

代理模式应用场景

\1. 日志的采集

\2. 权限控制

\3. 实现aop

\4. Mybatis mapper

\5. Spring的事务

\6. 全局捕获异常

\7. Rpc远程调用接口

\8. 代理数据源

代理模式实现的原理

代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:

抽象主题角色:可以是接口,也可以是抽象类;

委托类角色:真实主题角色,业务逻辑的具体执行者;

代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

代理模式创建方式

静态代理

静态代理需要自己人工编写代理类代码

基于接口实现方式

public class OrderServiceProxy  implements  OrderService{
    private OrderService orderService;
​
    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }
​
    public String addOrder(String userName, String userPwd) {
        System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
        String result = orderService.addOrder(userName, userPwd);
        System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
        return result;
    }
}
​
​
public interface OrderService {
    /**
     * 需要被代理的方法
     * @return
     */
     String addOrder(String userName,String userPwd);
}
​
​
public class Test001 {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.addOrder("mayikt","123456");
    }
}

基于继承的实现方式

public class OrderServiceProxy  extends OrderServiceImpl {
    private OrderService orderService;
​
    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }
​
    public String addOrder(String userName, String userPwd) {
        System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
        String result = super.addOrder(userName, userPwd);
        System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
        return result;
    }
}

动态代理与静态代理的区别

动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。

动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

Jdk动态代理

JDK动态代理的一般步骤如下:

1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。生成后的代理对象,继承Proxy,实现被代理接口。

实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对我们的目标方法增强。

public class $Proxy0 implements com.mayikt.service.OrderService {
​
    private MayiktJdkInvocationHandler h;
    private static Method m3;
​
    public $Proxy0(MayiktJdkInvocationHandler mayiktJdkInvocationHandler) {
        this.h = mayiktJdkInvocationHandler;
    }
​
    @Override
    public String addOrder(String ver1, String var2) {
        try {
            return (String) h.invoke(this, m3, new Object[]{ver1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }
​
    static {
        try {
            m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
​
​
public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目标对象
     */
    private Object target;
​
    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }
    public <T> T getProxy() {
        return (T) new $Proxy0(this);
    }
}
​
MyJdkInvocationHandler myJdkInvocationHandler = new MyJdkInvocationHandler(new OrderServiceImpl());
OrderService orderService = myJdkInvocationHandler.getProxy();
orderService.addOrder("mayikt", "meite");
​
public class MyProxy {
    private static String rt = "\r\t";
​
    public static Object newProxyInstance(JavaClassLoader classLoader, Class classInfo, MayiktInvocationHandler mayiktInvocationHandler) {
        try {
            // 1.拼接java代理代理源代码
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.mayikt.service;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.mayikt.service.proxy.MayiktInvocationHandler;" + rt
                    + "import java.lang.reflect.UndeclaredThrowableException;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MayiktInvocationHandler h;" + rt
                    + "public $Proxy0(MayiktInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2.将该源代码写入到本地文件中
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            // 3.编译为class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 4.将class文件加入到内存中
            Class proxy0Class = classLoader.findClass("$Proxy0");
            //5.使用java反射机制给函数中赋值
            Constructor m = proxy0Class.getConstructor(MayiktInvocationHandler.class);
            Object object = m.newInstance(mayiktInvocationHandler);
            return object;
        } catch (Exception e) {
            return null;
        }
    }
​
    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";
​
        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getName() + " ver" + (i + 1));
                if (i < parameterTypes.length - 1) {
                    sb.append(" ,");
                }
            }
            String parameterStr = sb.toString();
            proxyMe = "public " + method.getReturnType().getName() + " " + method.getName() + " ( " + parameterStr + " ) { " +
                    "try {   Method m3 = Class.forName(\"com.mayikt.service.OrderService\").getMethod(\"addOrder\", Class.forName(\"java.lang.String\"), Class.forName(\"java.lang.String\"));" +
                    "return (String) h.invoke(this, m3, new Object[]{ver1, ver2}); } catch (RuntimeException | Error var4) {  throw var4;  } catch (Throwable var5) {   throw new UndeclaredThrowableException(var5); } " +
                    "" +
                    " } ";
​
        }
        return proxyMe;
    }
​
    public static void main(String[] args) {
        newProxyInstance(null, OrderService.class, null);
    }
}
​
​
​
​
public class JavaClassLoader extends ClassLoader {
​
    private File classPathFile;
​
    public JavaClassLoader(){
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath="D:\\code";
        this.classPathFile=new File(classPath);
    }
​
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className= JavaClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
          File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
          if(classFile.exists()){
              FileInputStream in=null;
              ByteArrayOutputStream out=null;
              try {
                  in=new FileInputStream(classFile);
                  out=new ByteArrayOutputStream();
                  byte[] buff=new byte[1024];
                  int len;
                  while ((len=in.read(buff))!=-1){
                     out.write(buff,0,len);
                  }
                  return defineClass(className,out.toByteArray(),0,out.size());
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  if(in!=null){
                      try {
                          in.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if(out!=null){
                      try {
                          out.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        }
        return null;
    }
}
​
​
public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目标对象
     */
    private Object target;
​
    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }
​
​
    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(new JavaClassLoader(), target.getClass().getInterfaces()[0], this);
    }
}
​
​

加上该代码:

\1. 获取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

\2. 使用反编译工具该Proxy0.class

注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

*纯手写Jdk动态代理*

思路分析:

\1. 定义InvocationHandler类 回调方法

\2. 使用java反射技术获取接口下所有的方法,拼接 $Proxy0.java代码

\3. 在将$Proxy0.java编译成class文件,读取到内存中

*CGLIB动态代理*

利用asm字节码技术,生成子类对目标方法实现增强

*实现方式*

Maven依赖

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies>

Cglib动态代理底层源码分析

Cglib依赖于ASM字节码技术,直接生成class文件,再采用类加载器读取到程序中,使用fastclass对被代理类的方法建立索引文件不需要依赖于反射查找到目标方法,所以效率比Jdk动态代理要高。

Jdk与Cglib动态代理的区别

\1. Jdk动态代理利用反射技术生成匿名的代理类,走InvokeHandler回调方法实现增强,同时也是一种基于接口的方式实现代理。

\2. Cglib动态代理利用asm字节码技术生成一个子类覆盖其中的方法实现增强,同时采用fastClass机制对整个代理类建立索引比反射效率要高

\3. 在Spring中如果需要被代理的对象如果实现了接口采用Jdk动态代理,没有实现接口则使用Cglib动态代理。

Java注解是Jdk1.5推出一个重大特性 可以标记在类、方法、属性上面

内置注解:

1.@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

2.@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

元注解:

@Retention - 标识这个注解怎 么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

@Documented - 标记这些注解是否包含在用户文档中。

@Target - 标记这个注解应该是哪种 Java 成员。

@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

基于Jdk动态代理手写异步注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtAsync {
}
​
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;
    private ExecutorService executorService;
​
    public JdkInvocationHandler(Object target) {
        this.target = target;
        executorService = Executors.newFixedThreadPool(10);
    }
​
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
        // 执行我们的目标方法
//        ExtAsync extAsync = method.getDeclaredAnnotation(ExtAsync.class);
        // 获取对应子类上是否有加上 @注解ExtAsync
        Method methodImpl = target.getClass().getMethod(method.getName(), method.getParameterTypes());
        ExtAsync extAsync = methodImpl.getDeclaredAnnotation(ExtAsync.class);
        Object result = null;
​
​
        if (extAsync != null) {
            // 则开启线程执行目标方法
            try {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            method.invoke(target, args);
                            return;
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
​
            } catch (Exception e) {
​
            }
        } else {
            result = method.invoke(target, args);
        }
        return result;
    }
​
    /**
     * 生成代理类
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}
​
​
OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(orderServiceImpl);
OrderService orderService = jdkInvocationHandler.getProxy();
orderServiceImpl.setProxy(orderService);
orderService.addOrder();

观察者模式

观察者模式基本概念

一个对象状态改变,通知给其他相关的所有的对象

观察者模式的应用场景

Zk的事件监听、分布式配置中心刷新配置文件、业务中群发不同渠道消息

观察者模式的类图

简单的实现ObServer观察者

定义ObServer接口

public interface MayiktObServer {
    void sendMsg(JSONObject jsonObject);
}
@Component
public class EmailServer implements MayiktObServer {
​
    public void sendMsg(JSONObject jsonObject) {
        System.out.println(Thread.currentThread().getName() + "2.短信观察者监听");
    }
}
@Component
public class SmsObServer implements MayiktObServer {
    public void sendMsg(JSONObject jsonObject) {
        System.out.println(Thread.currentThread().getName()+"使用观察者监听短信");
    }
}

主题通知所有观察者

@Component
public class MayiktSmsSubject {
    private ExecutorService executorService;
​
    private List<MayiktObServer> listObServer = new ArrayList<MayiktObServer>();
​
    public MayiktSmsSubject() {
        executorService = Executors.newFixedThreadPool(10);
    }
​
    public void addObServer(MayiktObServer obServer) {
        listObServer.add(obServer);
    }
​
    public void deleteObServer(MayiktObServer obServer) {
        listObServer.remove(obServer);
    }
​
    public void notifyObServer(final JSONObject jsonObject) {
        for (final MayiktObServer obServer : listObServer) {
            executorService.execute(new Runnable() {
                public void run() {
                    obServer.sendMsg(jsonObject);
                }
            });
​
        }
    }
​
}

项目启动注册观察者

@Component
public class StartService implements ApplicationRunner {
​
    @Autowired
    private MayiktSmsSubject mayiktSmsSubject;
    @Autowired
    private EmailServer emailServer;
    @Autowired
    private EmailServer smsObServer;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        mayiktSmsSubject.addObServer(emailServer);
        mayiktSmsSubject.addObServer(smsObServer);
    }
}
​
​
    @Autowired
    private MayiktSmsSubject mayiktSmsSubject;
​
    @RequestMapping("/addOrder")
    public String addOrder() {
        log.info("1.调用数据库下单订单代码:");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sms", "1865891111");
        jsonObject.put("email", "644064779@qq.com");
//        log.info("2.发送短信代码");
//        log.info("3.发送邮件代码");
        mayiktSmsSubject.notifyObServer(jsonObject);
        return "success";
    }
 

实现自动化注册

@Component
public class StartService implements ApplicationRunner, ApplicationContextAware {
    @Autowired
    private SMSObServer smsObServer;
    @Autowired
    private EmailObServer emailObServer;
    @Autowired
    private MayiktSubject mayiktSubject;
​
    private ApplicationContext applicationContext;
​
    /**
     * 当我们的SpringBoot启动成功的时候,注册我们的SMSObServer
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
//        mayiktSubject.addObServer(smsObServer);
//        mayiktSubject.addObServer(emailObServer);
        //根据接口类型返回相应的所有bean
        Map<String, ObServer> map = applicationContext.getBeansOfType(ObServer.class);
        for (String key : map.keySet()) {
            mayiktSubject.addObServer(SpringUtils.getBean(key));
        }
    }
​
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
​
        this.applicationContext = applicationContext;
    }
}

基于Spring事件通知实现

定义事件

public class UserMessageEntity extends ApplicationEvent {
    private String email;
    private String phone;
    private String userId;
​
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public UserMessageEntity(Object source) {
        super(source);
    }
​
    public UserMessageEntity(Object source, String email, String phone) {
        super(source);
        this.email = email;
        this.phone = phone;
    }
​
    @Override
    public String toString() {
        return "email:" + email + ",phone:" + phone;
    }
}

定义事件监听处理逻辑

@Component
public class EmailListener implements ApplicationListener<UserMessageEntity> {
​
    @Override
    public void onApplicationEvent(UserMessageEntity event) {
        System.out.println("eamil:"+event.toString());
    }
}
​
​
@Component
public class SmsListener implements ApplicationListener<UserMessageEntity> {
​
    @Override
    public void onApplicationEvent(UserMessageEntity event) {
        System.out.println("sms:" + event.toString());
    }
​
}

事件发布

@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@RequestMapping("/addOrder2")
public String addOrder2() {
    log.info("1.调用数据库下单订单代码:" + mayiktSmsSubject);
    UserMessageEntity messageEntity = new UserMessageEntity(this, "644064779@qq.com", "1865891111");
    applicationEventPublisher.publishEvent(messageEntity);
    return "success";
}

基于策略模式实现发送消息

策略模式基本概念

策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。

1.环境(Context)角色:持有一个Strategy的引用。

2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

定义策略接口->实现不同的策略类->利用多态或其他方式调用策略

策略模式应用场景

\1. 异步实现发送短信 比如阿里云、腾讯云、其他短信渠道等

\2. 聚合支付系统 银联支付、支付宝、微信支付等

\3. 联合登陆 QQ、钉钉、微信联合登陆渠道等

策略模式实现的方式

基于工厂模式实现

public interface MsgStrategy {
    String sendMsg();
}
​
public class AliYunStrategy implements MsgStrategy {
    public String sendMsg() {
        return "阿里云";
    }
}
public class HuaWeiStrategy implements MsgStrategy {
    public String sendMsg() {
        return "华为云";
    }
}
public class TencentStrategy implements MsgStrategy {
    public String sendMsg() {
        return "腾讯云";
    }
}
​
​
​
​
public class FactoryStrategy {
​
    /**
     * 存放策略的容器
     */
    private static Map<String, MsgStrategy> strategys = new ConcurrentHashMap<String, MsgStrategy>();
​
    static {
        strategys.put("aliYunStrategy", new AliYunStrategy());
        strategys.put("tencentStrategy", new TencentStrategy());
        strategys.put("huaWeiStrategy", new HuaWeiStrategy());
    }
​
    public static MsgStrategy getMsgStrategy(String strategyId) {
        return strategys.get(strategyId);
    }
}
​

基于数据库模式实现

数据库表结构

DROP TABLE IF EXISTS `meite_strategy`;
CREATE TABLE `meite_strategy` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `strategy_NAME` varchar(32) NOT NULL COMMENT '策略名称',
  `strategy_ID` varchar(32) NOT NULL COMMENT '策略ID',
  `strategy_type` varchar(32) NOT NULL COMMENT '策略ID',
  `strategy_bean_id` varchar(255) DEFAULT NULL COMMENT '策略执行beanid',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='策略';
​
-- ----------------------------
-- Records of meite_strategy
-- ----------------------------
INSERT INTO `meite_strategy` VALUES ('6', '腾讯云', 'tencent_sms', 'send_msg', 'tencentStrategy');
INSERT INTO `meite_strategy` VALUES ('7', '阿里云', 'aliYun_sms', 'send_msg', 'aliYunStrategy');
INSERT INTO `meite_strategy` VALUES ('8', '华为云', 'huaWei_sms', 'send_msg', 'huaWeiStrategy');
INSERT INTO `meite_strategy` VALUES ('9', '阿里Pay', 'ali_pay', 'pay', 'aliPayStrategy');
INSERT INTO `meite_strategy` VALUES ('10', '银联Pay', 'yinlian_pay', 'pay', 'unionPayStrategy');

根据策略ID查询策略

@Component
public class StrategyManage {
    @Autowired
    private StrategyMapper strategyMapper;
​
    public <T> T getStrategy(String strategyId, String strategyType, Class<T> t) {
        if (StringUtils.isEmpty(strategyId)) {
            return null;
        }
        if (StringUtils.isEmpty(strategyType)) {
            return null;
        }
        if (t == null) {
            return null;
        }
        QueryWrapper tQueryWrapper = new QueryWrapper<>();
        tQueryWrapper.eq("strategy_id", strategyId);
        MeiteStrategy meiteStrategy = strategyMapper.selectOne(tQueryWrapper);
        String strategyBeanId = meiteStrategy.getStrategyBeanId();
        if (StringUtils.isEmpty(strategyBeanId)) {
            return null;
        }
        return SpringUtils.getBean(strategyBeanId, t);
    }
​
​
}

模板方法模式

模板方法属于行为型设计模式,行为型设计模式主要关注对象之间职责分配和算法的问题。类行为型模式使用继承来分配类之间的职责,模板方法就是个类行为型模式。对象行为型模式使用组合来分配职责。在我们构建软件的过程中大部分时候我们都是在思考实体之间的职责,怎样的职责分配最合理,不至于过重,又不至于过轻,而且又不越权。

一、什么是模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现。

模板方法模式确实非常简单,*仅仅使用继承机制*,但是它是一个应用非常广泛的模式。

二、模板方法模式的使用场景

当系统中算法的骨架是固定的时候,而算法的实现可能有很多种的时候,就需要使用模板方法模式。

· 多个子类有共有的方法,并且逻辑基本相同

· 可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现

· 重构时,模板方法是一个经常使用的方法,把相同的代码抽取到父类中,然后通过构造函数约束其行为。

*举例*:需要做一个报表打印程序,用户规定需要表头,正文,表尾。但是客户的需求会变化,一会希望这样显示表头,一会希望那样显示。 这时候采用模板方式就合适。

三、模板方法模式的优缺点

*优点:*

· 封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。

· 提取公共部分代码,便于维护。

· 行为由父类控制,子类实现

缺点:

算法骨架需要改变时需要修改抽象类。

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

四、模板方法模式的实现

*AbstractClass类---抽象模板类*,定义并实现了一个模板方法。 这个模板一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。 顶级逻辑也有可以调用具体的方法

abstract class AbstractClass
{
    //一些抽象行为放到子类去实现
    public abstract void PrivateOperation1();
    public abstract void PrivateOperation2();
​
    //模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类实现
    public void TemplateMethod()
    {
        PrivateOperation1();
        PrivateOperation2();
        Console.WriteLine("");
    }
}

*ConcreteClass类*,实现父类所定义的一个或者多个抽象方法。 每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

class ConcreteClassA : AbstractClass
{
    public override void PrivateOperation1()
    {
        Console.WriteLine("具体类A方法1实现");
    }
​
    public override void PrivateOperation2()
    {
        Console.WriteLine("具体类A方法2实现");
    }
}class ConcreteClassB : AbstractClass
{
    public override void PrivateOperation1()
    {
        Console.WriteLine("具体类B方法1实现");
    }
​
    public override void PrivateOperation2()
    {
        Console.WriteLine("具体类B方法2实现");
    }
}

客户端代码

static void Main(string[] args)
{
    AbstractClass c;
    c = new ConcreteClassA();
    c.TemplateMethod();
​
    c = new ConcreteClassB();
    c.TemplateMethod();
​
    Console.Read();
}

五、总结

*重复=易错+难改*,模板方法模式是*通过父类建立框架,子类在重写了父类部分方法之后,在调用从父类继承的方法,产生不同的效果,通过修改子类,影响父类行为的结果*,模板方法在一些开源框架中应用非常多,它提供了一个抽象类,然后开源框架写了一堆子类,如果需要扩展功能,可以继承此抽象类,然后覆写protected基本方法,然后在调用一个类似TemplateMethod()的模板方法,完成扩展开发。

基于责任链实现权限框架

Ps: 在复杂的业务场景下的逻辑处理,可以使用拆分出多层,或拆分成多个子方法,依次调用实际上也是使用责任链模式的一个场景。

责任链基本概念

*客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象*。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。使编程更有灵活性。

定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。其过程实际上是一个递归调用。

要点主要是:

 1、有多个对象共同对一个任务进行处理。

2、这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。

3、一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。

4、客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。

责任链模式优缺点

优点:

责任链模式的最主要功能就是:动态组合,请求者和接受者解耦。

请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。

动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。

缺点:

产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。

不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。

责任链模式类结构图

 1.抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

 2.具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家

责任链模式应用场景

\1. 多条件流程判断 权限控制

\2. ERP系统 流程审批 总经理、人事经理、项目经理

\3. Java过滤器的底层实现Filter

\4. 风控系统 失信名单→信用卡是否逾期→蚂蚁信用积分650

比如:在Java过滤器中客户端发送请求到服务器端,会经过参数过滤、session过滤、表单过滤、隐藏过滤、检测请求头过滤

网关权限控制责任链模式

在网关作为微服务程序的入口,拦截客户端所有的请求实现权限控制 ,比如先判断Api接口限流、黑名单、用户会话、参数过滤。

Api接口限流→黑名单拦截→用户会话→参数过滤

责任链的实现

基于工厂模式实现责任链

public abstract class GatewayHandler {
​
    /**
     * 处理业务逻辑
     */
    public abstract void doService();
​
    public GatewayHandler(GatewayHandler gatewayHandler) {
        this.gatewayHandler = gatewayHandler;
    }
​
    private GatewayHandler gatewayHandler;
​
    protected void nextService() {
        if (gatewayHandler != null) {
            gatewayHandler.doService();
        }
    }
​
}
​
@Slf4j
public class CurrentLimitHandler extends GatewayHandler {
​
    public CurrentLimitHandler(GatewayHandler blacklistHandler) {
        super(blacklistHandler);
    }
​
​
    @Override
    public void doService() {
        log.info(">>第一关api接口限流<<");
        nextService();
    }
​
​
}
​
​
@Slf4j
public class BlacklistHandler extends GatewayHandler {
​
    public BlacklistHandler(GatewayHandler conversationHandler) {
        super(conversationHandler);
    }
​
    @Override
    public void doService() {
        log.info(">>第二关黑名单拦截<<");
        nextService();
    }
​
​
}
@Slf4j
public class ConversationHandler extends GatewayHandler {
    private GatewayHandler gatewayHandler;
​
    public ConversationHandler(GatewayHandler gatewayHandler) {
        super(gatewayHandler);
    }
​
    @Override
    public void doService() {
        log.info(">>>>第三关-判断用户的会话信息 <<<");
    }
}
​
​
public class FactoryHandler {
​
    /**
     * 获取第一个currentLimitHandler
     *
     * @return
     */
    public static GatewayHandler getFirstGatewayHandler() {
        CurrentLimitHandler currentLimitHandler =
                new CurrentLimitHandler(new BlacklistHandler(new ConversationHandler(null)));
        return currentLimitHandler;
    }
}
​

基于数据库模式实现责任链

数据库表结构

CREATE TABLE `gateway_handler` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `handler_name` varchar(32) DEFAULT NULL COMMENT 'handler名称',
  `handler_id` varchar(32) DEFAULT NULL COMMENT 'handler主键id',
  `prev_handler_id` varchar(32) DEFAULT NULL,
  `next_handler_id` varchar(32) DEFAULT NULL COMMENT '下一个handler',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
​
-- ----------------------------
-- Records of gateway_handler
-- ----------------------------
INSERT INTO `gateway_handler` VALUES ('16', 'Api接口限流', 'currentLimitHandler', null, 'blacklistHandler');
INSERT INTO `gateway_handler` VALUES ('17', '黑名单拦截', 'blacklistHandler', 'currentLimitHandler', 'conversationHandler');
INSERT INTO `gateway_handler` VALUES ('18', '会话验证', 'conversationHandler', 'blacklistHandler', null);

@Component
@Slf4j
public class DbHanlder {
​
    @Autowired
    private GatewayHandlerMapper gatewayHandlerMapper;
​
    public GatewayHandler getFirstGatewayHandler() {
        //1.查找到链表头部
        QueryWrapper<GatewayHandlerEntity> queryHeadWrapper = new QueryWrapper<GatewayHandlerEntity>();
        queryHeadWrapper.isNull("prev_handler_id");
        GatewayHandlerEntity gatewayHeadHandlerEntity = gatewayHandlerMapper.selectOne(queryHeadWrapper);
        if (gatewayHeadHandlerEntity == null) {
            log.info(">>>数据库中没有配置链表头部<<");
            return null;
        }
​
        // 2.从Spring中查找到该头对象
        GatewayHandler gatewayHeadHandler = SpringUtils.getBean(gatewayHeadHandlerEntity.getHandlerId(),
                GatewayHandler.class);
        if (gatewayHeadHandler == null) {
            log.info(">>>在相聚中没有配置<<" + gatewayHeadHandler);
            return null;
        }
        //3.关联NextGatewayHandler
        String nextHandlerId = gatewayHeadHandlerEntity.getNextHandlerId();
​
        // 4.创建临时对象指针
        GatewayHandler tempGatewayHeadHandler = gatewayHeadHandler;
        while (StringUtils.isNoneBlank(nextHandlerId)) {
            GatewayHandler gatewayNextHandler = SpringUtils.getBean(nextHandlerId, GatewayHandler.class);
            if (gatewayNextHandler == null) {
                return null;
            }
​
            //4.查询下一个节点
            QueryWrapper queryNextWrapper = new QueryWrapper<GatewayHandlerEntity>();
            queryNextWrapper.eq("handler_id", nextHandlerId);
            GatewayHandlerEntity gatewayNextHandlerEntity = gatewayHandlerMapper.selectOne(queryNextWrapper);
            nextHandlerId = gatewayNextHandlerEntity.getNextHandlerId();
            tempGatewayHeadHandler.setGatewayHandler(gatewayNextHandler);
            tempGatewayHeadHandler = gatewayNextHandler;
        }
        return gatewayHeadHandler;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

史上最全Hadoop面试题:尼恩大数据面试宝典专题1

说在前面&#xff1a; 《尼恩 大数据 面试宝典》 是 《尼恩Java面试宝典》 姊妹篇。 这里特别说明一下&#xff1a;《尼恩Java面试宝典》41个专题 PDF &#xff08;请在文末获取&#xff09;自发布以来&#xff0c; 已经收集了 好几千题&#xff0c; 足足4000多页&#xff0c…

haproxy

haproxy haproxy一&#xff1a;常见的Web集群调度器1.软件2.硬件3.LVS &#xff0c;Nginx &#xff0c;Haproxy 的区别&#xff1a; 二&#xff1a;Haproxy应用分析1.HAProxy的主要特性有&#xff1a;2.HAProxy负载均衡策略非常多&#xff0c;常见的有如下8种&#xff1a; 三&a…

【深度学习】YOLOv8训练过程,YOLOv8实战教程,目标检测任务SOTA,关键点回归

文章目录 可用资源资源安装模型训练&#xff08;检测&#xff09;模型pridict模型导出 可用资源 https://github.com/ultralytics/ultralytics 官方教程&#xff1a;https://docs.ultralytics.com/modes/train/ 资源安装 更建议下载代码后使用 下面指令安装&#xff0c;这样…

C高级 day37

1、编写一个名为myfirstshell.sh的脚本&#xff0c;它包括以下内容。 1、包含一段注释&#xff0c;列出您的姓名、脚本的名称和编写这个脚本的目的 2、和当前用户说“hello 用户名” 3、显示您的机器名 hostname 4、显示上一级目录中的所有文件的列表 5、显示变量PATH和HOME的值…

uniapp实现应用wgt资源热更新

APP更新一般有两种形式 1、整包更新&#xff0c;通过hbuliderx提供的在线云打包就属于整包更新&#xff0c;属于全量更新&#xff0c;缺点就是打包时间长、要重新走市场审核。费时 2、wgt资源包热更新&#xff0c;通过hbuliderx打wgt包 &#xff0c;速度快&#xff0c;能在应用…

28.vite

目录 1 一些概念 1.1 单页面应用程序SPA 1.2 vite 2 初始化vite项目 3 项目中的文件 1 一些概念 1.1 单页面应用程序SPA 单页面应用程序是只有一个页面的前端&#xff0c;切换页面通过前端路由来切换 特点如下 实现了前后端分离&#xff0c;后端仅出接口&#…

域名解析详解

域名解析 记录类型&#xff1a; 提示&#xff1a; 将域名指向云服务器&#xff0c;选择 A&#xff1b; 将域名指向另一个域名&#xff0c;选择 CNAME&#xff1b; 建立邮箱选择 MX&#xff0c;根据邮箱服务商提供的 MX 记录填写。 记录类型解释A用来指定域名的 IPv4 地址&…

优思学院|六西格玛倡导者与项目赞助人是什么角色?有何区别?

倡导者&#xff08;Champion&#xff09;和项目赞助人&#xff08;Sponsor&#xff09;在正式的六西格玛的组织架构中是两个不同的角色&#xff0c;所以希望在这篇文章中解释一下两个角色的区别。 倡导者&#xff08;Champion&#xff09;是负责组织竞争力和增长的董事和高管&…

C#提升(一、泛型)

一、什么是泛型 泛型&#xff0c;即“参数化类型” 我们来看以下代码&#xff0c;目的很明确&#xff0c;就是显示参数类型&#xff0c;这种类似的代码或者说只有参数类型不同&#xff0c;但是功能相同时&#xff0c;我们如何让代码写的更优雅&#xff1f; 在泛型没有出现的…

Android11 DNS解析流程

Android11 DNS解析 1. DNS解析概念 ​ DNS的全称是domain name system&#xff0c;即域名系统。主要目的是将域名解析为IP地址&#xff0c;域名是方便用户记忆&#xff0c;但网络传输中源目地址使用IP地址来进行标识的&#xff0c;所以Android中的网络应用程序在发起http请求…

Python(request)爬虫有多强大?

requests是Python中的一种HTTP客户端库&#xff0c;用于发送HTTP请求并获取服务器响应。使用requests库可以轻松地进行常见的HTTP操作&#xff0c;如GET、POST、PUT、DELETE等&#xff0c;支持HTTPS和HTTP连接以及摘要验证、基本认证等身份验证方式。 因此&#xff0c;Python的…

阿里云创建ALB_Ingress

阿里云参考文档 1、编写alb AlbConfig创建文档 rootbiking-pre-middleware:~/alb# cat ingress-alb.yaml apiVersion: alibabacloud.com/v1 kind: AlbConfig metadata: #alb名称name: ingress-alb spec:config:name: ingress-albaddressType: InternetzoneMappings:# 两个交换…

ESP32学习之定时器和PWM

一.定时器代码如下&#xff1a; #include <Arduino.h>hw_timer_t *timer NULL; int interruptCounter 0;// 函数名称&#xff1a;onTimer() // 函数功能&#xff1a;中断服务的功能&#xff0c;它必须是一个返回void&#xff08;空&#xff09;且没有输入参数的函数 //…

Redis 哨兵模式

哨兵模式 自动选举 Redis 主服务器&#xff08;当主服务器宕机时&#xff09; 在 Redis2.8 之前&#xff0c;采用手动配置主从机的形式&#xff08;会导致一段时间服务不可用&#xff09; Redis2.8 之后&#xff0c;Redis正是提供了 Sentinel&#xff08;哨兵&#xff09;来解…

逆向Android开发工程,抓包!抓包!学习哪里?

抓包是什么&#xff1f; 在Android逆向工程中&#xff0c;抓包是一项重要的技术&#xff0c;用于获取手机应用程序与服务器之间的通信数据。通过抓包&#xff0c;可以分析应用程序的网络请求&#xff0c;获取请求的URL、参数、响应数据等信息&#xff0c;对应用程序的行为进行…

Python编程入门基础及高级技能、Web开发、数据分析和机器学习与人工智能

文章目录 入门基础安装 Python 环境&#xff0c;选择一个 IDE&#xff0c;如 PyCharm、VSCode等。学习基本语法&#xff1a;变量、数据类型、条件语句、循环语句、函数、异常处理等。熟悉标准库&#xff1a;常用模块、内置函数等。学习基本的面向对象编程&#xff08;OOP&#…

Rust in Action笔记 第六章 内存

Option<T>类型在Rust中使用了空指针优化&#xff08;null pointer optimization&#xff09;来保证该类型在编译后的二进制文件中占用0个字节。None变量是通过一个空指针null pointer来表示&#xff1b;内存地址、指针、引用的区别&#xff0c;内存地址是指在内存中的一个…

【kubernetes】Etcd集群部署与验证

前言:二进制部署kubernetes集群在企业应用中扮演着非常重要的角色。无论是集群升级,还是证书设置有效期都非常方便,也是从事云原生相关工作从入门到精通不得不迈过的坎。通过本系列文章,你将从虚拟机准备开始,到使用二进制方式从零到一搭建起安全稳定的高可用kubernetes集…

基于OpenMV的自动驾驶智能小车模拟系统

一、项目简介 基于机器视觉模块OpenMV采集车道、红绿灯、交通标志等模拟路况信息&#xff0c;实现一辆能车道保持、红绿灯识别、交通标志识别、安全避障以及远程WiFi控制的多功能无人驾驶小车。 赛道规格&#xff1a; 1、编程所需软件&#xff1a; OpenMV&#xff1a;使用Op…

Python自动化测试框架:Pytest和Unittest的区别

pytest和unittest是Python中常用的两种测试框架&#xff0c;它们都可以用来编写和执行测试用例&#xff0c;但两者在很多方面都有所不同。本文将从不同的角度来论述这些区别&#xff0c;以帮助大家更好地理解pytest和unittest。 1. 原理 pytest是基于Python的assert语句和Pytho…