设计模式

一、代理模式

使用代理类对真实对象进行代理,包括真实对象方法的调用、功能的扩展等。访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行扩展。类似于中介在卖家和买家之间的角色。

代理模式的角色主要有:抽象角色、真实角色、代理角色

1.静态代理

以张三到二手平台售卖二手电脑为例,张三为真实角色,二手平台为代理角色

抽象角色:

public interface User {
void sell();//购买方法
}

真实角色:

@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserImpl implements User{ private String name="张三";
private String thing="二手电脑"; @Override
public void sell() {
System.out.println(name+"要卖掉"+thing);
}
}

代理角色:

@Component
public class UserProxy implements User {
UserImpl user; @Override
public void sell() {
before();
user.sell();
after();
}
//扩展
public void before(){
System.out.println("包装了"+user.getThing());
}
public void after(){
System.out.println("售后服务");
}
}

测试:

@Component
public class StaticProxyTest {
public static void main(String[] args) {
new UserProxy(new UserImpl()).sell();
}
}

看起来似乎很简单,只是加了一层代理类就实现了扩展功能,但实际上维护时非常困难的。如果用户此时新增了需求,要在平台上买东西。那么直接带来了大量的代码工作,效率很低。也就产生了动态代理。

2.动态代理

和静态代理不同的是,动态代理的代理类是spring为我们生成的。相当于mybatisplus和mybatis的区别。

代理类生成工具类

public class DynamicProxy {
/*jdk动态代理
代理类需要是接口实现类impl.getClass().getInterfaces(),接收代理实例也是用接口来接收
通过反射Instance+拦截器Handler实现
jdk自带的代理支持
*/
public static Object jdkProxy(final Object impl){
Object proxyInstance = Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass()
.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke=null;
System.out.println("商品包装");
invoke = method.invoke(impl, args);
System.out.println("售后服务");
return invoke;
}
});
return proxyInstance;
} /*CGlib动态代理
通过对类继承来实现,无需接口实现
第三方工具,基于ASM实现
*/
public static Object CGlibProxy(final Object impl){
Object proxyInstance = Enhancer.create(impl.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = null;
System.out.println("商品包装");
invoke = method.invoke(impl, objects);
System.out.println("售后服务");
return invoke;
}
});
return proxyInstance;
} }

又或者可以实现对应的接口,以InvocationHandler举例

public class DynamicProxy implements InvocationHandler {
private User user; public Object getProxyInstance(User user){
this.user=user;
return Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println("商品包装");
invoke = method.invoke(user, args);
System.out.println("售后服务");
return invoke;
}
}//实际使用中只需要调用getProxyInstance方法,丢入一个原始类即可

使用:

public class DynamicProxyTest {
public static void main(String[] args) {
UserImpl user = new UserImpl();
User o = (User)DynamicProxy.jdkProxy(user);
o.sell();
// UserImpl o1 = (UserImpl)DynamicProxy.CGlibProxy(user);
// o1.sell();
}
}

3.Spring AOP

springaop便是动态代理实践的一个典例,不改变方法原有的代码,实现对方法功能的增强,使用aop之前,对aop相关的概念是一定要了解清楚的。

(1)aop相关概念

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 接入点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切入点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的抽象集合,一般以类的形式呈现。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
(2)springaop注解开发

由于目前来说,注解开发是最简单快捷的,这里只介绍注解开发,我们只需要知道底层是用动态代理实现的即可。

  • 创建切面类和通知

    只需要在类上添加@Aspect注解

    @Aspect
    public class MyAspect {
    public void before(){
    System.out.println("方法执行前");
    }
    public void after(){
    System.out.println("方法执行后");
    }
    }
  • 创建并注入切点

    @Aspect
    public class MyAspect {
    //表示将com.amlia.service包下的所有的类的所有方法(任何参数)定义为切点
    @Pointcut("execution(* com.amlia.service.*.*(..))")
    public void pointCut(){}
    @Before("pointCut()")
    public void before(){
    System.out.println("方法执行前");
    }
    //也可以直接在通知上面定义切点
    @After("execution(* com.amlia.service.*.*(..))")
    public void after(){
    System.out.println("方法执行后");
    }
    }
  • 除了before和after类型的通知外,还有其他类型

    @Before:方法执行前通知

    @After:方法执行后通知

    @Around:方法环绕通知

    @AfterReturning:方法返回后通知

    @AfterThrowing:方法错误抛出之后

    可以测试他们的执行顺序:

(3)aop的应用
  • 打印日志(方法执行前后打印参数方法名返回值或者调用关系等信息)

    日志级别:

    • OFF 关闭日志
    • FATAL 较严重的问题,高于ERROR
    • ERROR 打印错误信息
    • WARN 打印告警信息
    • INFO 打印日常信息
    • DEBUG 打印调试信息
    • TRACE 打印追踪信息
    • ALL 打印所有信息
  • 性能检测(方法执行前和方法执行后分别进行时间截取求差值)

  • 事务控制(抛出错误后进行事务回滚)

  • 权限控制(方法执行前检测用户是否有权限)

二、单例模式

单例模式顾名思义就是该类只能有一个实例,而且是被类自己创建的。外界不能访问该类的构造方法,因为他是私有的。这种模式为了解决单个类频繁的创建和销毁的情况或者说是某种单个实例的场景,比如只能有一个中国实例,并且很多框架底层都使用了单例模式,比如bean的生命周期中就有singleton单例。

