设计模式大全

news2024/11/26 2:23:05

使用设计模式的目的:

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

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;
    }
}

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

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

相关文章

3-JVM 运行时数据区

目录 1.堆&#xff08;线程共享&#xff09;&#xff08;最大的一块区域&#xff09; 2.Java虚拟机栈&#xff08;线程私有&#xff09; 3.本地方法栈&#xff08;线程私有&#xff09; 4.程序计数器&#xff08;线程私有&#xff09; 5.方法区&#xff08;线程共享&#…

Oracle JSON_ARRAYAGG()函数的默认排序问题

引入&#xff1a; 在实际操作中&#xff0c;俺写了这样一个Funtcion&#xff1a; FUNCTION fun_get_xxx(v_param_one VARCHAR2) RETURN CLOB ASv_OUTPUT CLOB;BEGINWITH temp_table AS (SELECT * FROM (( SELECT one.action_id,two.log_timeFROM table_one oneLEFT JOIN table…

Python程序设计基础:字符串

文章目录 一、字符串二、字符串的索引与切片三、字符串处理与操作四、format()格式化方法五、字符串与数值的转换 一、字符串 在Python中&#xff0c;使用单引号或双引号括起来的内容&#xff0c;称为字符串类型数据&#xff08;str&#xff09;&#xff0c;可以使用以下4种方…

Linux主分区,扩展分区,逻辑分区的联系和区别

基本概念 硬盘分区有三种&#xff0c; 主磁盘分区、扩展 磁盘分区、 逻辑分区。 一个 硬盘 主分区至少有1个&#xff0c;最多4个&#xff0c;扩展分区可以没有&#xff0c;最多1个。且 主分区扩展分区总共不能超过4个。 逻辑分区可以有若干个。 在windows下激活的 主分区是 …

树形结构-二叉树结构

树形结构 树形结构简介 树结构是一种非线性储存结构&#xff0c;存储的是具有“一对多”关系的数据元素的集合 树的相关术语 结点&#xff08;Node&#xff09; 使用树结构存储的每一个数据元素被成为“结点” 结点的度&#xff08;Degree of Node&#xff09; 某个结点所拥…

一次完整的Loadrunner基本流程操作

目录 一.生成脚本&#xff1a; 二.回放脚本&#xff1a; 三.创建场景&#xff1a; 四.生成报告&#xff1a; Loadrunner基本流程操作 准备条件&#xff1a; 一.安装loadrunner 二.破解loadrunner &#xff08;注&#xff1a;本次使用lr11版本可以兼容的IE浏览器版本为I…

Qt简单讲解项目结构

Qt简单讲解项目结构 项目结构 主函数入口 #include "mainwindow.h"#include <QApplication>// 程序入口 argc 表示命令行变量的数量 argv表示命令行变量的数组 int main(int argc, char *argv[]) {// a表示应用程序对象QApplication a(argc, argv);MainWin…

绿色节能数据中心供配电系统设计

随着新一代信息技术的快速发展&#xff0c;数据资源存储、计算和应用需求大幅提升&#xff0c;机房在各个领域都有着广泛的应用&#xff0c;如学校内有专用机房、通信类企业有通信机房等。近年来&#xff0c;国家对新型数据中心机房建设也越来越重视&#xff0c;据工信部、国家…

Flutter私服搭建之package查询

温馨提示&#xff1a;这是一篇私有的package客户端查询的平台搭建文章&#xff0c;牵扯到python中的Djiango框架&#xff0c;虽和Flutter相关&#xff0c;但客户端的代码并没有关联&#xff0c;请您根据需要进行阅读。 公有的package&#xff0c;对于一个Flutter开发者而言&…

centos + lnmp + tp6部署的项目,访问的时候经常出现No input file specified

1.检查路径设置 检查你的 Nginx 配置文件是否正确指定了 PHP 路径&#xff0c;确认文件路径是否正确。同时&#xff0c;确保你的 Web 服务器具有访问权限。 server { listen 80; server_name example.com; root /usr/share/nginx/html; index index.html ind…

汽车电子行业ECU烧录工艺人必须面对的重要课题

