Java进阶篇——设计模式
设计模式
一、代理模式
使用代理类对真实对象进行代理,包括真实对象方法的调用、功能的扩展等。访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行扩展。类似于中介在卖家和买家之间的角色。
代理模式的角色主要有:抽象角色、真实角色、代理角色
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进阶篇——设计模式的更多相关文章
- Java进阶篇 设计模式之十四 ----- 总结篇
前言 本篇是讲述之前学习设计模式的一个总结篇,其目的是为了对这些设计模式的进行一个提炼总结,能够通过查看看此篇就可以理解一些设计模式的核心思想. 设计模式简介 什么是设计模式 设计模式是一套被反复使用 ...
- Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式
前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...
- Java进阶篇设计模式之十一 ---- 策略模式和模板方法模式
前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...
- Java进阶篇设计模式之十 ---- 访问者模式和中介者模式
前言 在上一篇中我们学习了行为型模式的解释器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern).本篇则来学习下行为型模式的两个模式,访问者模式(Visito ...
- Java进阶篇设计模式之九----- 解释器模式和迭代器模式
前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...
- Java进阶篇设计模式之八 ----- 责任链模式和命令模式
前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...
- Java进阶篇设计模式之七 ----- 享元模式和代理模式
前言 在上一篇中我们学习了结构型模式的组合模式和过滤器模式.本篇则来学习下结构型模式最后的两个模式, 享元模式和代理模式. 享元模式 简介 享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能 ...
- Java进阶篇设计模式之二 ----- 工厂模式
前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...
- Java进阶篇设计模式之三 ----- 建造者模式和原型模式
前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...
- Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式
前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...
随机推荐
- JavaScript函数式编程之函子
函子(Functor) 函子是一个特殊的容器,通过一个普通对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系),容器包含值和值变形关系(这个变形关系就是函数).函数式编 ...
- 39.BasicAuthentication认证
BasicAuthentication认证介绍 BasicAuthentication使用HTTP基本的认证机制 通过用户名/密码的方式验证,通常用于测试工作,尽量不要线上使用 用户名和密码必须在HT ...
- 前端性能优化——首屏时间&&白屏时间
1.首屏时间概念 首屏时间是指用户打开一个网站时,直到浏览器首页面内容渲染完成的时间. 2.白屏时间概念 白屏时间即是,浏览器开始显示内容的时间,所以我们一般认为解析完<head>的时刻, ...
- 从ObjectPool到CAS指令
相信最近看过我的文章的朋友对于Microsoft.Extensions.ObjectPool不陌生:复用.池化是在很多高性能场景的优化技巧,它能减少内存占用率.降低GC频率.提升系统TPS和降低请求时 ...
- Java安全之反序列化(1)
序列化与反序列化 概述 Java序列化是指把Java对象转换为字节序列的过程:这串字符可能被储存/发送到任何需要的位置,在适当的时候,再将它转回原本的 Java 对象,而Java反序列化是指把字节序列 ...
- ModuleNotFoundError: No module named 'XXX'
先来一张表情包: pycharm在小黑框使用pip安装某个包,在解释器没有找到某个包,所以运行程序的时候总是报错. 我相信大家可能都遇到这样的问题. 我下载有3.8.3.10版本的python,我py ...
- 图解S.O.L.I.D原则
如果您熟悉面向对象的编程,那么您可能已经听说过SOLID原理. 这五项软件开发原则是构建软件时要遵循的准则,以便于扩展和维护. 它们受到软件工程师Robert C. Martin的欢迎. 在线上有很多 ...
- salesforce零基础学习(一百二十一)Limitation篇之Heap Size Limitation
本篇参考: https://help.salesforce.com/s/articleView?id=000384468&type=1 https://help.salesforce.com/ ...
- Training: ASCII
题目链接:http://www.wechall.net/challenge/training/encodings/ascii/index.php 让我们使用标准的美国信息交换编码格式解下面的ASCLL ...
- python实现AES加密解密
1. 前言 AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个. 之前写过一片关于python AES加密解密的文章,但是这里面细节实在很多,这次我从 参数类型.加密模式.编码模式.补全 ...