单例模式有很多实现方式,为了适应不同种情况:

1.懒汉模式

客人点单了厨师才开始做菜,线程不安全,如果需要线程安全,单体架构下,在方法上加synchronize关键字即可,多体架构下,方法体内加分布式锁。

public class Singleton {
private static Singleton instance;
private Singleton (){}//构造方法私有 public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();//需要时加载——懒式加载
}
return instance;
}
}

2.饿汉模式

厨师提前做好菜,客人点了直接上菜。由于类加载过程中是阻塞等待机制,所以是线程安全的。缺点是产生了大量的"垃圾"对象,比较占用资源。

public class Singleton {
private static Singleton instance = new Singleton();//加载时初始化
private Singleton (){}//构造方法私有 public static Singleton getInstance() {
return instance;
}
}

3.双重校验的单例模式

大部分情况下,懒汉饿汉已经满足了。但是如果要求多线程下安全且高性能,那么还有一个较为复杂的模式

public class Singleton {
//volatile防止初始化指令重排,导致其他线程误以为初始化完成,空指针
private volatile static Singleton singleton;排
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {//提升性能
synchronized (Singleton.class) {
if (singleton == null) {//防止阻塞等待下实例化已经完成的情况
singleton = new Singleton();
}
}
}
return singleton;
}
}

4.静态内部类单例模式

这种方法同样利用了类加载机制的线程安全,但是巧妙的规避了资源的浪费。他利用内部类静态域的加载特性(使用时加载)达到了懒汉饿汉结合的效果。

public class Singleton {
private static class SingletonHolder {
//静态内部类的静态域
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

Java进阶篇——设计模式的更多相关文章

  1. Java进阶篇 设计模式之十四 ----- 总结篇

    前言 本篇是讲述之前学习设计模式的一个总结篇,其目的是为了对这些设计模式的进行一个提炼总结,能够通过查看看此篇就可以理解一些设计模式的核心思想. 设计模式简介 什么是设计模式 设计模式是一套被反复使用 ...

  2. Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式

    前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...

  3. Java进阶篇设计模式之十一 ---- 策略模式和模板方法模式

    前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...

  4. Java进阶篇设计模式之十 ---- 访问者模式和中介者模式

    前言 在上一篇中我们学习了行为型模式的解释器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern).本篇则来学习下行为型模式的两个模式,访问者模式(Visito ...

  5. Java进阶篇设计模式之九----- 解释器模式和迭代器模式

    前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...

  6. Java进阶篇设计模式之八 ----- 责任链模式和命令模式

    前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...

  7. Java进阶篇设计模式之七 ----- 享元模式和代理模式

    前言 在上一篇中我们学习了结构型模式的组合模式和过滤器模式.本篇则来学习下结构型模式最后的两个模式, 享元模式和代理模式. 享元模式 简介 享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能 ...

  8. Java进阶篇设计模式之二 ----- 工厂模式

    前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...

  9. Java进阶篇设计模式之三 ----- 建造者模式和原型模式

    前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...

  10. Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式

    前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...

随机推荐

  1. tensorboard图表显示不全的问题

    之前跑bcq生成tensorboard文件的时候,有二十个点用来描图,然而后10个数据点总是不显示,之后将tensorboard换成tensorboardX便解决了问题. 比如 from torch. ...

  2. 大文件分片上传,后端拼接保存(前端:antd;后端:.Net 5 WebAPI)

    前言 对于普通业务场景而言,直接用 FormData() 将文件以入参的一个参数传给后端即可,但此方法有一个弊端就是,有个 30M 的上限. 对于动辄几百 M.几个 G 的文件上传需求,FormDat ...

  3. debian如何删除无效的应用图标

    1.看/usr/share/applications下是否有xxx.desktop 2.可以到-/.local/share/applications下看是否有xxx.desktop 来源:https: ...

  4. Element Ui 安装以及配置

    npm 安装 推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用. npm i element-ui -S 引入 Element 你可以引入整个 Element,或是根据需要 ...

  5. freeswitch的mod_curl模块

    概述 有时候,我们需要在呼叫的过程中,或过程后调用web api接口. freeswitch的mod_curl模块可以很方便的实现web api的接口调用. mod_curl模块默认不安装,需要进入模 ...

  6. 对比entrypoint以及cmd

    如何查看 现有 images 的 入口信息 docker image inspect image_id { "entrypoint": "xxx" " ...

  7. Python 包(package)

    在比较大型的项目中常常需要编写.用到大量的模块,此时我们可以使用包(Package)来管理这些模块. (一)什么是包? Python包,就是里面装了一个__init__.py文件的文件夹. __ini ...

  8. Ubuntu环境下LLVM 15.0 完全编译 附windows编译LLVM master

    1. 预先安装 sudo apt install ninja-build sudo apt install llvm clang # 第一次编译需要 sudo apt-get install libn ...

  9. python选课系统项目详解

    选课系统项目详解 选课系统简介及分析 选课系统架构设计分析 选课系统目录设计 管理员视图 注册 登录 创建学校 创建课程 创建讲师 学生视图 注册 登录 选择学校 选择课程 查看分数 教师视图 登录 ...

  10. Web Api出现500 Internal Server Error 错误

    在测试环境一切正常,但是部署到了生产环境发现一直报错.查询网上的方法设置了权限等等.都没有解决 原来发现是数据库连接字符串的问题.只需要把数据库连接字符串修改正确即可!