在汽车电子行业ECU烧录是很一个关键工序&#xff0c;如何有效地通过对它的过程进行管控是每个工艺人必须面对的重要课题。 为了解决烧录过程管控的问题&#xff0c;我们合共软件针对汽车电子行业研发的HG MES中有专门的烧录模块用于应对这一问题。对ECU烧录管控的核心目标是如…

MySQL 数据库的命令操作

文章目录 一.Mysql数据库的基本概念二.Mysql数据库系统发展史三.现主流Mysql数据库介绍四.关系数据库五.非关系数据库介绍六.MySQL安装方法1. 创建新的数据库2.创建新的表3.删除指定的数据库4.删除指定的数据表5.向数据表中插入新的数据记录6.修改、更新数据表中的数据记录7.在…

腾讯云对象存储COS及CDN加速配置

1. 登陆腾讯云官网&#xff0c;进入腾讯云对象存储COS控制台 腾讯云&#xff1a;https://cloud.tencent.com 2. 创建存储空间 3. 添加自定义CDN加速域名 在腾讯云COS的指定的存储桶中添加自定义CDN加速域名 在阿里云官网添加一个解析记录&#xff0c;等待两分钟就可以用该域名…

新书上市丨开启学习自然语言处理与ChatGPT的精彩旅程,你需要这本书!

2022年10月30日&#xff0c;ChatGPT 的横空出世&#xff0c;引起了全球范围内的广泛关注。微软创始人比尔盖茨 (Bill Gates) 认为 “ChatGTP 与互联网具有同等重要的意义”。作为一个人工智能系统&#xff0c;ChatGPT 能准确识别用户意图&#xff0c;与用户进行对话并提供有价值…

postgresql优化案例三:recheck cond

文章目录 1.SQL语句2.查看改善前执行计划:3.解决方案3.1增加work_mem的size3.2.创建合适的索引 4.改善后执行计划 1.SQL语句 delete from sap_dispatchingd_hist awhere exists (select 1 from sap_dispatchingm_hist b where a.ffact_nob.ffact_noand a.fsfc_nob.fsfc_noand …

正确认识:DOTA-CH2-Alkynyl(HCl salt),螯合剂修饰肽,物理化学性质参数

1.试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; DOTA-CH2-Alkynyl(HCl salt)使用有机溶剂或浓盐酸&#xff08;HCl&#xff09;&#xff0c;在酸化的环境中螯合剂修饰肽的主要优点是高效率和不存在有机溶剂。它可以应用于多种…

给清洁设备以“生命”,国邦从生产型制造走向服务型制造的转型之路|案例研究

国邦协同科技&#xff08;广州&#xff09;有限公司&#xff08;以下简称“国邦”&#xff09;成立于2011年&#xff0c;是行业领先的清洁解决方案服务商&#xff0c;致力于为客户提供全周期清洁解决方案&#xff0c;提高其清洁品质及效率。在2016年到2020年连续5年的时间里&am…

python 操作配置文件。

一&#xff1a;配置文件 1. 什么是配置文件 配置文件是为程序配置参数和初始设置的文件。一般为文本文件&#xff0c;以ini,conf,cnf,cfg,yaml等作为后缀名。 例如mysql的配置文件my.cnf内容如下&#xff1a; [mysqld] # Only allow connections from localhost bind-addre…

Java30天拿下---第三天(选择,循环,二重循环语句,输入,调试,标签)

Java30天拿下---第三天 一 流程图二 选择结构简单的if-else嵌套的if-else多重if的选择语句&#xff08;else-if&#xff09;switch语句 二 输入验证三 程序调试四 循环结构while循环do-while循环for循环流程控制二重循环&#xff08;重点&#xff09; 五 标签 label&#xff08…

中国移动云能力中心捐赠 secScanner 和 ksPack 项目,助力openEuler社区繁荣发展

2023 开放原子全球开源峰会于 6 月 11 日至 13 日在全球数字经济大会期间召开。本届大会以“开源赋能、普惠未来”为主题&#xff0c;全面展示开源技术应用&#xff0c;聚焦全球开源生态最新发展与前沿技术动态。中国移动云能力中心张胜举出席本次大会&#xff0c;并代表移动云…