设计模式(一)

文章目录

前言及工厂模式

七大原则

开闭原则

对扩展开放,对修改关闭

开闭原则,是面向对象设计中最基础的设计原则。它指导我们如何建立稳定灵活的系统,
例如:我们版本更新,我尽可能不修改源代码,但是可以增加新功能。
在现实生活中对于开闭原则也有体现。比如,很多互联网公司都实行弹性制作息时间,
规定每天工作 8 小时。意思就是说,对于每天工作 8 小时这个规定是关闭的,但是你什
么时候来,什么时候走是开放的。早来早走,晚来晚走。

单一职责原则

一个类,接口,方法只做一件事

不要存在多于一个,导致类变更的原因。假设我们有一个 Class 负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能会导致另一个职责的功能发生故障。

依赖倒置原则

通过一个抽象的接口或者类,使得各个类和模块,相互不影响,实现松耦合

接口隔离原则

尽量保证接口的纯洁性,客户端不应该依赖不需要的就看

迪米特法则

最小知道原则,一个类对其所依赖的类知道的越少越好。不该让子类知道的就别让他知道
private:不该让子类知道的
protected:不该让其他依赖的类知道的

里式替换原则

子类可以扩展父类的功能,但是不能改变原来的功能

合成复用原则

尽量得去使用对象的组合,聚合而不使用继承关系,达到代码的复用(代理模式,包装器模式)

设计原则解释
开闭原则对扩展开放,对修改关闭
依赖倒置原则通过抽象使各个类或者模块不相互影响,实现松耦合
单一职责原则一个类,接口,方法只做一件事
接口隔离原则尽量保证接口的纯洁性,客户端不应该依赖不需要的接口
迪米特法则又叫最少知道原则,一个类对其所依赖的类知道得越少越好
里氏替换原则子类可以扩展父类的功能但不能改变父类原有的功能
合成复用原则尽量使用对象组合,聚合,而不使用继承关系达到代码复用的目的

目标

  1. 写出优雅的代码
  2. 更好地重构项目
  3. 经典框架都在用设计模式解决问题

Spring 中用到的设计模式

  • 工厂模式 BeanFactory
  • 装饰器模式 BeanWrapper
  • 代理模式 AopProxy
  • 单例模式 ApplicationContext
  • 委派模式 DispathcerServlet
  • 策略模式 HandlerMapping
  • 适配器模式 JdbcTemplate
  • 观察者模式 ContextLoaderListener

Spring 设计思路

  • Spring IOC 工厂,单例,装饰器
  • Spring AOP 代理,观察者
  • Spring MVC 委派,适配器
  • Spring JDBC 模板方法

参考资料

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2GdZMixU-1647080017419)(D:\文件_Typora\1591348078755.png)]

设计模式分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7Qb01UL-1647080017420)(D:\文件_Typora\1591582047907.png)]

工厂模式

富士康工厂可以生产苹果,小米,华为。

简单工厂模式

SimpleFactory

并不是23种设计模式之一

定义

简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例

用一个单独的类来做和这个创造实例的过程,封装类的创建过程

优化

优化前:类中使用switch

public ICourse create(String name){
    if("java".equals(name))
        return new JavaCourse();
    else
        return null;
}

优化后:

///优化一//
public ICourse create(String className){
    try{
        if(!(null==className||"".equals(className))){
            return (ICourse)Class.forName(className).newInstance();
        }
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}
public class SimpleFactoryTest{
    public static void main(String[] args){
        CourseFactory factory=new CourseFactory();
        //缺点:写错不会标红
        ICourse course=factory.create("com.**.JavaCourse");
    }
}
///优化二//
//优点:防止表示打错,写错后会标红
public ICourse create(Class clazz){
    try{
        if(null!=clazz)){
            //缺点:存在强转
            return (ICourse)clazz.newInstance();
        }
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}
public class SimpleFactoryTest{
    public static void main(String[] args){
        CourseFactory factory=new CourseFactory();
        ICourse course=factory.create("JavaCourse.class");
    }
}
///优化三//
//优点:不用强转,加泛型
public class CourseFactory {
    //规定传进来的值必须是ICourse的子类
    //单一职责原则
    public ICourse create(Class<? extends ICourse> clazz){
            try {
                if (null != clazz) {
                    return clazz.newInstance();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
    }
}
类图

优化二
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOL6sdJc-1647080017421)(D:\文件_Typora\1591582099612.png)]

优化三
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pb2Tinvf-1647080017421)(D:\文件_Typora\1591582117195.png)]

源码应用

calendar 不经常变化

package java.util;
public static Calendar getInstance()
{
    //传入时区,语言
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

private static Calendar createCalendar(TimeZone zone,Locale aLocale){}

logger

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zDfTLWRi-1647080017421)(D:\文件_Typora\1591352167390.png)]

适用场景

工厂类负责创建的对象较少

客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关系

优点

只需要传入一个正确的参数,就可以获得你所需要的对象,无需知道其创建的细节

缺点

工厂类的职责相对过重(违背单一职责原则),增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则

不易于扩展过于复杂的产品结构

工厂方法模式

Factory Method Pattern

是简单工厂模式的升级:简单工厂模式一旦发生升级,大规模增加,工厂类的职责会很重,不易于代码的扩展,所以产生工厂方法模式

简单工厂模式是产品的工厂,工厂方法模式是工厂的工厂

定义

工厂方法模式是指定义一个创建对象的接口,但让实现这个接口的类来觉得实例化哪个类,工厂方法让类的实例化推迟到子类中进行。起到调用作用。

属于创建型设计模式

实现

1.工厂本身也做一个抽象,先创建ICourseFactory接口:

public interface ICourseFactory{
    ICourse create();
}

2.再分别创建子工厂,JavaCourseFactory和PythonCourseFactory:

public class JavaCourseFactory implements ICourseFactory{
    public ICourse create(){
        return new JavaCourse();
    }
}
public class PythonCourseFactory implements ICourseFactory{
    public ICourse create(){
        return new PythonCourse();
    }
}

3.测试代码:

public static void main(String[] args){
    ICourseFactory factory=new PythonCourseFactory();
    ICourse course=factory.create();
    course.record();
    
    factory=new JavaCourseFactory();
    course=factory.create();
    course.record();
}
类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TcW6vaY9-1647080017421)(D:\文件_Typora\1591582146554.png)]

应用

logback中工厂方法模式的应用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i2FyVA5z-1647080017422)(D:\文件_Typora\1591582160192.png)]

适用场景
  1. 创建对象需要大量重复的代码
  2. 客户端(应用层)不依赖产品类实例如何被创建,实现等细节
  3. 一个类通过其子类来指定创建哪个对象
优点
  1. 加入新东西,工厂的实现逻辑不用改变,符合开闭原则,解决产品扩展问题。
  2. 用户只需关心产品对应的工厂,无需关心创建细节
缺点
  1. 类的个数容易过多,增加复杂度
  2. 增加了系统的抽象性和理解难度

抽象工厂模式

Abstract Factory Pattern

与工厂方法模式最大的区别:我们不负责创建工厂,是由用户创建,用户选哪个工厂就是那个工厂

简单工厂:产品的工厂

工厂方法:工厂的工厂

抽象工厂:复杂产品的工厂

定义

抽象工厂模式是指提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类

属于创建型设计模式

产品等级结构与产品族

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vxv0stgP-1647080017422)(D:\文件_Typora\1591582186483.png)]

相同颜色深浅的代表同一个产品族(品牌)
相同形状的代表同一个产品等级结构

同一个品牌会有同一条生产线,统一的标准和规范,同一个抽象工厂
同一个产品等级结构,国家会提供一个标准,如ISO标准

产品族:一系列的相关的产品,整合到一起有关联性
产品等级:同一个继承体系

需求

一个课程有了新的标准,每个课程不仅要提供课程的录播视频,而且还要提供老师的课堂笔记。相当于现在的业务变更为同一个课程不单纯是一个课程信息,要同时包含录播视频,课堂笔记甚至还要提供源码才能构成一个完整的课程。在产品等级中增加两个产品IVideo录播视频和INote课堂笔记

实现

IVideo接口:

public interface IVideo{
    void record();
}

INote接口:

public interface INote{
    void edit();
}

然后创建一个抽象工厂CourseFactory类:

如果创建之前或创建之后没有公共逻辑的话,就可以用接口
如果有公共逻辑,用抽象类

/**
在Spring中应用得最为广泛的一种设计模式
**/
public abstract class CourseFactory{
    //公共逻辑
    public void init(){
        System.out.println("初始化基础数据");
    }
    //必须有笔记,没笔记有不达标
    protected abstract INote createNote();
    protected abstract IVideo createVideo();
}

接下来,创建Java产品族,Java视频JavaVideo类:

public class JavaVideo implements IVideo{
    public void record(){
        System.out.println("录制Java视频");
    }
}

扩展产品等级Java课堂笔记JavaNote类:

public class JavaNote implements INote{
    public void edit(){
        System.out.println("编写Java笔记");
    }
}

创建Java产品族的具体工厂JavaCourseFactory:

public class JavaCourseFactory extends CourseFactory{
    public INote createNote(){
        super.init();
        return new JavaNote();
    }
    
    public IVideo createVideo(){
        super.init();
        return new JavaVideo;
    }
}

然后创建Python产品,Python视频PythonVideo类:

public class PythonVideo implements IVideo{
    public void record(){
        System.out.println("录制Python视频");
    }
}

扩展产品等级Python课堂笔记PythonNote类:

public class PythonNote implements IPython{
    public void edit(){
        System.out.println("编写Python笔记");
    }
}

创建Python产品族的具体工厂PythonCourseFactory:

public class PythonCourseFactory extends CourseFactory{
    public INote createNote(){
        super.init();
        return new PythonNote();
    }
    
    public IVideo createVideo(){
        super.init();
        return new PythonVideo;
    }
}

客户端调用

public static void main(String[] args){
    //符合单一职责原则
    CourseFactory factory=new JavaCourseFactory();
    factory.createNote().edit();
    factory.createVideo().record();
}
类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-13zGzCgD-1647080017422)(D:\文件_Typora\1591582242898.png)]

适用场景

客户端(应用层)不依赖于产品类实例如何被创建,实现等细节

强调的是一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。

提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现

优点

完全遵循单一职责原则,具体产品在应用层代码隔离,无需关心创建细节

将一个系列的产品族统一到一起创建

缺点

如果我们再继续扩展产品等级,将源码Source也加入到课程中,那么我们的代码从抽象工厂,到具体工厂要全部调整,很显然不符合开闭原则,因此也有缺点

  1. 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
  2. 增加了系统的抽象性和理解难度
  3. 慎用
  4. 不符合开闭原则
Pool

抽象工厂模式:有各种各样的产品

package org.jdbc.sqlhelper;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;


/**
 * 自定义连接池 getInstance()返回POOL唯一实例,第一次调用时将执行构造函数
 * 构造函数Pool()调用驱动装载loadDrivers()函数;连接池创建createPool()函数 loadDrivers()装载驱动
 * createPool()建连接池 getConnection()返回一个连接实例 getConnection(long time)添加时间限制
 * freeConnection(Connection con)将con连接实例返回到连接池 getnum()返回空闲连接数
 * getnumActive()返回当前使用的连接数
 *
 * @author Tom
 *
 */

public abstract class Pool {
	public String propertiesName = "connection-INF.properties";

	private static Pool instance = null; // 定义唯一实例

	/**
	 * 最大连接数
	 */
	protected int maxConnect = 100; // 最大连接数

	/**
	 * 保持连接数
	 */
	protected int normalConnect = 10; // 保持连接数

	/**
	 * 驱动字符串
	 */
	protected String driverName = null; // 驱动字符串

	/**
	 * 驱动类
	 */
	protected Driver driver = null; // 驱动变量


	/**
	 * 私有构造函数,不允许外界访问
	 */
	protected Pool() {
		try
		{
			init();
			loadDrivers(driverName);
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * 初始化所有从配置文件中读取的成员变量成员变量
	 *
	 */
	private void init() throws IOException {
		InputStream is = Pool.class.getResourceAsStream(propertiesName);
		Properties p = new Properties();
		p.load(is);
		this.driverName = p.getProperty("driverName");
		this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));
		this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));
	}

	/**
	 * 装载和注册所有JDBC驱动程序
	 *
	 * @param dri  接受驱动字符串
	 */
	protected void loadDrivers(String dri) {
		String driverClassName = dri;
		try {
			driver = (Driver) Class.forName(driverClassName).newInstance();
			DriverManager.registerDriver(driver);
			System.out.println("成功注册JDBC驱动程序" + driverClassName);
		} catch (Exception e) {
			System.out.println("无法注册JDBC驱动程序:" + driverClassName + ",错误:" + e);
		}
	}

	/**
	 * 创建连接池
	 */
	public abstract void createPool();

	/**
	 *
	 * (单例模式)返回数据库连接池 Pool 的实例
	 *
	 * @param driverName 数据库驱动字符串
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static synchronized Pool getInstance() throws IOException,
			InstantiationException, IllegalAccessException,
			ClassNotFoundException {

		if (instance == null) {
			instance.init();
			instance = (Pool) Class.forName("org.jdbc.sqlhelper.Pool")
					.newInstance();
		}
		return instance;
	}

	/**
	 * 获得一个可用的连接,如果没有则创建一个连接,且小于最大连接限制
	 *
	 * @return
	 */
	public abstract Connection getConnection();

	/**
	 *
	 * 获得一个连接,有时间限制
	 *
	 * @param time 设置该连接的持续时间(以毫秒为单位)
	 * @return
	 */
	public abstract Connection getConnection(long time);

	/**
	 * 将连接对象返回给连接池
	 *
	 * @param con 获得连接对象
	 */
	public abstract void freeConnection(Connection con);

	/**
	 *
	 * 返回当前空闲连接数
	 *
	 * @return
	 */
	public abstract int getnum();

	/**
	 *
	 * 返回当前工作的连接数
	 *
	 * @return
	 */
	public abstract int getnumActive();

	/**
	 *
	 * 关闭所有连接,撤销驱动注册(此方法为单例方法)
	 */
	protected synchronized void release() {
		// /撤销驱动
		try {
			DriverManager.deregisterDriver(driver);
			System.out.println("撤销JDBC驱动程序 " + driver.getClass().getName());
		} catch (SQLException e) {
			System.out
					.println("无法撤销JDBC驱动程序的注册:" + driver.getClass().getName());
		}
	}
}
DBConnectionPool

具体的实现

package org.jdbc.sqlhelper;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
import java.util.Date;


/**
 * 数据库链接池管理类
 *
 * @author Tom
 *
 */
public final class DBConnectionPool extends Pool {
	private int checkedOut; //正在使用的连接数
	/**
	 * 存放产生的连接对象容器
	 */
	private Vector<Connection> freeConnections = new Vector<Connection>(); //存放产生的连接对象容器

	private String passWord = null; // 密码

	private String url = null; // 连接字符串

	private String userName = null; // 用户名

	private static int num = 0;// 空闲连接数

	private static int numActive = 0;// 当前可用的连接数

	private static DBConnectionPool pool = null;// 连接池实例变量

	/**
	 * 产生数据连接池
	 * @return
	 */
	public static synchronized DBConnectionPool getInstance()
	{
		if(pool == null)
		{
			pool = new DBConnectionPool();
		}
		return pool;
	}

	/**
	 * 获得一个 数据库连接池的实例
	 */
	private DBConnectionPool() {
		try
		{
			init();
			for (int i = 0; i < normalConnect; i++) { // 初始normalConn个连接
				Connection c = newConnection();
				if (c != null) {
					freeConnections.addElement(c); //往容器中添加一个连接对象
					num++; //记录总连接数
				}
			}
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	/**
	 * 初始化
	 * @throws IOException
	 *
	 */
	private void init() throws IOException
	{
		InputStream is = DBConnectionPool.class.getResourceAsStream(propertiesName);
		Properties p = new Properties();
		p.load(is);
		this.userName = p.getProperty("userName");
		this.passWord = p.getProperty("passWord");
		this.driverName = p.getProperty("driverName");
		this.url = p.getProperty("url");
		this.driverName = p.getProperty("driverName");
		this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));
		this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));
	}

	/**
	 * 如果不再使用某个连接对象时,可调此方法将该对象释放到连接池
	 *
	 * @param con
	 */
	public synchronized void freeConnection(Connection con) {
		freeConnections.addElement(con);
		num++;
		checkedOut--;
		numActive--;
		notifyAll(); //解锁
	}

	/**
	 * 创建一个新连接
	 *
	 * @return
	 */
	private Connection newConnection() {
		Connection con = null;
		try {
			if (userName == null) { // 用户,密码都为空
				con = DriverManager.getConnection(url);
			} else {
				con = DriverManager.getConnection(url, userName, passWord);
			}
			System.out.println("连接池创建一个新的连接");
		} catch (SQLException e) {
			System.out.println("无法创建这个URL的连接" + url);
			return null;
		}
		return con;
	}

	/**
	 * 返回当前空闲连接数
	 *
	 * @return
	 */
	public int getnum() {
		return num;
	}

	/**
	 * 返回当前连接数
	 *
	 * @return
	 */
	public int getnumActive() {
		return numActive;
	}

	/**
	 * (单例模式)获取一个可用连接
	 *
	 * @return
	 */
	public synchronized Connection getConnection() {
		Connection con = null;
		if (freeConnections.size() > 0) { // 还有空闲的连接
			num--;
			con = (Connection) freeConnections.firstElement();
			freeConnections.removeElementAt(0);
			try {
				if (con.isClosed()) {
					System.out.println("从连接池删除一个无效连接");
					con = getConnection();
				}
			} catch (SQLException e) {
				System.out.println("从连接池删除一个无效连接");
				con = getConnection();
			}
		} else if (maxConnect == 0 || checkedOut < maxConnect) { // 没有空闲连接且当前连接小于最大允许值,最大值为0则不限制
			con = newConnection();
		}
		if (con != null) { // 当前连接数加1
			checkedOut++;
		}
		numActive++;
		return con;
	}

	/**
	 * 获取一个连接,并加上等待时间限制,时间为毫秒
	 *
	 * @param timeout  接受等待时间(以毫秒为单位)
	 * @return
	 */
	public synchronized Connection getConnection(long timeout) {
		long startTime = new Date().getTime();
		Connection con;
		while ((con = getConnection()) == null) {
			try {
				wait(timeout); //线程等待
			} catch (InterruptedException e) {
			}
			if ((new Date().getTime() - startTime) >= timeout) {
				return null; // 如果超时,则返回
			}
		}
		return con;
	}

	/**
	 * 关闭所有连接
	 */
	public synchronized void release() {
		try {
			//将当前连接赋值到 枚举中
			Enumeration allConnections = freeConnections.elements();
			//使用循环关闭所用连接
			while (allConnections.hasMoreElements()) {
				//如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素
				Connection con = (Connection) allConnections.nextElement();
				try {
					con.close();
					num--;
				} catch (SQLException e) {
					System.out.println("无法关闭连接池中的连接");
				}
			}
			freeConnections.removeAllElements();
			numActive = 0;
		} finally {
			super.release();
		}
	}

	/**
	 * 建立连接池
	 */
	public void createPool() {

		pool = new DBConnectionPool();
		if (pool != null) {
			System.out.println("创建连接池成功");
		} else {
			System.out.println("创建连接池失败");
		}
	}
}
ConManager
DataBaseCmd

总结

简单工厂:产品的工厂

工厂方法:工厂的工厂

抽象工厂:复杂产品的工厂

Spring

Spring最顶层:AbstractFactory

很多实现:AnnotationApplicationContext,Xml…

为什么Spring要用抽象工厂:
首先,不管是Xml还是Annotation,最终都是Bean封装,属于同一个产品等级,但是呢,去解析这些Bean的方法多种多样(Xml,Annotation),所以把每一种逻辑封装到具体的方法里,但最终又需要一个统一的管理体系(AbstractFactory)

抽象工厂不符合开闭原则,但仍然用,因为变化少,代价小

作业

问:1、工厂类一定需要将构造方法私有化吗,为什么?

工厂类一般和单例模式结合,当工厂类需要单例时,构造函数私有化,否则可以不私有化

问:2、用工厂模式设计支付业务场景,包含跨境支付,支付宝、微信、银联支付,并画出类图。

策略模式画

单例模式

Singleton Pattern

课程目标

1、掌握单例模式的应用场景。

2、掌握IDEA环境下的多线程调试方式。

3、掌握保证线程安全的单例模式策略。

4、掌握反射暴力攻击单例解决方案及原理分析。

5、序列化破坏单例的原理及解决方案。

6、掌握常见的单例模式写法。

定义

单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点

隐藏其所有的构造方法

属于创建型模式

为什么需要

防止出现重复的创建,类本来就独有一份,多份会出问题,不能有多分配置文件

适用场景

ServletContext,ServletConfig,ApplicationContext,DBPool

常见写法

  1. 饿汉式单例
  2. 懒汉式单例
  3. 注册式单例
  4. ThreadLocal单例

优点

在内存中只有一个实例,减少了内存开销。

可以避免对资源的多重占用。

设置全局访问点,严格控制访问。

缺点

没有接口,扩展困难,不是面向接口,面向抽象编程,所有逻辑在自己内部实现,违背开闭原则

如果要扩展单例对象,只有修改代码,没有其他途径

饿汉式单例

饿汉式单例在类加载的时候就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没有出现以前就实例化了,不可能存在访问安全问题

标准代码

public class HungrySingleton{
    private static final HungrySingleton hungrySingleton=new HungrySingleton();
    //私有化构造方法
    private HungrySingleton(){}
    
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

静态代码块

//饿汉式静态块单例模式
public class HungryStaticSongleton{
    //先静态,后动态
    //先属性,后方法
    //先上后下
    private static fianl HungryStaticSingleton hungrySingleton;
    static{
        hungrySingleton=new HungryStaticSingleton();
    }
    private HungryStaticSingleton(){}
    public static HungryStaticSingleton getInstance(){
        return hungrySingleton();
    }
    
}

为什么这么写

static 在类初始化时就加载了,并且创建

适用场景

适用于单例对象比较少的情况。这样写可以保证绝对线程安全,执行效率比较高。

不适用场景

大量使用单例

优点

执行效率高,性能高,没有任何锁

缺点

它的缺点也很明显,就是所有对象类加载的时候就实例化。这样,如果系统中有大批量的单例对象存在,那系统初始化时就会造成大量的内存浪费。“占着茅坑不拉屎”,怎么优化呢?——懒汉模式

懒汉式单例

被外部类调用时才创建实例

简单实现

public class LazySimpleSingleton{
    private LazySimpleSingleton(){}
    //静态块,公共内存区域
    private static LazySimpleSingleton lazy=null;
    public static LazySimpleSingleton getInstance(){
        if(lazy==null){
            lazy=new LazySimpleSingleton();
        }
        return lazy;
    }
}

优点

节省了内存

缺点

线程不安全(两个线程同时调用Instance方法)

测试类

线程类
public class ExectorThread implements Runnable{
    @Override
    public void run(){
        LazySimpleSingleton singleton=LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+":"+singleton);
    }
}
客户端
public class LazySimpleSingletonTest{
    public static void main(String[] args){
        Thread t1=new Thread(new ExectorThread());
        Thread t2=new Thread(new ExectorThread());
        t1.start();
        t2.start();
        System.out.println("End");
    }
}
运行结果

同一个实例:

1. 正常顺序执行
2. 后者覆盖前者情况,假结果,其实创建了两个线程,只不过调用了两次构造函数,第一个被覆盖

不同的实例:

同时进入条件,按顺序返回

线程断点调试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X7X9cpDy-1647080017423)(D:\文件_Typora\1591582286406.png)]

优化一

synchronized 关键字

public class LazySimpleSingleton{
    private LazySimpleSingleton(){}
    //静态块,公共内存区域
    private static LazySimpleSingleton lazy=null;
    public synchronized static LazySimpleSingleton getInstance(){
        if(lazy==null){
            lazy=new LazySimpleSingleton;
        }
        return lazy;
    }
}

第一个进程进去后,第二个进去不,为Monitor

新缺点

在线程数量比较多的情况下,会造成大批线程阻塞,从而使程序性能大幅下降

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kN2i7XoJ-1647080017423)(D:\文件_Typora\1591582300351.png)]

优化二

双重检查锁,不在外面排队,而在里面排队

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrFGKgpZ-1647080017423)(D:\文件_Typora\1591582309350.png)]

public class LazyDoubleCheckSingleton{
    private volatile static LazyDoubleCheckSingleton lazy=null;
    private LazyDoubleCheckSingleton(){}
    
    public static LazyDoubleCheckSingleton getInstance(){
        //检查是否要阻塞,等于空才阻塞
        if(lazy==null){
            synchronized (LazyDoubleCheckSingleton.class){
                //再判断,避免两个线程同时阻塞在第一个if(){}中
                if(lazy==null){
                    lazy=new LazyDoubleCheckSingleton();
                    //指令重排序的问题
                    //lazy=new LazyDoubleCheckSingleton();有两步
                    //一:new 实例  二:lazy指针指向
                    //解决方法 lazy 前加 volatile  关键字
                    //1、分配内存给这个对象
                    //2,初始化对象
                    //3,设置 lazy 指向刚分配的内存地址
                }
            }
        }
        return lazy;
    }
}

当第一个线程调用getInstance()方法时,第二个线程也可以调用。当第一个线程执行到synchronized时会上锁,第二个线程就会变成MONITOR状态,出现阻塞。此时,阻塞并不是基于整个LazySimpleSingleton类的阻塞,而是在getInstance()方法内部的阻塞,只要逻辑不太负责,对于调用者而言是感知不到的。

优点:性能高,线程安全

新缺点

但是,用到synchronized关键字总归要上锁,对程序性能还是存在一定影响的。双if使可读性难度加大

难道就真的没有更好的方案吗?当然有。我们可以从类初始化的角度考虑,看下列代码,采用静态内部类的方式

优化三

静态内部类,利用Java语法优势

静态的成员变量,在加载时就会被分配空间,但静态内部类不一样,用的时候才会分配内存

类加载时,加载ClassPath下的LazyInnerClassSingleton.class文件
而内部类在ClassPath下的形式是LazyInnerClassSingleton$LazyHolder.class
也就是说,一开始不会扫描内部类,当需要内部类时才加载

//这种形式兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题
//完美地屏蔽了这两个缺点
public class LazyInnerClassSingleton{
    //使用 LazyInnerClassGeneral的时候,默认会先初始化内部类
    //如果没有使用,则内部类是不加载的
    private LazyInnerClassSingleton(){}
    
    //每一个关键字都不多余,static是为了使单例的空间共享,保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
        //在返回结果以前,一定会先加载内部类
        return LazyHolder.LAZY;
    }
    
    //默认不加载,用的时候才会分配内存
    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY=new LazyInnerClassSingleton();
    }
}

写法优雅,很好的利用了Java中的语法特点,这种方式兼顾了饿汉式单例模式的内存浪费问题和synchronized的性能问题。内部类一定是要在方法调用之前初始化,巧妙地避免了线程安全问题。

新缺点

构造方法仅仅是加上了private,而没有考虑反射,可以被反射破坏

优化四

构造方法中加判断

测试

public class LazyInnerClassSingletonTest{
    public static void main(String[] args){
        try{
            //进行破坏
            Class<?> clazz=LazyInnerClassSingleton.class;
            
            //通过反射获取私有的构造方法
            //拿到构造器
            Constructor c=clazz.getDeclaredConstructor(null);
            //设置强制访问
            c.setAccessible(true);
            
            //暴力初始化
            Object o1=c.newInstance();
            
            //调用了两次构造方法,相当于“new”了两次,犯了原则性错误
            Object o2=c.newInstance();
            
            System.out.println(o1==o2);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

结果为false

优化

public class LazyInnerClassSingleton{
    //使用LazyInnerClassGeneral的时候,默认会先初始化内部类
    //如果没使用,则内部类不会加载
    private LazyInnerClassSingleton(){
        if(LazyHolder.LAZY!=null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }
    
     //每一个关键字都不多余,static是为了使单例的空间共享,保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
        //在返回结果以前,一定会先加载内部类
        return LazyHolder.LAZY;
    }
    
    //默认不加载,用的时候才会分配内存
    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY=new LazyInnerClassSingleton();
    }
}

新缺点

不优雅,可读性低,还是有可能被破坏

注册式单例

register

将每一个实例都缓存到统一的容器中,使用唯一标识获取实例

既优雅,又高效,又不会被反射破坏,又线程安全。《Java Effictive》

枚举式单例

实现
//继承了java.lang Enum
public enum EnumSingleton{
    INSTANCE;
    private Object data;
    public Object getData(){
        return data;
    }
    public void setData(Object data){
        this.data=data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}
测试
public class EnumSingletonTest{
    public static void main(String[] args){
        try{
            EnumSingleton instance1=null;
            
            EnumSingleton instance2=EnumSingleton.getInstance();
            //给这个单例设置对象
            instance2.setData(new Object());
            
            Class clazz=EnumSingleton.class;
            //java.lang.NoSuchMethodException:
            Constructor c = clazz.getDeclaredConstructor();
            //测试加参数后
            Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
            //private com.lewis.zookeeper_demo.EnumSingleton(java.lang.String,int)
            System.out.println(c);
            c.setAccessible(true);
            Object o=c.newInstance();
            //java.lang.IllegalArgumentException: Cannot reflectively create enum objects
            System.out.println(o);
            
            
            
            
            FileOutputStream fos=new FileOutputStream("EnumSingleton.obj");
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            oos.writeObject(instance2);
            oos.flush();
            oos.close();
            
            FileInputStream fis=new FileInputStream("EnumSingleton.obj");
            ObjectInputStream ois=new ObjectInputStream(fis);
            instance1=(EnumSingleton)ois.readObject();
            ois.close();
            
            System.out.println(instance1.getData());
            System.out.println(instance2.getData());
            System.out.println(instance1.getData()==instance2.getData());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

结果:

java.lang.IllegalAccessException:

java.lang.Object@5fd0d5ae

java.lang.Object@5fd0d5ae

true

凭什么

查看Enum源码

为啥报错一

java.lang.NoSuchMethodException:

它有构造方法,但没有无参的构造方法

package java.lang.reflect;
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to
     * enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    //String 是作为Key
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
}
为啥报错二

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

Enum从jdk的底层就确定了不能用反射创建Enum

避免了自己写if(),交给官方写

package java.lang.reflect;
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    @CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
    IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        //如果修饰符是枚举的话,就不能用反射创建
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }
}
为啥线程安全

在类声明INSTANCE时就已经将INSTANCE创建好了,每一个枚举都有一个ValueOf方法,通过此方法,就可以找到枚举Key所对应的对象值

package java.lang.reflect;
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        //通过name找
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
}

如何通过name找到值得呢?进入enumType.enumConstantDirectory()

package java.lang;
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
	/**
     * Returns a map from simple name to enum constant.  This package-private
     * method is used internally by Enum to implement
     * {@code public static <T extends Enum<T>> T valueOf(Class<T>, String)}
     * efficiently.  Note that the map is returned by this method is
     * created lazily on first use.  Typically it won't ever get created.
     */
    Map<String, T> enumConstantDirectory() {
        if (enumConstantDirectory == null) {
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(
                    getName() + " is not an enum type");
            Map<String, T> m = new HashMap<>(2 * universe.length);
            for (T constant : universe)
                m.put(((Enum<?>)constant).name(), constant);
            //枚举的常量字典在Class类中声明,是一个Map,是一个注册式单例的容器
            //private volatile transient Map<String, T> enumConstantDirectory = null;
            enumConstantDirectory = m;
        }
        return enumConstantDirectory;
    }                                  
}

枚举的常量字典在Class类中声明,是一个Map,是一个注册式单例的容器
private volatile transient Map<String, T> enumConstantDirectory = null;

也就是说,当我们声明枚举的时候,它就吧INSTANCE当做常量放到枚举的常量字典Map中存着,很像饿汉式单例,不管用不用都存着。也就和饿汉式单例一样,不存在线程安全问题了

优点

优雅

缺点

和饿汉一样,某些情况下会造成内存浪费,不能大批量创建对象

Spring肯定不会用,而是借鉴后改良,出现了Spring IOC容器

容器式单例

实现
public class ContainerSingleton{
    private ContainerSingleton(){}
    private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
    public static Object getBean(String className){
        synchronized (ioc){
            //有就使用,没有就创建
            if(!ioc.containsKay(className)){
                Object instance=null;
                try{
                    instance=Class.froName(className).newInstance();
                    ioc.put(className,instance);
                }catch(Exception e){
                    e.printStackTrace();
                }
                return instance;
            }else{
                return ioc.get(className);
            }
        }
    }
}
测试
public class ContainerSingletonTest{
    public static void main(String[] args){
        //工厂模式
        Object instance1=ContainerSingleton.getInstance("com.***.Pojo");
        Object instance2=ContainerSingleton.getInstance("com.***.Pojo");
    	System.out.println(instance1==instance2);
    }
}
package com.***.Pojo;

public class Pojo{
    
}
优点

适用于需要大量创建单例对象的场景,便于管理

缺点

没有防止反射,非线程安全,序列化时单例会被破坏

Spring的实现
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
    implements AutowireCapableBeanFactory{
    private final Map<String,BeanWrapper> factoryBeanInstance=new ConcurrentHashMap<>(16);
    ...
}

序列化破坏单例

一个单例对象创建好后,有时候需要将其对象序列化然后写入磁盘,下次使用时再从磁盘读取对象并进行反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建。如果序列化的目标对象为单例模式,就违背了单例模式的初衷,相当于破坏了单例

实现

//反序列化导致破坏单例模式
public class SeriableSingleton implements Serializable{
    //序列化
    //序列化就是把内存中的状态通过转换成字节码的形式
    //从而转换一个I/O流,写入其他地方(可以是磁盘,网络I/O)
    //内存中的状态会永久保存下来,变成一个文件
    
    //场景
    //Redis是一个内存中间件,把全部信息放在内存中,读写快,但不安全(断电)
    //所以定时得持久化到磁盘上
    
    //反序列化
    //反序列化就是将已经持久化的字节码内容转换成I/O流
    //通过I/O流的读取,进而将读取的内容转换成Java对象
    //在转换过程中会重新创建对象new
    //饿汉式单例写法
    public final static SeriableSingleton INSTANCE=new SeriableSingleton();
    private SeriableSingleton(){}
    
    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }
}

测试

public class SeriableSingletonTest{
    public static void main(String[] args){
        SeriableSingleton s1=null;
        //此实例在内存中
        SeriableSingleton s2=SeriableSingleton.getInstance();
        
        FileOutputStream fos=null;
        try{
            //将 s2,通过输出流,输出到磁盘上
            //此文件 "SeriableSingleton.obj" 与 target 同级
            fos=new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();
            
            //通过输入流,读到内存中,从而得到对象,赋值给 s1
            FileInputStream fis=new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois=new ObjectInputStream(fis);
            s1=(SeriableSingleton)ois.readObject();
            ois.close();
            
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1==s2);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

结果:

false

缺点

从结果看出,反序列化的对象和手动创建的对象是不一致的,实例化了两次,违背了单例模式的初衷

优化

增加readResolve()方法即可

public class SeriableSingleton implements Serializable{
    public final static SeriableSingleton INSTANCE=new SeriableSingleton();
    private SeriableSingleton(){}
    
    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }
    
    //桥接模式,约定好,就是一个桥,把这个维度(SeriableSingleton)和JDK里面的维度进行连接
    //有就调,没有就不调,没有任何继承
    private Object readResolve(){
        return INSTANCE;
    }
}

凭什么

查看 s1=(SeriableSingleton)ois.readObject(); 源码

package java.io;
public class ObjectInputStream
    extends InputStream implements ObjectInput, ObjectStreamConstants
{

    public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) {
            return readObjectOverride();
        }

        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(false);
            handles.markDependency(outerHandle, passHandle);
            ClassNotFoundException ex = handles.lookupException(passHandle);
            if (ex != null) {
                throw ex;
            }
            if (depth == 0) {
                vlist.doCallbacks();
            }
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
    }    
}

调用 Object obj = readObject0(false);

package java.io;
public class ObjectInputStream
    extends InputStream implements ObjectInput, ObjectStreamConstants
{
    /**
     * Underlying readObject implementation.
     */
    private Object readObject0(boolean unshared) throws IOException {
        boolean oldMode = bin.getBlockDataMode();
        if (oldMode) {
            int remain = bin.currentBlockRemaining();
            if (remain > 0) {
                throw new OptionalDataException(remain);
            } else if (defaultDataEnd) {
                /*
                 * Fix for 4360508: stream is currently at the end of a field
                 * value block written via default serialization; since there
                 * is no terminating TC_ENDBLOCKDATA tag, simulate
                 * end-of-custom-data behavior explicitly.
                 */
                throw new OptionalDataException(true);
            }
            bin.setBlockDataMode(false);
        }

        byte tc;
        while ((tc = bin.peekByte()) == TC_RESET) {
            bin.readByte();
            handleReset();
        }

        depth++;
        totalObjectRefs++;
        try {
            switch (tc) {
                case TC_NULL:
                    return readNull();

                case TC_REFERENCE:
                    return readHandle(unshared);

                case TC_CLASS:
                    return readClass(unshared);

                case TC_CLASSDESC:
                case TC_PROXYCLASSDESC:
                    return readClassDesc(unshared);

                case TC_STRING:
                case TC_LONGSTRING:
                    return checkResolve(readString(unshared));

                case TC_ARRAY:
                    return checkResolve(readArray(unshared));

                case TC_ENUM:
                    //检查是否有写Reslove方法
                    return checkResolve(readEnum(unshared));

                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared));

                case TC_EXCEPTION:
                    IOException ex = readFatalException();
                    throw new WriteAbortedException("writing aborted", ex);

                case TC_BLOCKDATA:
                case TC_BLOCKDATALONG:
                    if (oldMode) {
                        bin.setBlockDataMode(true);
                        bin.peek();             // force header read
                        throw new OptionalDataException(
                            bin.currentBlockRemaining());
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected block data");
                    }

                case TC_ENDBLOCKDATA:
                    if (oldMode) {
                        throw new OptionalDataException(true);
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected end of block data");
                    }

                default:
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }
}

进入 return checkResolve(readOrdinaryObject(unshared));

package java.io;
public class ObjectInputStream
    extends InputStream implements ObjectInput, ObjectStreamConstants
{
/**
     * Reads and returns "ordinary" (i.e., not a String, Class,
     * ObjectStreamClass, array, or enum constant) object, or null if object's
     * class is unresolvable (in which case a ClassNotFoundException will be
     * associated with object's handle).  Sets passHandle to object's assigned
     * handle.
     */
    private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {
            throw new InternalError();
        }

        ObjectStreamClass desc = readClassDesc(false);
        desc.checkDeserialize();

        Class<?> cl = desc.forClass();
        if (cl == String.class || cl == Class.class
                || cl == ObjectStreamClass.class) {
            throw new InvalidClassException("invalid class descriptor");
        }

        Object obj;
        try {
            //如果没有,就创建一个newInstance()方法
            obj = desc.isInstantiable() ? desc.newInstance() : null;
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }

        passHandle = handles.assign(unshared ? unsharedMarker : obj);
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandle, resolveEx);
        }

        if (desc.isExternalizable()) {
            readExternalData((Externalizable) obj, desc);
        } else {
            readSerialData(obj, desc);
        }

        handles.finish(passHandle);

        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            //判断是有有ReadResovler
            desc.hasReadResolveMethod())
        {
            //如果有,则用反射机制调用
            //拿到返回值
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                // Filter the replacement object
                if (rep != null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1);
                    }
                }
                //最终返回 rep
                handles.setObject(passHandle, obj = rep);
            }
        }

        return obj;
    }
}    

进入Object rep = desc.invokeReadResolve(obj);

package java.io;
public class ObjectStreamClass implements Serializable {
	/**
     * Invokes the readResolve method of the represented serializable class and
     * returns the result.  Throws UnsupportedOperationException if this class
     * descriptor is not associated with a class, or if the class is
     * non-serializable or does not define readResolve.
     */
    Object invokeReadResolve(Object obj)
        throws IOException, UnsupportedOperationException
    {
        requireInitialized();
        if (readResolveMethod != null) {
            try {
                //用反射机制调用readResolver方法
                return readResolveMethod.invoke(obj, (Object[]) null);
            } catch (InvocationTargetException ex) {
                Throwable th = ex.getTargetException();
                if (th instanceof ObjectStreamException) {
                    throw (ObjectStreamException) th;
                } else {
                    throwMiscException(th);
                    throw new InternalError(th);  // never reached
                }
            } catch (IllegalAccessException ex) {
                // should not occur, as access checks have been suppressed
                throw new InternalError(ex);
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }
}

总之,如果写了readResolver方法,就会反射拿到对象,不会再去创建

ThreadLocal单例

不能保证其创建的对象是全局唯一的,但能保证线程内部的全局唯一,且天生线程安全

实现

public class ThreadLocalSingleton{
    private ThreadLocalSingleton(){}
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = 
        //匿名实现
        new ThreadLocal<ThreadLocalSingleton>(){
        @Override
        protected ThreadLocalSingleton initialValue(){
            return new ThreadLocalSingleton();
        }
    };
    
    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
}

测试

public static void main(String[] args){
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    
    Thread t1=new Thread(new ExectorThread());
    Thread t2=new Thread(new ExectorThread());
    t1.start();
    t2.start();
    System.out.println("End");
}
package com.lewis.zookeeper_demo;

public class ExectorThread implements Runnable{
    @Override
    public void run(){
        //LazySimpleSingleton singleton=LazySimpleSingleton.getInstance();
        ThreadLocalSingleton instance=ThreadLocalSingleton.getInstance();
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(Thread.currentThread().getName()+":"+singleton);
    }
}

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RiQz1F1C-1647080017423)(D:\文件_Typora\1591854594492.png)]

前5个全部一样

End

后两个一样

ThreadLocalSingleton是基于线程的,而在同一个Main线程类,它获得的对象是相同的
而在同一个ExectorThread线程中,获得的对象是相同的
但是出了线程就不一样了

凭什么

进入ThreadLocal

package java.lang;
public class ThreadLocal<T> {
    public T get() {
        Thread t = Thread.currentThread();
        //容器
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //this 是当前对象所在的线程
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
}

ThreadLocalMap.Entry e = map.getEntry(this);
this是当前对象所在的线程
所以线程内都一样

在源码中的应用

Runtime

public class Runtime{
    private static Runtime currentRuntime=new Runtime();
    public static Runtime getRuntime(){
        return currentRuntime;
    }
    private Runtime(){}
    ...
}

AbstractFactoryBean

(Spring)

getObject()方法,之后进入getEarlySingletonInstance()

package org.springframework.beans.factory.config;
public abstract class AbstractFactoryBean<T>
		implements FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
	@Override
	public final T getObject() throws Exception {
        //判断对象是否是单例
		if (isSingleton()) {
             //判断此单例是否初始化,是的话就用,不是就创建
		    return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
		}
		else {
			return createInstance();
		}
	}
    
    private T getEarlySingletonInstance() throws Exception {
		Class<?>[] ifcs = getEarlySingletonInterfaces();
		if (ifcs == null) {
			throw new FactoryBeanNotInitializedException(
					getClass().getName() + " does not support circular references");
		}
		if (this.earlySingletonInstance == null) {
             //判断是否要代理对象
			this.earlySingletonInstance = (T) Proxy.newProxyInstance(
					this.beanClassLoader, ifcs, new EarlySingletonInvocationHandler());
		}
		return this.earlySingletonInstance;
	}
}

ErrorContext

(Mybatis)

ThreadLocal应用场景

package org.apache.ibatis.executor;
public class ErrorContext {
    private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
    //构造方法私有化
    private ErrorContext() {}
    //instance方法
    public static ErrorContext instance() {
        //LOCAL即ThreadLocal,get看有没有对象
        ErrorContext context = LOCAL.get();
        if (context == null) {
          context = new ErrorContext();
          LOCAL.set(context);
        }
        return context;
  	}
}

好处:

让MyBatis每一个操作线程之间的错误上下文就能够很好的隔离,各自能够拿到各自的错误信息,不会穿插起来

知识重点

  1. 私有化构造器
  2. 保证线程安全
  3. 延迟加载
  4. 防止序列化和反序列化破坏单例
  5. 防止反射攻击单例

作业

问:解决容器式单例的线程安全问题。

备选项:用双重检查锁,直接加锁

从Spring源码中找答案,AbstractBeanFactory的getBean()方法

package org.springframework.beans.factory.support;

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    //getBean()方法
	@Override
    //获取IOC容器中指定名称和类型的Bean
	public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
		//doGetBean才是真正向IoC容器获取被管理Bean的过程
		return doGetBean(name, requiredType, null, false);
	}
}

AbstractBeanFactory的doGetBean(name, requiredType, null, false)方法

package org.springframework.beans.factory.support;

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	//真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		//根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖
		//如果指定的是别名,将别名转换为规范的Bean名称
		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		//先从缓存中取是否已经有被创建过的单态类型的Bean
		//对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
		Object sharedInstance = getSingleton(beanName);
		//IOC容器创建单例模式Bean实例对象
        //判断单例对象是否为空
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				//如果指定名称的Bean在容器中已有单例模式的Bean被创建
				//直接返回已经创建的Bean
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + 
                                 beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + 
                                 beanName + "'");
				}
			}
			//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
			//注意:BeanFactory是管理容器中Bean的工厂,而FactoryBean是
			//创建创建对象的工厂Bean,两者之间有区别
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			//缓存没有正在创建的单例模式Bean
			//缓存中已经有已经创建的原型模式Bean
			//但是由于循环引用的问题导致实例化对象失败
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			//对IOC容器中是否存在指定名称的BeanDefinition进行检查,首先检查是否
			//能在当前的BeanFactory中获取的所需要的Bean,如果不能则委托当前容器
			//的父级容器去查找,如果还是找不到则沿着容器的继承体系向父级容器查找
			BeanFactory parentBeanFactory = getParentBeanFactory();
			//当前容器的父级容器存在,且当前容器中不存在指定名称的Bean
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				//解析指定Bean名称的原始名称
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					//委派父级容器根据指定名称和显式的参数查找
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					//委派父级容器根据指定名称和类型查找
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

			//创建的Bean是否需要进行类型验证,一般不需要
			if (!typeCheckOnly) {
				//向容器标记指定的Bean已经被创建
				markBeanAsCreated(beanName);
			}

			try {
				//根据指定Bean名称获取其父级的Bean定义
				//主要解决Bean继承时子类合并父类公共属性问题
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				//获取当前Bean所有依赖Bean的名称
				String[] dependsOn = mbd.getDependsOn();
				//如果当前Bean有依赖Bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), 
                                                            beanName,
									"Circular depends-on relationship between '" + 
                                                            beanName + "' and '" + dep + "'");
						}
						//递归调用getBean方法,获取当前Bean的依赖Bean
						registerDependentBean(dep, beanName);
						//把被依赖Bean注册给当前依赖的Bean
						getBean(dep);
					}
				}

				// Create bean instance.
				//创建单例模式Bean的实例对象
				if (mbd.isSingleton()) {
					//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							//显式地从容器单例模式Bean缓存中清除实例对象
							destroySingleton(beanName);
							throw ex;
						}
					});
					//获取给定Bean的实例对象
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				//IOC容器创建原型模式Bean实例对象
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					//原型模式(Prototype)是每次都会创建一个新的对象
					Object prototypeInstance = null;
					try {
						//回调beforePrototypeCreation方法,默认的功能是注册当前创建的原型对象
						beforePrototypeCreation(beanName);
						//创建指定Bean对象实例
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						//回调afterPrototypeCreation方法,默认的功能告诉IOC容器指定Bean的原型对象不再创建
						afterPrototypeCreation(beanName);
					}
					//获取给定Bean的实例对象
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				//要创建的Bean既不是单例模式,也不是原型模式,则根据Bean定义资源中
				//配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中
				//比较常用,如:request、session、application等生命周期
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					//Bean定义资源中没有配置生命周期范围,则Bean定义不合法
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						//这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						//获取给定Bean的实例对象
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		//对创建的Bean实例对象进行类型检查
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}
}

进入bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

package org.springframework.beans.factory.support;

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		//容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean,
		//也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象,
		//如果调用本身就想获得一个容器的引用,则指定返回这个工厂Bean实例对象
		//如果指定的名称是容器的解引用(dereference,即是对象本身而非内存地址),
		//且Bean实例也不是创建Bean实例对象的工厂Bean
		if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		//如果Bean实例不是工厂Bean,或者指定名称是容器的解引用,
		//调用者向获取对容器的引用,则直接返回当前的Bean实例
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		//处理指定名称不是容器的解引用,或者根据名称获取的Bean实例对象是一个工厂Bean
		//使用工厂Bean创建一个Bean的实例对象
		Object object = null;
		if (mbd == null) {
			//从Bean工厂缓存中获取给定名称的Bean实例对象
			object = getCachedObjectForFactoryBean(beanName);
		}
		//让Bean工厂生产给定名称的Bean对象实例
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			//如果从Bean工厂生产的Bean是单态模式的,则缓存
			if (mbd == null && containsBeanDefinition(beanName)) {
				//从容器中获取指定名称的Bean定义,如果继承基类,则合并基类相关属性
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			//如果从容器得到Bean定义信息,并且Bean定义信息不是虚构的,
			//则让工厂Bean生产Bean实例对象
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			//调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,
			//实现工厂Bean生产Bean对象实例的过程
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}
}

进入object = getObjectFromFactoryBean(factory, beanName, !synthetic);

package org.springframework.beans.factory.support;

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
    //容器
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
    
    //Bean工厂生产Bean实例对象
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		//Bean工厂是单态模式,并且Bean工厂缓存中存在指定名称的Bean实例对象
		if (factory.isSingleton() && containsSingleton(beanName)) {
			//多线程同步,以防止数据不一致
			synchronized (getSingletonMutex()) {
				//直接从Bean工厂缓存中获取指定名称的Bean实例对象
				Object object = this.factoryBeanObjectCache.get(beanName);
				//Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象
				if (object == null) {
					//调用Bean工厂的getObject方法生产指定Bean的实例对象
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
						}
						//将生产的实例对象添加到Bean工厂缓存中
						this.factoryBeanObjectCache.put(beanName, object);
					}
				}
				return object;
			}
		}
		//调用Bean工厂的getObject方法生产指定Bean的实例对象
		else {
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}
    
}

答案:双重检查锁

原型模式

Prototype Pattern

课程目标

1、掌握原型模式和建造者模式的应用场景

2、掌握原型模式的浅克隆和深克隆的写法。

3、掌握建造者模式的基本写法。

4、了解克隆是如何破坏单例的。

5、了解原型模式的优、缺点

6、掌握建造者模式和工厂模式的区别。

定义

原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

把复制的过程封装起来,核心在于拷贝对象,调用者不需要知道任何创建细节,不调用构造函数

对不通过new关键字,而是通过对象拷贝来实现创建对象的模式就称作原型模式

可以减少对象复杂度,让系统性能得到一些提升

复制是基于二进制流,而不是简单的get set

属于创建型模式

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzSfHIgC-1647080017424)(D:\文件_Typora\1591854646241.png)]

从UML图中看到,原型模式主要包含三个角色:

客户(Client):客户类提出创建对象的请求

抽象原型(Prototype):规定拷贝接口

具体原型(Concrete Prototype):被拷贝的对象

适用场景

  1. 类初始化消耗资源较多
  2. new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
  3. 构造函数比较复杂
  4. 循环体中生产大量对象时

在Spring中,原型模式应用非常广泛。例如scope=“prototype”,在我们经常使用的JSON.parseObject()也是一种原型模式

实现

先创建原型IPrototype接口

public interface IPrototype<T>{
    T clone();
}

创建具体需要克隆的对象ConcretePrototype

public class ConcretePrototype implements IPrototype{
    private int age;
    private String name;
    
    public int getAge(){return age;}
    public void setAge(){this.age=age;}
    public String getName(){return name;}
    public void setName(){this.name=name;}
    
    @Override
    public ConcretePrototype clone(){
        ConcretePrototy concretePrototype=new ConcretePrototype();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        return concretePrototype;
    }
    
    @Override
    public String toString(){
        return "concretePrototype{"+
            "age="+age+
            ",name='"+name+'\''+'}';
    }
}

测试

public class Client{
    public static void main(String[] args){
        //创建原型对象
        ConcretePrototype prototype=new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("tom");
        System.out.println(prototype);
        //拷贝原型对象
        ConcretePrototype cloneType=prototype.clone();
        System.out.println(cloneType);
    }
}

结果:

ConcretePrototype{age=18,name=‘tom’}
ConcretePrototype{age=18,name=‘tom’}

或者用反射创建对象

public class BeanUtils {

    public static Object copy(Object protorype) {
        Class clazz = protorype.getClass();
        Object returnValue = null;
        try {
            returnValue = clazz.newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                field.set(returnValue, field.get(protorype));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return returnValue;
    }
}

优势

如果有几百个属性需要复制,可以一劳永逸

new(参数[]…) 很累 clone不用反复设值,不走new,而是直接调clone方法

改造

JDK封装

public class ConcretePrototype implements Cloneable{
    private int age;
    private String name;
    
    public int getAge(){return age;}
    public void setAge(){this.age=age;}
    public String getName(){return name;}
    public void setName(){this.name=name;}
    
    @Override
    public ConcretePrototype clone(){
        try{
            return (ConcretePrototype)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public String toString(){
        return "concretePrototype{"+
            "age="+age+
            ",name='"+name+'\''+'}';
    }
}

浅克隆

场景

加入个人爱好属性hobbies

@Data
public class ConcretePrototype implements Cloneable{
    private int age;
    private String name;
	private List<String> hobbies;
    
    @Override
    public ConcretePrototype clone(){
        try{
            return (ConcretePrototype)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public String toString(){
        return "concretePrototype{"+
            "age="+age+
            ",name='"+name+'\''+'}';
    }
}

问题

给复制后的克隆对象新增一项爱好,发现原型对象也发生了变化

显然,hobbies共用了一个内存地址,意味着复制的不是值,而是引用的地址

原因

jdk有个机制:当一个类存在时,它就不会再重新加载,而是直接复制地址

所以浅克隆很少用

定义

只是完整复制了值类型数据,没有赋值引用对象。所有的引用对象仍然指向原来的对象

深克隆

Serializable

在上面基础上继续改造,增加一个deepClone()方法

@Data
public class ConcretePrototype implements Cloneable,Serializable{
    private int age;
    private String name;
	private List<String> hobbies;
    
    //里式替换原则:不去改变父类本身,而且新增
    @Override
    public ConcretePrototype clone(){
        try{
            return (ConcretePrototype)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public String toString(){
        return "concretePrototype{"+
            "age="+age+
            ",name='"+name+'\''+'}';
    }
    
    //通过序列化和反序列化进行克隆
    public ConcretePrototype deepClone(){
        try{
            //序列化
            //要用必须实现 Serializable
            //不输出到磁盘了,直接在内存中完成
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            
            //反序列化
            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            //看过源码,readObject是通过反射创建新的对象
            //正好符合我们的要求
            return (ConcretePrototype)ois.readObject();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

Json

//TODO 更常见

缺点

性能不好,占用I/O

冲突

单例模式通过私有化构造函数来实现,而原型模式根本不走构造方法

如果克隆的目标对象是单例对象,那意味着深克隆会破坏单例,禁止深克隆即可

解决

要么单例类不实现Cloneable接口

要么重新clone()方法,在clone方法中返回单例对象即可

@Override
protected Object clone() throws CloneNotSupportedException{
    return INSTANCE;
}

源码应用

JDK中Cloneable接口:

public interface Cloneable{}

找原型模式,只需要找哪些实现了Cloneable即可

ArrayList

package java.util;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
        public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            //copyOf()将elementData的值重新循环遍历了一遍,再把它赋值出来
            //赋值给新创建的ArrayList v
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
}

进一步进入(ArrayList<?>) super.clone();方法

package java.util;

public class Arrays {
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
	//调用同类的静态方法
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
}

HashMap

package java.util;

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    @SuppressWarnings("unchecked")
    @Override
    public Object clone() {
        HashMap<K,V> result;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            //迭代后创新的
            throw new InternalError(e);
        }
        result.reinitialize();
        result.putMapEntries(this, false);
        return result;
    }
}

结论

实现Cloneable的都是浅克隆,深克隆一般要自己写

实现深克隆只有两种方法:

  1. 序列化
  2. 转Json

启发

由ArrayList和HashMap的源码受启发,硬编码,利用ArrayList默认实现的浅克隆,去实现具体类的深克隆

@Data
public class ConcretePrototype implements Cloneable,Serializable{
    private int age;
    private String name;
	private List<String> hobbies;
    
    //里式替换原则:不去改变父类本身,而且新增
    @Override
    public ConcretePrototype clone(){
        try{
            return (ConcretePrototype)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
            return null;
        }
    }
    
    @Override
    public String toString(){
        return "concretePrototype{"+
            "age="+age+
            ",name='"+name+'\''+'}';
    }
    
    //通过序列化和反序列化进行克隆
    public ConcretePrototype deepClone(){
        try{
            //序列化
            //要用必须实现 Serializable
            //不输出到磁盘了,直接在内存中完成
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            
            //反序列化
            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            //看过源码,readObject是通过反射创建新的对象
            //正好符合我们的要求
            return (ConcretePrototype)ois.readObject();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
    
    //里式替换原则:不去改变父类本身,而且新增
     public ConcretePrototype deepCloneHobbies(){
        try{
            //太麻烦,硬编码,不如上面两种
            ConcretePrototype reslut=(ConcretePrototype)super.clone();
            result.hobbies=(List)((ArrayList)result.hobbies).clone;
            return result;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
     }
}

优点

  1. 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了很多
  2. 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存下来,简化了创建对象的过程,以便于在需要的时候使用(例如恢复到历史某一时刻),可辅助实现撤销操作

缺点

  1. 需要为每一个类配置一个克隆方法
  2. 克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则
  3. 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深度克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆,浅克隆需要使用得当
  4. 原型模式和单例模式要区分开

建造者模式

Builder Pattern

定义

建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构造过程可以创建不同的表示

特征:用户只需要指定需要创建的类型就可以获得对象,构造过程及细节不需要了解

属于创造型模式

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAgg3Xtv-1647080017424)(D:\文件_Typora\1591854689953.png)]

角色

建造者模式的设计中有四个主要角色

  1. 产品(Product):要创建的产品类对象
  2. 建造者抽象(Builder):建造者的抽象类,规范产品对象的各个组成部分,一般由子类实现具体的建造过程
  3. 建造者(ConcreteBuilder):具体的Builder类,根据不同的业务逻辑,具体化对象的各个组成部分的创建
  4. 调用者(Director):调用具体的建造者,来创建对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建

应用场景

建造者模式适用于一个具体较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是它们的组合方式却相对稳定

建造者模式适用于以下几种场景:

  1. 相同的方法,不同的执行顺序,产生不同的结果时
  2. 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同
  3. 产品类非常复杂,或者产品类中的调用顺序不同产生不同的作用
  4. 当初始化一个对象特别复杂,参数多,而且很多参数都具有默认值时

基本写法

一个完整的课程需要由PPT课件,回放视频,课堂笔记,课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造者模式带入理解

创建产品类Course:

@Data
public class Course{
    private String name;
    private String ppt;
    private String video;
    private String note;
    
    private String homework;
    
    @Override
    public String toString(){
        return "CourseBuilder{"+
            "name'"+name+'\''+
            ",ppt='"+ppt+'\''+
            ",video='"+video+'\''+
            ",note='"+note+'\''+
            ",homework='"+homework+'\''+
            '}';
    }
}

然后创建建造者类CourseBuilder,将复杂的构造过程封装起来,构造步骤由用户决定

public class CourseBuilder{
    private Course course=new Course();
    
    public void addName(String name){
        course.setName(name);
    }
    public void addPPT(String ppt){
        course.setPpt(ppt);
    }
    public void addVideo(String video){
        course.setVideo(video);
    }
    public void addNote(String note){
        course.setHomework(homework);
    }
    public Course builder(){
        return course;
    }
}

测试

public static void main(String[] args){
    CourseBuilder builder=new CourseBuilder();
    builder.addName("设计模式");
    builder.addPPT("【PPT课件】");
    builder.addVideo("【回放视频】");
    builder.addNote("【课堂笔记】");
    builder.addHomework("【课后作业】");
    System.out.println(builder.builder());
}

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZZ2NQd0-1647080017424)(D:\文件_Typora\1591854757743.png)]

链式写法

在平常的应用中,建造者模式通常是采用链式编程的方法构造对象,修改CourseBuilder类,将Course变为CourseBuilder的内部类:

public class CourseBuilder{
    @Data
    public class Course{
        private String name;
        private String ppt;
        private String video;
        private String note;

        private String homework;

        @Override
        public String toString(){
            return "CourseBuilder{"+
                "name'"+name+'\''+
                ",ppt='"+ppt+'\''+
                ",video='"+video+'\''+
                ",note='"+note+'\''+
                ",homework='"+homework+'\''+
                '}';
        }
    }
}

然后,将构造步骤添加进去,每完成一个步骤,都返回this:

public class CourseBuilder{
    private Course course=new Course();
    
    public CourseBuilder addName(String name){
        course.setName(name);
        return this;
    }
    public CourseBuilder addPPT(String ppt){
        course.setPpt(ppt);
        return this;
    }
    public CourseBuilder addVideo(String video){
        course.setVideo(video);
        return this;
    }
    public CourseBuilder addNote(String note){
        course.setNote(note);
        return this;
    }
    public CourseBuilder addHomework(String homework){
        course.setHomework(homework);
        return this;
    }
    
    public Course build(){
        return this.course;
    }
    
    @Data
    public class Course{
		...
    }
}

客户端使用:

public static void main(String[] args){
    CouseBuilder builder=new CourseBuilder()
        .addName("设计模式")
        .addPPT("【PPT课件】")
        .addVideo("【回放视频】")
        .addNote("【课堂笔记】")
        .addHomework("【课后作业】");
    System.out.println(builder.build());
}

类图变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IY0CZwrW-1647080017424)(D:\文件_Typora\1591854785027.png)]

源码模仿

参考JPA的SQL构造模式,根据不同条件在拼接SQL字符串。用QueryRuleSqlBuilder将复杂的构造SQL过程进行封装,用QueryRule对象专门保存SQL查询时的条件,最后根据查询条件,自动生成SQL语句

QueryRule

先创建QueryRule类:

package com.gupaoedu.vip.pattern.builder.sql;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * QueryRule,主要功能用于构造查询条件
 * 
 * @author 
 */
public final class QueryRule implements Serializable
{
	private static final long serialVersionUID = 1L;
	public static final int ASC_ORDER = 101;
	public static final int DESC_ORDER = 102;
	public static final int LIKE = 1;
	public static final int IN = 2;
	public static final int NOTIN = 3;
	public static final int BETWEEN = 4;
	public static final int EQ = 5;
	public static final int NOTEQ = 6;
	public static final int GT = 7;
	public static final int GE = 8;
	public static final int LT = 9;
	public static final int LE = 10;
	public static final int ISNULL = 11;
	public static final int ISNOTNULL = 12;
	public static final int ISEMPTY = 13;
	public static final int ISNOTEMPTY = 14;
	public static final int AND = 201;
	public static final int OR = 202;
	private List<Rule> ruleList = new ArrayList<Rule>();
	private List<QueryRule> queryRuleList = new ArrayList<QueryRule>();
	private String propertyName;

	private QueryRule() {}

	private QueryRule(String propertyName) {
		this.propertyName = propertyName;
	}

	public static QueryRule getInstance() {
		return new QueryRule();
	}
	
	/**
	 * 添加升序规则
	 * @param propertyName
	 * @return
	 */
	public QueryRule addAscOrder(String propertyName) {
		this.ruleList.add(new Rule(ASC_ORDER, propertyName));
		return this;
	}

	/**
	 * 添加降序规则
	 * @param propertyName
	 * @return
	 */
	public QueryRule addDescOrder(String propertyName) {
		this.ruleList.add(new Rule(DESC_ORDER, propertyName));
		return this;
	}

	public QueryRule andIsNull(String propertyName) {
		this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(AND));
		return this;
	}

	public QueryRule andIsNotNull(String propertyName) {
		this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(AND));
		return this;
	}

	public QueryRule andIsEmpty(String propertyName) {
		this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(AND));
		return this;
	}

	public QueryRule andIsNotEmpty(String propertyName) {
		this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(AND));
		return this;
	}

	public QueryRule andLike(String propertyName, Object value) {
		this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andBetween(String propertyName, Object... values) {
		this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(AND));
		return this;
	}

	public QueryRule andIn(String propertyName, List<Object> values) {
		this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(AND));
		return this;
	}

	public QueryRule andIn(String propertyName, Object... values) {
		this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(AND));
		return this;
	}
	
	public QueryRule andNotIn(String propertyName, List<Object> values) {
		this.ruleList.add(new Rule(NOTIN, propertyName, new Object[] { values }).setAndOr(AND));
		return this;
	}

	public QueryRule orNotIn(String propertyName, Object... values) {
		this.ruleList.add(new Rule(NOTIN, propertyName, values).setAndOr(OR));
		return this;
	}
	

	public QueryRule andNotEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andGreaterThan(String propertyName, Object value) {
		this.ruleList.add(new Rule(GT, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andGreaterEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(GE, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andLessThan(String propertyName, Object value) {
		this.ruleList.add(new Rule(LT, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}

	public QueryRule andLessEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(LE, propertyName, new Object[] { value }).setAndOr(AND));
		return this;
	}
	
	
	public QueryRule orIsNull(String propertyName) {
		this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(OR));
		return this;
	}

	public QueryRule orIsNotNull(String propertyName) {
		this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(OR));
		return this;
	}

	public QueryRule orIsEmpty(String propertyName) {
		this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(OR));
		return this;
	}

	public QueryRule orIsNotEmpty(String propertyName) {
		this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(OR));
		return this;
	}

	public QueryRule orLike(String propertyName, Object value) {
		this.ruleList.add(new Rule(LIKE, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(EQ, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orBetween(String propertyName, Object... values) {
		this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(OR));
		return this;
	}

	public QueryRule orIn(String propertyName, List<Object> values) {
		this.ruleList.add(new Rule(IN, propertyName, new Object[] { values }).setAndOr(OR));
		return this;
	}

	public QueryRule orIn(String propertyName, Object... values) {
		this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(OR));
		return this;
	}

	public QueryRule orNotEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orGreaterThan(String propertyName, Object value) {
		this.ruleList.add(new Rule(GT, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orGreaterEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(GE, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orLessThan(String propertyName, Object value) {
		this.ruleList.add(new Rule(LT, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}

	public QueryRule orLessEqual(String propertyName, Object value) {
		this.ruleList.add(new Rule(LE, propertyName, new Object[] { value }).setAndOr(OR));
		return this;
	}
	

	public List<Rule> getRuleList() {
		return this.ruleList;
	}

	public List<QueryRule> getQueryRuleList() {
		return this.queryRuleList;
	}

	public String getPropertyName() {
		return this.propertyName;
	}

	protected class Rule implements Serializable {
		private static final long serialVersionUID = 1L;
		private int type;	//规则的类型
		private String property_name;
		private Object[] values;
		private int andOr = AND;

		public Rule(int paramInt, String paramString) {
			this.property_name = paramString;
			this.type = paramInt;
		}

		public Rule(int paramInt, String paramString,
				Object[] paramArrayOfObject) {
			this.property_name = paramString;
			this.values = paramArrayOfObject;
			this.type = paramInt;
		}
		
		public Rule setAndOr(int andOr){
			this.andOr = andOr;
			return this;
		}
		
		public int getAndOr(){
			return this.andOr;
		}

		public Object[] getValues() {
			return this.values;
		}

		public int getType() {
			return this.type;
		}

		public String getPropertyName() {
			return this.property_name;
		}
	}
}

QueryRuleSqlBuilder

然后创建QueryRuleSqlBuilder类:

package com.gupaoedu.vip.pattern.builder.sql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.ArrayUtils;

import com.gupaoedu.vip.pattern.builder.sql.QueryRule.Rule;
import org.apache.commons.lang.StringUtils;


/**
 * 根据QueryRule自动构建sql语句
 * @author 
 *
 */
public class QueryRuleSqlBuilder {
	private int CURR_INDEX = 0; //记录参数所在的位置
	private List<String> properties; //保存列名列表
	private List<Object> values; //保存参数值列表
	private List<Order> orders; //保存排序规则列表
	
	private String whereSql = ""; 
	private String orderSql = "";
	private Object [] valueArr = new Object[]{};
	private Map<Object,Object> valueMap = new HashMap<Object,Object>();
	
	/**
	 * 或得查询条件
	 * @return
	 */
	private String getWhereSql(){
		return this.whereSql;
	}
	
	/**
	 * 获得排序条件
	 * @return
	 */
	private String getOrderSql(){
		return this.orderSql;
	}
	
	/**
	 * 获得参数值列表
	 * @return
	 */
	public Object [] getValues(){
		return this.valueArr;
	}
	
	/**
	 * 获取参数列表
	 * @return
	 */
	private Map<Object,Object> getValueMap(){
		return this.valueMap;
	}
	
	/**
	 * 创建SQL构造器
	 * @param queryRule
	 */
	public QueryRuleSqlBuilder(QueryRule queryRule) {
		CURR_INDEX = 0;
		properties = new ArrayList<String>();
		values = new ArrayList<Object>();
		orders = new ArrayList<Order>();
		for (QueryRule.Rule rule : queryRule.getRuleList()) {
			switch (rule.getType()) {
			case QueryRule.BETWEEN:
				processBetween(rule);
				break;
			case QueryRule.EQ:
				processEqual(rule);
				break;
			case QueryRule.LIKE:
				processLike(rule);
				break;
			case QueryRule.NOTEQ:
				processNotEqual(rule);
				break;
			case QueryRule.GT:
				processGreaterThen(rule);
				break;
			case QueryRule.GE:
				processGreaterEqual(rule);
				break;
			case QueryRule.LT:
				processLessThen(rule);
				break;
			case QueryRule.LE:
				processLessEqual(rule);
				break;
			case QueryRule.IN:
				processIN(rule);
				break;
			case QueryRule.NOTIN:
				processNotIN(rule);
				break;
			case QueryRule.ISNULL:
				processIsNull(rule);
				break;
			case QueryRule.ISNOTNULL:
				processIsNotNull(rule);
				break;
			case QueryRule.ISEMPTY:
				processIsEmpty(rule);
				break;
			case QueryRule.ISNOTEMPTY:
				processIsNotEmpty(rule);
				break;
			case QueryRule.ASC_ORDER:
				processOrder(rule);
				break;
			case QueryRule.DESC_ORDER:
				processOrder(rule);
				break;
			default:
				throw new IllegalArgumentException("type " + rule.getType() + " not supported.");
			}
		}
		//拼装where语句
		appendWhereSql();
		//拼装排序语句
		appendOrderSql();
		//拼装参数值
		appendValues();
	}
	
	/**
	 * 去掉order
	 * 
	 * @param sql
	 * @return
	 */
	private String removeOrders(String sql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(sql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
	
	/**
	 * 去掉select
	 * 
	 * @param sql
	 * @return
	 */
	private String removeSelect(String sql) {
		if(sql.toLowerCase().matches("from\\s+")){
			int beginPos = sql.toLowerCase().indexOf("from");
			return sql.substring(beginPos);
		}else{
			return sql;
		}
	}
	
	/**
	 * 处理like
	 * @param rule
	 */
	private  void processLike(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		Object obj = rule.getValues()[0];

		if (obj != null) {
			String value = obj.toString();
			if (!StringUtils.isEmpty(value)) {
				value = value.replace('*', '%');
				obj = value;
			}
		}
		add(rule.getAndOr(),rule.getPropertyName(),"like","%"+rule.getValues()[0]+"%");
	}

	/**
	 * 处理between
	 * @param rule
	 */
	private  void processBetween(QueryRule.Rule rule) {
		if ((ArrayUtils.isEmpty(rule.getValues()))
				|| (rule.getValues().length < 2)) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),"","between",rule.getValues()[0],"and");
		add(0,"","","",rule.getValues()[1],"");
	}
	
	/**
	 * 处理 =
	 * @param rule
	 */
	private  void processEqual(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),"=",rule.getValues()[0]);
	}

	/**
	 * 处理 <>
	 * @param rule
	 */
	private  void processNotEqual(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),"<>",rule.getValues()[0]);
	}

	/**
	 * 处理 >
	 * @param rule
	 */
	private  void processGreaterThen(
			QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),">",rule.getValues()[0]);
	}

	/**
	 * 处理>=
	 * @param rule
	 */
	private  void processGreaterEqual(
			QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),">=",rule.getValues()[0]);
	}

	/**
	 * 处理<
	 * @param rule
	 */
	private  void processLessThen(QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),"<",rule.getValues()[0]);
	}

	/**
	 * 处理<=
	 * @param rule
	 */
	private  void processLessEqual(
			QueryRule.Rule rule) {
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		add(rule.getAndOr(),rule.getPropertyName(),"<=",rule.getValues()[0]);
	}

	/**
	 * 处理  is null
	 * @param rule
	 */
	private  void processIsNull(QueryRule.Rule rule) {
		add(rule.getAndOr(),rule.getPropertyName(),"is null",null);
	}

	/**
	 * 处理 is not null
	 * @param rule
	 */
	private  void processIsNotNull(QueryRule.Rule rule) {
		add(rule.getAndOr(),rule.getPropertyName(),"is not null",null);
	}

	/**
	 * 处理  <>''
	 * @param rule
	 */
	private  void processIsNotEmpty(QueryRule.Rule rule) {
		add(rule.getAndOr(),rule.getPropertyName(),"<>","''");
	}

	/**
	 * 处理 =''
	 * @param rule
	 */
	private  void processIsEmpty(QueryRule.Rule rule) {
		add(rule.getAndOr(),rule.getPropertyName(),"=","''");
	}

	
	/**
	 * 处理in和not in
	 * @param rule
	 * @param name
	 */
	private void inAndNotIn(QueryRule.Rule rule,String name){
		if (ArrayUtils.isEmpty(rule.getValues())) {
			return;
		}
		if ((rule.getValues().length == 1) && (rule.getValues()[0] != null)
				&& (rule.getValues()[0] instanceof List)) {
			List<Object> list = (List) rule.getValues()[0];
			
			if ((list != null) && (list.size() > 0)){
				for (int i = 0; i < list.size(); i++) {
					if(i == 0 && i == list.size() - 1){
						add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list.get(i),")");
					}else if(i == 0 && i < list.size() - 1){
						add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list.get(i),"");
					}
					if(i > 0 && i < list.size() - 1){
						add(0,"",",","",list.get(i),"");
					}
					if(i == list.size() - 1 && i != 0){
						add(0,"",",","",list.get(i),")");
					}
				}
			}
		} else {
			Object[] list =  rule.getValues();
			for (int i = 0; i < list.length; i++) {
				if(i == 0 && i == list.length - 1){
					add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list[i],")");
				}else if(i == 0 && i < list.length - 1){
					add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list[i],"");
				}
				if(i > 0 && i < list.length - 1){
					add(0,"",",","",list[i],"");
				}
				if(i == list.length - 1 && i != 0){
					add(0,"",",","",list[i],")");
				}
			}
		}
	}
	
	/**
	 * 处理 not in
	 * @param rule
	 */
	private void processNotIN(QueryRule.Rule rule){
		inAndNotIn(rule,"not in");
	}
	
	/**
	 * 处理 in
	 * @param rule
	 */
	private  void processIN(QueryRule.Rule rule) {
		inAndNotIn(rule,"in");
	}
	
	/**
	 * 处理 order by
	 * @param rule 查询规则
	 */
	private void processOrder(Rule rule) {
		switch (rule.getType()) {
		case QueryRule.ASC_ORDER:
			// propertyName非空
			if (!StringUtils.isEmpty(rule.getPropertyName())) {
				orders.add(Order.asc(rule.getPropertyName()));
			}
			break;
		case QueryRule.DESC_ORDER:
			// propertyName非空
			if (!StringUtils.isEmpty(rule.getPropertyName())) {
				orders.add(Order.desc(rule.getPropertyName()));
			}
			break;
		default:
			break;
		}
	}
	
	
	/**
	 * 加入到sql查询规则队列
	 * @param andOr and 或者 or
	 * @param key 列名
	 * @param split 列名与值之间的间隔
	 * @param value 值
	 */
	private  void add(int andOr,String key,String split ,Object value){
		add(andOr,key,split,"",value,"");
	}
	
	/**
	 * 加入到sql查询规则队列
	 * @param andOr and 或则 or
	 * @param key 列名
	 * @param split 列名与值之间的间隔
	 * @param prefix 值前缀
	 * @param value 值
	 * @param suffix 值后缀
	 */
	private  void add(int andOr,String key,String split ,String prefix,Object value,String  suffix){
		String andOrStr = (0 == andOr ? "" :(QueryRule.AND == andOr ? " and " : " or "));  
		properties.add(CURR_INDEX, andOrStr + key + " " + split + prefix + (null != value ? " ? " : " ") + suffix);
		if(null != value){
			values.add(CURR_INDEX,value);
			CURR_INDEX ++;
		}
	}
	
	
	/**
	 * 拼装 where 语句
	 */
	private void appendWhereSql(){
		StringBuffer whereSql = new StringBuffer();
		for (String p : properties) {
			whereSql.append(p);
		}
		this.whereSql = removeSelect(removeOrders(whereSql.toString()));
	}
	
	/**
	 * 拼装排序语句
	 */
	private void appendOrderSql(){
		StringBuffer orderSql = new StringBuffer();
		for (int i = 0 ; i < orders.size(); i ++) {
			if(i > 0 && i < orders.size()){
				orderSql.append(",");
			}
			orderSql.append(orders.get(i).toString());
		}
		this.orderSql = removeSelect(removeOrders(orderSql.toString()));
	}
	
	/**
	 * 拼装参数值
	 */
	private void appendValues(){
		Object [] val = new Object[values.size()];
		for (int i = 0; i < values.size(); i ++) {
			val[i] = values.get(i);
			valueMap.put(i, values.get(i));
		}
		this.valueArr = val;
	}

	public String builder(String tableName){
		String ws = removeFirstAnd(this.getWhereSql());
		String whereSql = ("".equals(ws) ? ws : (" where " + ws));
		String sql = "select * from " + tableName + whereSql;
		Object [] values = this.getValues();
		String orderSql = this.getOrderSql();
		orderSql = (StringUtils.isEmpty(orderSql) ? " " : (" order by " + orderSql));
		sql += orderSql;
		return sql;
	}


	private String removeFirstAnd(String sql){
		if(StringUtils.isEmpty(sql)){return sql;}
		return sql.trim().toLowerCase().replaceAll("^\\s*and", "") + " ";
	}

}

Order类

创建Order类

package com.gupaoedu.vip.pattern.builder.sql;


/**
 * sql排序组件
 * @author 
 */
public class Order {
	private boolean ascending; //升序还是降序
	private String propertyName; //哪个字段升序,哪个字段降序
	
	public String toString() {
		return propertyName + ' ' + (ascending ? "asc" : "desc");
	}

	/**
	 * Constructor for Order.
	 */
	protected Order(String propertyName, boolean ascending) {
		this.propertyName = propertyName;
		this.ascending = ascending;
	}

	/**
	 * Ascending order
	 *
	 * @param propertyName
	 * @return Order
	 */
	public static Order asc(String propertyName) {
		return new Order(propertyName, true);
	}

	/**
	 * Descending order
	 *
	 * @param propertyName
	 * @return Order
	 */
	public static Order desc(String propertyName) {
		return new Order(propertyName, false);
	}

}

测试

package com.gupaoedu.vip.pattern.builder.sql;

import javax.management.Query;
import java.util.Arrays;

/**
 * Created by 
 */
public class Test {
    public static void main(String[] args) {
        QueryRule queryRule = QueryRule.getInstance();
        queryRule.addAscOrder("age");
        queryRule.andEqual("addr","Changsha");
        queryRule.andLike("name","Tom");
        QueryRuleSqlBuilder builder = new QueryRuleSqlBuilder(queryRule);

        System.out.println(builder.builder("t_member"));

        System.out.println("Params: " + Arrays.toString(builder.getValues()));


    }
}

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wviH2Xv8-1647080017425)(D:\文件_Typora\1591854815257.png)]

源码应用

StringBuilder:提供append()方法,用来建造String,最后toString()生成最终字符串

package java.lang;
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

Mybatis中的CacheBuilder:通过调用builder()方法获得的是一个SqlSessionFactory

package org.apache.ibatis.mapping;
public class CacheBuilder {
    public CacheBuilder properties(Properties properties) {
        this.properties = properties;
        return this;
    }
}

Mybatis中的SqlSessionFactoryBuilder:通过调用builder()方法获得的是一个SqlSessionFactory

package org.apache.ibatis.session;
public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
}

Spring中的BeanDefinitionBuilder:

package org.springframework.beans.factory.support;
public class BeanDefinitionBuilder {
    //Bean的来源可以很多,比如XML,Annotation,但最终都变成
    //比如childBeanDefinition父子的
    //有根的rootBeanDefinition
    //有默认的genericBeanDefinition
    //添加属性addPropertyReference
    //设置初始化方法
    //设置Scope,SCOPE_SINGLETON or SCOPE_PROTOTYPE
    //设置抽象setAbstract
    //不要求构造方法写的全
    //甚至可以加 if
    //最终都变成AbstractBeanDefinition
    private AbstractBeanDefinition beanDefinition;
    public AbstractBeanDefinition getBeanDefinition() {
        this.beanDefinition.validate();
        return this.beanDefinition;
    }
}

优点

  1. 封装性好,创建和使用分离
  2. 扩展性好(构建过程独立,构建结果统一),建造类之间独立,一定程度上解耦
  3. 最终结果一定是合法的
  4. 可以给默认值

缺点

  1. 产生多余的Builder对象,多了一层类
  2. 产品内部发生变化,建造者都要修改,成本较大
  3. 不符合开闭原则,维护成本大

与工厂区别

  1. 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象
  2. 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创造出来的都一样
  3. 关注重点不一样,工厂模式只需要将对象创建出来就可以了,而建造者模式不仅要创建出这个对象,还要知道这个对象由哪些部件组成
  4. 建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样

作业

1、用JSON方式实现一个原型模式的深克隆,并画出UML图。

public static void main(String[] args){
    StudentBuilder builder=new StudentBuilder().addName("张三").addCourse(Array.asList("英语","语文"));
    Student source=builder.build();
    System.out.println("source:"+source);
    List<String> sourceCoursies=source.getCoursies();
    String jsonString=JSON.toJSONString(source);
    Student desc=JSON.parseObject(jsonString,Student.class);
    System.out.println(desc);
    List<String> descCoursies=desc.getCoursies();
    System.out.println(source==desc);
    System.out.println(sourceCoursies==descCoursies);
}

2、请列举1-3个需要用到建造者模式的业务场景。

SQL,StringBuilder,bulider()

补充

原型模式:

  1. 只要是实现了Cloneable接口的都是浅克隆
  2. ArrayList重写clone()方法还是浅克隆,适用于集合元素搬家,不适用传输
  3. 深克隆,就是完全不同的对象

工厂模式:不允许选配
建造者模式:允许选配

代理模式

Proxy Pattern

Spring AOP是用代理模式实现的,实现系统的监控,日志的监控,数据源的动态切换,分库分表的规则,权限的设置

课程目标

1、掌握代理模式的应用场景和实现原理。

2、了解静态代理和动态代理的区别。

3、了解CGLib和JDK Proxy的根本区别。

4、手写实现定义的动态代理。

定义

代理模式是指为其他对象提供一种代理,以控制对这个对象的访问(代理对象是建立在客户端和目标客户之间,起到中介的作用,中介有可能帮助代理对象做一些前后的辅助工作,增强代码的功能)

代理对象在客户端和目标对象之间起到中介作用

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间气到中介的作用

属于结构型设计模式

生活例子

房产中介,快递小哥,黄牛,非入侵式日志监听

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-44r1Ljh2-1647080017425)(D:\文件_Typora\1591854859171.png)]

角色

代理模式一般包含三种角色:

抽象主题角色(Subject):抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象类

真实主题角色(RealSubject):该类也被成为代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的逻辑业务对象

代理主题角色(Proxy):也被称为代理类,其内部持有RealSubject的引用,因此具备完全的对RealSubject的代理权。客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理代码

在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。代理模式属于结构型模式,分为静态代理和动态代理

目的

  1. 包含目标对象
  2. 增强目标对象

通用写法

首先创建代理主题角色 ISubject类

public interface ISubject{
    void request();
}

创建真实主题角色 RealSubject类

public class RealSubject implements ISubject{
    public void request(){
        System.out.println("real service is called.");
    }
}

创建代理主题角色 Proxy类

public class Proxy implements ISubject{
    private ISubject subject;
    
    public Proxy(ISubject subject){
        this.subject=subject;
    }
    
    public void request(){
        before();
        subject.request();
        after();
    }
    
    public void before(){
        System.out.println("called before request().");
    }
    
    public void after(){
        System.out.println("called after request().");
    }
}

客户端调用代码

public static void main(String[] args){
    Proxy proxy=new Proxy(new RealSubject());
    proxy.request();
}

从静到动

动态代理的底层实现一般不用我们自己亲自去实现,已经有很多现成的API。在Java生态中,目前最普遍使用的是JDK自带的代理和Cglib提供的类库。

动态生成一个新的类,这个类叫做代理类,只在内存中存在,然后实现目标类接口,或者继承目标类,是同一个体系,意味着它具有相同方法,可以实现或者重新。所以调用动态代理类方法时,就会通过反射机制去找到代理类的相同名字方法,再调用,在调用之前加东西,在调用之后加东西

生活例子

创建顶层接口IPerson

public interface IPerson{
    void findLove();
}

儿子张三要找对象,实现Zhangsan类

public class ZhangSan implements IPerson{
    public void findLove(){
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}

父亲张老三要帮儿子张三相亲,实现Father类

public class ZhangLaosan implements IPerson{
    private ZhangSan zhangsan;
    
    public ZhangLaosan(ZhangSan zhangsan){
        this.zhangsan=zhangsan;
    }
    
    public void findLove(){
        System.out.println("张老三开始物色");
        zhangsan.findLove();
        System.out.println("开始交往");
    }
}

测试代码

public class Test{
    public static void main(String[] args){
        ZhangLaosan zhangLaosan=new ZhangLaosan(new ZhangSan());
        zhangLaosan.findLove();
    }
}
弊端

媒婆无法实现

解决
JDK

基于JDK升级代码,要求类继承接口,因为生成代理类时,需要传入Interface,需要拿到接口才好知道实现哪个接口

首先创建媒婆类JdkMeipo

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkMeipo implements InvocationHandler{
    private IPerson target;
    //拿到目标类的引用
    public IPersion getInstance(IPerson target){
        this.target=target;
        Class<?> clazz=target.getClass();
        //需要传入Interface,需要拿到接口才好知道实现哪个接口
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        before();
        Object result=method.invoke(this.target,args);
        after();
        return result;
    }
    private void after(){
        System.out.println("双方同意,开始交往");
    }
    private void before(){
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
}

再创建一个类ZhaoLiu:

public class ZhaoLiu implements IPerson{
    public void findLove(){
        System.out.println("赵六要求:有车有房学历高")
    }
    public void buyInsure(){}
}

测试代码:

public static void main(String[] args){
    JdkMeipo jdkMeipo=new JdkMeipo();
    //这两个张三不是同一个张三了,只是在同一个体系内
    //用$符号开头的类都是动态代理生成的类,只在内存中
    IPerson zhangsan=jdkMeipo.getInstance(new Zhangsan());
    zhangsan.findLove();
    
    IPerson zhaoliu = jdkMeipo.getInstance(new ZhaoLiu());
    zhaoliu.findLove();
}

结果:

我是媒婆,已经收集到你的需求,开始物色
张三要求:肤白貌美大长腿
双方同意,开始交往
我是媒婆,已经收集到你的需求,开始物色
赵六要求:有车有房学历高
双方同意,开始交往

CgLib

最大的好处是,不用用户有任何的前置条件

import net.sf.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;
import java.lang.reflect.Method;

public class CglibMeipo implements MethodInterceptor{
    private IPerson target;
    //拿到目标类的引用
    public Object getInstance(Class<?> clazz){
        //目标不需要实现任何的接口,是个非常普通的类就行,因为可以通过class,拿到类下面的所有的方法,会生成一个类去继承它所有的方法
        Enhancer enhancer=new Enhancer();
        //而Cglib是直接目标类,不需要其接口
        enhancer.setSuperClass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throws ThrowableException{
        before();
        Object  result=methodProxy.invokeSuper(o,objects);
        after();
        return result;
    }
    
    private void after(){
        System.out.println("双方同意,开始交往");
    }
    private void before(){
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
}
//不用实现任何接口
public class ZhaoLiu {
    public void findLove(){
        System.out.println("赵六要求:有车有房学历高")
    }
    public void buyInsure(){}
}

测试:

public class Test{
    public static void main(String[] args){
       Zhaoliu zhaoliu=(Zhaoliu) new CglibMeipo().getInstance(Zhaoliu.class);
        zhaoliu.findLove();
    }
}
二者区别

Cglib采用继承的方式,覆盖父类的方法
JDK采用实现的方式,必须要求代理的目标对象一定要实现一个接口

思想:都是去通过生成字节码,重新组成一个新的类

JDK Proxy 对用户而言,依赖性更强(必须要实现接口),调用也更负责
Cglib 对目标类没有任何的要求

Cglib 是通过FastClass机制直接调用方法,更优化,效率更高,底层没有用到反射
JDK Proxy 生成逻辑较为简单,执行效率低,每次都要反射(耗性能)

Cglib 有个坑,目标代理类不能有final修饰的方法,忽略final修饰的方法

业务例子

三层架构就是静态代理

//代理类
public class OrderService implements IOrderService{
    
    @Autowired
    //目标代理对象
    private OrderDao orderDao;
    
	public int createOrder(Order order){
        System.out.println("OrderService调用orderDao创建订单");
        int rows=this.orderDao.insert(order);
        return rows;
    }
    
}
弊端

非功能型代码抽离出来,放到外面的组合代理类上避免写重复代码

需求

根据不同的订单时间,使用不同的数据源

解决
//代理类
public class OrderService implements IOrderService{
    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");
    
    @Autowired
    //目标代理对象
    private OrderDao orderDao;
    
	public int createOrder(Order order){
        System.out.println("OrderService调用orderDao创建订单");
        ///重复代码//
        //一般由架构师来写
        Long time=order.getCreateTime();
        Integer dbRouter=Integer.vlaueOf(yearFormat.format(new Date(time)));
        //dbRouter在配置文件中配置好,预先初始化
        System.out.println("自动切换数据源到【DB_"+dbRouter+"】");
        //切换数据源,拿到连接对象,而连接对象事先保存在Spring中
        DynamicDataSourceEntity.set(dbRouter);
        /
        
        int rows=this.orderDao.insert(order);
        return rows;
    }
    
    
}

创建数据源路由器DynamicDataSourceEntity

//Spring中会继承 AbstractDBRouter类数据源路由器
public class DynamicDataSourceEntity{
    public final static String DEFAULE_SOURCE=null;
    
    //针对每一个方法,都去创建一个独立的数据源,这样,每个对象的调用方法都不影响
    private final static ThreadLocal<String> local=new ThreadLocal<~>();
    
    private DynamicDataSourceEntity(){}
    
    public static String get(){return local.get();}
    
    public static void restore(){local.set(DEFAULE_SOURCE);}
    
    //DB_2018
    //DB_2019
    public static void set(String source){local.set(source);}
    
    public static void set(int year){local.set("DB_"+year);}
}
静态代理类
//抽象化
public class OrderServiceStaticProxy implements IOrderService{
    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");
    
    private IOrderService orderService;
    public OrderServiceStaticProxy(IOrderService orderService){this.orderService=orderService;}
    
    public int createOrder(Order order){
        Long time=order.getCreateTime();
        Integer dbRouter=Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理数据");
        DynamicDataSourceEntity.set(dbRouter);
        
        this.orderService.createOrder(order);
        DynamicDataSourceEntity.restore();
        
        return 0;
    }
}
测试
public class DbRouteProxyTest{
    public static void main(String[] args){
        try{
            Order order=new Order();
            
            //order.setCreateTime(new Date().getTime());
            
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
            Date date=sdf.parse("2021/03/01");
            order.setCreateTime(date.getTime());
            
            IOrderService orderService=new OrderService();
            //重复性大,非功能性需求
            //orderService.createOrder(order);
            //改用静态代理
            IOrderService orderService=(IOrderService)new OrderServiceStaticProxy(new OrderService());
            
             
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
修改

修改OrderService

//代理类
public class OrderService implements IOrderService{    
    @Autowired
    //目标代理对象
    private OrderDao orderDao;
    
	public int createOrder(Order order){
        System.out.println("OrderService调用orderDao创建订单");
        int rows=this.orderDao.insert(order);
        return rows;
    }
    
    
}
缺点

只能代理OrderService,不能代理其他Service,所有需要动态代理

动态代理

创建动态代理的类OrderServiceDynamicProxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

//继承的InvocationHandler规定了invoke()方法
public class OrderServiceDynamicProxy implements LewInvacationHandler{
    private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy");
    private Object target;
    //只要是Object都能代理
    public Object getInstance(Object target){
        this.target=target;
        Class<?> clazz=target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        before(args[0]);
        Object object=method.invoke(target,args);
        after();
        return object;
    }
    
    private void before(Object target){
        try{
            System.out.println("Proxy before method.");
            Long time=(Long) target.getClass().getMethod("getCreateTime").invoke(target);
            Integer dbRouter=Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("静态代理类自动分配到【DB_"+dbRouter+"】数据源处理对象");
            DynamicDataSourceEntry.set(dbRouter);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    private void after(){
        System.out.println("Proxy after method");
    }
}
测试

测试代码

public static void main(String[] args){
    try{
        Order order=new Order();
        
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
        Date date=sdf.parse("2018/02/01");
        order.setCreateTime(date.getTime());
        
        IOrderService orderService=(IOrderService)new OrderServiceDynamicProxy().getInstance(new OrderService());
        orderService.createOrder(order);
    }catch(Exception e){
        e.printStackTrace();
    }
}

使用动态代理实现之后,我们不仅能实现Order的数据源动态路由,还可以实现其他任何类的数据源路由。当然,有个比较重要的约定,必须实现getCreateTime()方法,因为路由规则是根据时间来运算的。我们可以通过接口规范来达到约束的目的

手写jdk

//用$符号开头的类都是动态代理生成的类,只在内存中
IPerson zhangsan=jdkMeipo.getInstance(new Zhangsan());

通过将这个只在内存中的类拿到字节码,写入硬盘成class文件,再把class反编译成源码

public static void main(String[] args){
    JdkMeipo jdkMeipo=new JdkMeipo();
    //这两个张三不是同一个张三了,只是在同一个体系内
    //用$符号开头的类都是动态代理生成的类,只在内存中
    IPerson zhangsan=jdkMeipo.getInstance(new Zhangsan());
    zhangsan.findLove();
    
    byte[] bytes=ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
    try{
        FileOutputStream os=new FileOutputStream("E://$Proxy0.class");
        os.write(bytes);
        os.close();
    }catch(Exception e){
        
    }
    
    IPerson zhaoliu = jdkMeipo.getInstance(new ZhaoLiu());
    zhaoliu.findLove();
}

jdk原理

反编译后$Proxy0.class

public final void findLove(){
    try{
        //又下面的分析可知,h是目标类(媒婆类)
        //m4为 private static Method m4
        //赋值语句
        //m4=Class.forName("com.*****.IPerson").getMethod("findLove",new Class[0]);
        //所以m4就是findLove方法,没参数,所以后面传null
        super.h.invoke(this,m4,null);
        return;
    }
    catch(Error _ex){}
    catch(Throwable throwable)
    {
        throw new UndeclaredThrowableException(throwable);
    }
}

发现,$Proxy0继承了Proxy类,同时还实现了Person接口,而且还重写了findLove()等方法,在静态块中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,重现的方法用反射调用目标对象的方法。这些代码是JDK帮我们自动生成的

Proxy源码

package java.lang.reflect;
public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;
    
    //在此赋值
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            //h被赋值于此,所以h就是目标类(媒婆类)
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
}
总结
JdkMeipo jdkMeipo=new JdkMeipo();
//这两个张三不是同一个张三了,只是在同一个体系内
//用$符号开头的类都是动态代理生成的类,只在内存中
IPerson zhangsan=jdkMeipo.getInstance(new Zhangsan());
zhangsan.findLove();

上面代码中调用的zhangsan.findLove();
实际上是新生成的$Proxy0调用jdkMeipo中的invoke()方法,实现了动态代理

手写

因为public class JdkMeipo implements InvocationHandler{}

LIHandler

LewInvocationHandler

import java.lang.reflect.Method;

public interface LewInvocationHandler{
    public Object invoke(Object proxy,Method method,Object[] args)
        throws Throwable;
}
LewProxy
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HahMap;
import java.util.Map
public class LewProxy{
    public static final String ln="\r\n";
    
    //其中的一个重要方法
    public static Object newProxyInstance(LewClassLoader loader,
                                         Class<?> interfaces,
                                         LewInvocationHandler h){
        try{
            //1. 动态生成源代码.java文件,传入实现类的接口
            String src=generateSrc(interfaces);
            
            //2. Java文件输出到磁盘(之前还在内存中,$Proxy0)
            String filePath=LewProxy.class.getResource("").getPath();
            File f=new File(filePath+"$Proxy0.java");
            FileWreiter fw=new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            
            //3. 把生成的.java文件编译成.class文件
            JavaCompiler compiler=ToolProvider.getSystemJavaComplier();
            StandardJavaFileManager manage=compiler.getStandardJavaFileManager(null,null,null);
            Iterable iterable=manage.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task=compiler.getTask(null,manage,null,null,null,iterable);
            task.call();
            manage.close();
            
            //4. 把编译生成的.class文件加载到JVM中
            //LewClassLoader的findClass方法
            Class proxyClass=classLoader.findClass("$Proxy0");
            Constructor c=proxyClass.getConstructor(LewInvocationHandler.class);
            f.delete();
            
            //5. 返回字节码重组以后的新的代理对象
            return c.newInstance(h);
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }
    
    //传入实现类的接口
    private static String generateSrc(Class<?>[] interfaces){
        StringBuffer sb=new StringBuffer();
        //首先找到类对应的包
        sb.append("package com.******;"+ln);
        //导入IPerson包
        sb.append("import com.*****.IPerson;"+ln);
        //导入反射包
        sb.append("imporu java.lang.reflect.*;"+ln);
        //实现的接口
        sb.append("public class $Proxy0 implements "+interfaces[0].getName()+"{"+ln);
        sb.append("LewInvocationHandler h;"+ln);
        sb.append("public $Proxy0(LewInvocationHandler h){"+ln);
        sb.append("this.h=h;"+ln);
        sb.append("}"+ln);
        //把接口下的所有的方法找出来
        //把名字,参数,返回值类型迭代出来
        for(Method m:interfaces[0].getMethods()){
            Class<?>[] params=m.getParameterTypes();
            
            StringBuffer paramNames=new StringBuffer();
            StringBuffer paramValues=new StringBuffer();
            StringBuffer paramClasses=new StringBuffer();
            
            //参数迭代
            for(int i=0;i<params.length;i++){
                //先把类型拿到
                Class clazz=params[i];
                String type=clazz.getName();
                String paramName=toLowerFirstCase(clazz.getSimpleName);
                paramNames.append(type+""=paramName);
                paramValues.append(paramName);
                paramClasses.append(clazz.getName()+".class");
                if(i>0&&i<params.length-1){
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }
            sb.append("public "+m.getReturnType().getName()+" "+m.getName()+"(")+paramNames.toString()+"){"+ln);
            sb.append("try{"+ln);
            sb.append("Method m="+interfaces[0].getName()+".class.getMethod(\""+m.getName()+"\",new Class[]{"+paramClasses.toString()+"});"+ln);
            sb.append((hasReturnValue(m.getReturnType())?"return   ":	"")+getCaseCode("this.h.invoke(this,m,new Object[]{"+paramValues+"})",m.getReturnType())+";"+ln);
            sb.append("}catch(Error -ex){ }");
            sb.append("catch(Throwable e){"+ln);
            sb.append("throw new UndeclaredThrowableException(e);"+ln);
            sb.append("}");
        }
        sb.append("}"+ln);
        return sb.toString();
    }
    
    private static Map<Class,Class> mappings=new HashMap<Class,Class>();
    static{
        mappings.put(int.class,Integer.class);
    }
    
    private static String getReturnEmptyCode(class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "return 0;";
        }else if(returnClass==void.class){
            return "";
        }else{
            return "return null";
        }
    }
    
    private static String getCaseCode(String code,Class<?> returnClass){
        if(mappings.containsKey(returnClass)){
            return "(("+mappings.get(returnClass).getName()+")"+code+")."+returnClass.getSimpleName()+"Value()";
        }
        return code;
    }
    
    private static boolean hasReturnValue(Class<?> clazz){
        return clazz!=void.class;
    }
    
    private static String toLowerFirstCase(String src){
        char[] chars=src.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}
LCLoader

LewClassLoader

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class LewClassLoader extends ClassLoader{
    private File classPathFile;
    
    public LewClassLoader(){
        String classPath=LewClassLoader.class.getResource("").getPath();
        this.classPathFile=new File(classPath);
    }
    //有一个很重要的方法findClass
    //它是通过加载机制找到Class文件,再告诉JDK,怎么加载
    //把一个class文件找出来后变成字节码数组(内存只支持字节码数组)
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException{
        String className=LewClassLoader.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;
                    //把class转化成内存能懂的字节码
                    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(null!=in){
                        try{
                            in.close();
                        }catch(IOException e){
                            e.printStackTrace();
                        }
                    }
                    if(out!=null){
                        try{
                            out.close();
                        }catch(IOException e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        
        return null;
    }

}
LewMeipo
public class LewMeipo implements LewInvocationHandler{
    //被代理的对象,把引用保存起来
    private IPerson target;
    //拿到目标类的引用
    public IPersion getInstance(IPerson target){
        this.target=target;
        Class<?> clazz=target.getClass();
        //需要传入Interface,需要拿到接口才好知道实现哪个接口
        return (IPerson) LewProxy.newProxyInstance(new LewClassLoader,clazz.getInterfaces(),this);
    }
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        before();
        Object result=method.invoke(this.target,args);
        after();
        return result;
    }
    private void after(){
        System.out.println("双方同意,开始交往");
    }
    private void before(){
        System.out.println("我是媒婆,已经收集到你的需求,开始物色");
    }
}

客户端

public class Test{
    public static void main(String[] args){
        LewMeipo lewMeipo=new LewMeipo();
        IPerson zhangsan=lewMeipo.getInstance(new Zhangsan());
        zhangsan.findLove();
    }
}

本质区别

  1. 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背了开闭原则
  2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循了开闭原则
  3. 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略模式便可完成,无需修改代理类的代码

Spring应用

Spring中的代理选择原则

  1. 当Bean有实现接口时,Spring就会用JDK动态代理

  2. 当Bean没有实现接口时,Spring会选择CGLib代理

  3. Spring可以通过配置强制使用CGLib代理,只需在Spring的配置文件中加入

    <aop:aspectj-autoproxy proxy-target-clas="true"/>
    

ProxyFactoryBean核心用法getObject(),源码如下:

package org.springframework.aop.framework;
public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
	public Object getObject() throws BeansException {
        this.initializeAdvisorChain();
        if (this.isSingleton()) {
            return this.getSingletonInstance();
        } else {
            if (this.targetName == null) {
                this.logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
            }

            return this.newPrototypeInstance();
        }
    }
}

在getObject()方法中,主要调用getSingletonInstance()和newPrototypeInstance()。在Spring的配置中如果不做任何设置,那么Spring代理生成的Bean都是单例对象。如果修改scope,则每次创建一个新的原型对象

Spring利用动态代理实现AOP时有两个非常重要的类:JdkDynamicAopProxy类和CglibAopProxy类

优点

  1. 代理模式能将代理对象与真实被调用目标对象分离
  2. 在一定程度上降低了系统的耦合性,扩展性好
  3. 可以起到保护目标对象的作用
  4. 可以增强目标对象的功能

缺点

  1. 代理模式会造成系统设计中类的数量增加
  2. 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢
  3. 增加系统的复杂度
  4. 动态代理造成频繁GC

作业

1、请总结静态代理和动态代理的根本区别。

静态代理:硬编码,手动注入(通过构造方法去声明,手动拿到目标对象的引用和方法)
动态代理:具有更强的扩展性,自动注入,自动生成新的类(同一个继承体系的类)
共同特征:拿到代理目标对象的引用
实现功能增强
保护目标对象

2、继续完成手写Proxy类中带参数方法的代理实现。

参考课堂笔记

门面模式

Facade Pattern

课程目标

1、掌握门面模式和装饰器模式的特征和应用场景

2、理解装饰器模式和代理模式的根本区别。

3、了解门面模式的优、缺点。

了解装饰器模式的优、缺点。

定义

门面模式又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接口

特征:门面模式定义了一个高层接口,让子系统更容易使用

属于结构型模式

应用场景

  1. 子系统越来越复杂,增加门面模式提供简单接口
  2. 构建多层系统结构,利用门面对象作为每层的入口,简化层间调用

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAV7ykWG-1647080017425)(D:\文件_Typora\1591854884039.png)]

门面模式主要包含两种角色:

外观角色(Facade):也称门面角色,系统对外的统一接口

子系统角色(SubSystem):可以同时有一个或多个SubSystem。每个SubSystem都不是一个单独的类,而是一个类的集合。SubSystem并不知道Facade的存在,对于SubSystem而言,Facade只是另一个客户端而已(即Facade对SubSystem透明)

通用代码

首先分别创建3个子系统的业务SubSystemA,SubSystemB,SubSystemC

public class SubSystemA{
    public void doA(){
        System.out.println("doing A stuff");
    }
}
public class SubSystemB{
    public void doB(){
        System.out.println("doing B stuff");
    }
}
public class SubSystemC{
    public void doC(){
        System.out.println("doing C stuff");
    }
}

再创建Facade类:

public class Facade{
    private SubSystemA a=new SubSystemA();
    private SubSystemB b=new SubSystemB();
    private SubSystemC c=new SubSystemC();
    
    //对外接口
    public void doA(){
        this.a.doA();
    }
    public void doB(){
        this.b.doB();
    }
    public void doC(){
        this.c.doC();
    }
}

客户端

public static void main(String[] args){
    Facade facade=new Facade();
    facade.doA();
    facade.doB();
    facade.doC();
}

业务场景

社区上线了一个积分兑换礼品的商城,这礼品商城中的大部分功能并不是全部重新开发的,而是要去对接已有的各个系统,这些子系统可能涉及积分系统,支付系统,物流系统的接口调用。如果所有的接口调用全部由前端发送网络请求去调用现有的接口的话,一则会增加前端开发人员的难度,二则会增加一些网络请求影响页面性能。这个时候就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口给前端调用,这样前端开发人员就不需要关系各接口的业务关系,只需要把精力集中在页面交互上。

首先创建礼品的实体类GiftInfo:

public class GiftInfo{
    private String name;
    public GiftInfo(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
}

编写各个子系统的业务逻辑代码,分别创建积分系统QualifyService类

public class QualifyService{
    public boolean isAvailable(GiftInfo giftInfo){
        System.out.println("校验"+giftInfo.getName()+"积分资格通过,库存通过");
        return true;
    }
}

支付系统PaymentService类

public class PaymentService{
    public boolean pay(GiftInfo pointsGift){
        //扣除积分
        SYstem.out.println("支付"+pointsGift.getName()+"积分成功");
        return true;
    }
}

物流系统ShippingService类

public class ShippingService{
    //发货
    public String delivery(GiftInfo giftInfo){
        //物流系统的对接逻辑
        System.out.println(giftInfo.getName()+"进入物流系统");
        String shippingOrderNo="666";
        return shippingOrderNo;
    }
}

然后创建外观角色GiftFacdeService类,对外只开放一个兑换礼物的exchange()方法,在exchange()方法内部整合3个子系统的所有功能

public class GiftFacadeService{
    private QualifyService qualifyService=new QualifyService();
    private PaymentService pointsPaymentService=new PaymentService();
    private ShippingService shippingService=new ShippingSerice();
    
    //兑换
    public void exchange(GiftInfo giftInfo){
        if(qualifyService.isAvailable(giftInfo)){
            //资格校验通过
            if(pointsPaymentService.pay(giftInfo)){
                //如果支付成功
                String shippingOrderNo=shippingService.delivery(giftInfo);
                System.out.println("物流系统下单成功,订单号是:"+shippingOrderNo);
            }
        }
    }
}

客户端

public static void main(String[] args){
    GiftInfo giftInfo=new GiftInfo("《???》");
    GiftFacadeService giftFacadeService=new GiftFacadeService();
    giftFacadeService.exchange(giftInfo);
}

源码应用

Spring JDBC模块下的JdbcUtils类

它调用的都不是Jdbc自己的功能,而是别人的,自己没干啥

package org.springframework.jdbc.support;

public abstract class JdbcUtils {
    public static final int TYPE_UNKNOWN = -2147483648;
    private static final boolean getObjectWithTypeAvailable;
    
    public JdbcUtils() {
    }

    public static void closeConnection(Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException var2) {
                logger.debug("Could not close JDBC Connection", var2);
            } catch (Throwable var3) {
                logger.debug("Unexpected exception on closing JDBC Connection", var3);
            }
        }

    }

    public static void closeStatement(Statement stmt) {
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC Statement", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC Statement", var3);
            }
        }

    }

    public static void closeResultSet(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException var2) {
                logger.trace("Could not close JDBC ResultSet", var2);
            } catch (Throwable var3) {
                logger.trace("Unexpected exception on closing JDBC ResultSet", var3);
            }
        }

    }
    .......
}

MyBatis中的Configuration类,有很多new开头的方法

package org.apache.ibatis.session;
public class Configuration {
    public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
  }

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

Tomcat中的RequestFacade,有很多get开头的方法

package org.apache.catalina.connector;
public class RequestFacade implements HttpServletRequest {
    public String getContentType() {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getContentType();
        }
    }

    public ServletInputStream getInputStream() throws IOException {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getInputStream();
        }
    }

    public String getParameter(String name) {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return Globals.IS_SECURITY_ENABLED ? (String)AccessController.doPrivileged(new RequestFacade.GetParameterPrivilegedAction(name)) : this.request.getParameter(name);
        }
    }
}

它封装了非常多的request的操作,也整合了很多servlet-api以外的一些内容,给用户使用提供很大的便捷。

优点

  1. 简化了调用过程,无需深入了解子系统,以防给子系统带来风险
  2. 减少系统依赖,松散耦合
  3. 更好地划分访问层次,提高了安全性
  4. 遵循迪米特法则,即最少知道原则
  5. 把操作集中到一个类,只用引入一个类

缺点

  1. 当增加子系统和扩展子系统行为时,可能容易带来位置风险
  2. 不符合开闭原则
  3. 某些情况下可能违背单一职责原则

对比

门面模式与代理模式:
门面模式就是特殊的静态代理模式,区别在于,门面模式的重点在于封装,静态代理的重点在于增强,不做增强的静态代理就是门面模式

委派模式也是一种静态代理,代理模式是结构型模式,委派模式是行为型模式,委派不属于23种

门面模式和单例模式:
很多时候,会把门面模式做成单例模式:工具包

与组合模式也相似

装饰器模式

Decorator Pattern

定义

装饰器模式也叫包装模式,是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。

以前只能通过继承,动态扩展

核心:透明的,动态的,功能扩展或撤销功能,和建造者模式有点像(X)

(建造者模式是把建造步骤开放给用户定制,但装饰器与步骤无关,是吧功能给用户去定制)

属于结构型模式

原理

让装饰器实现被包装类(Concrete Component)相同的接口(Component)(使得装饰器与被扩展类类型一致),并在构造函数中传入该接口(Component)对象,然后就可以在接口需要实现的方法中在被包装类对象的现有功能上添加新功能了。而且由于装饰器与被包装类属于同一类型(均为Componet),且构造函数的参数为其实现接口类(Component),因此装饰器模式具备嵌套扩展功能,这样我们就能使用装饰器模式一层一层的对最底层被包装类进行功能扩展

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vrbq76Dz-1647080017425)(D:\文件_Typora\1591854911398.png)]

装饰器模式主要包含4种角色:

抽象组件(Component):可以是一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;

具体组件(ConcreteComponent):实现/继承 Component的一个具体对象,也即被装饰对象;

抽象装饰器(Decorator):通用的装饰ConcreteComponent的装饰器,其内部必然有一个指向Component抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component抽象组件,这是强制的通用行为(当然,如果系统中装饰逻辑单一,并不需要实现许多装饰,就可以直接省略该类,而直接实现一个具体装饰器(ConcreteDecorator)即可)

具体装饰器(ConcreteDecorator):Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能

应用场景

  1. 用于扩展一个类的功能或给一个类添加附加职责
  2. 动态的给一个对象添加功能,这些功能可以再动态的撤销
  3. 需要为一批的兄弟类进行改装或加重功能

通用写法

优化前

创建煎饼Battercake类

public class Battercake{
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){
        return 5;
    }
}

创建一个加鸡蛋的煎饼BattercakeWithEgg类

public class BattercakeWithEgg extends Battercake{
    @Override
    protected String getMsg(){
        return super.getMsg()+"+1个鸡蛋";
    }
    @Override
    //加鸡蛋一块钱
    public int getPrice(){
        return super.getPrice()+1;
    }
}

再创建一个既加鸡蛋又加香肠的BattercakeWithEggAndSausage类

public class BattercakeWithEggAndSausage extends BattercakeWithEgg{
    @Override
    protected String getMsg(){
        return super.getMsg()+"+1根香肠";
    }
    
    @Ovrride
    //加香肠两块钱
    public int getPrice(){
        return super.getPrice()+2;
    }
}

客户端

public class BattercakeTest{
    public static void main(String[] args){
        Battercake battercake=new Battercake();
        System.out.println(battercake.getMsg()+",总价格:"+battercake.getPrice());
        
        Battercake battercakeWithEgg=new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg()+",总价格:"+battercakeWithEgg.getPrice());
        
        Battercake battercakeWithEggAndSausage=new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg()+",总价格:"+battercakeWithEggAndSausage.getPrice());
    }
}

缺点

如果用户需要一个加2个鸡蛋和一根香肠的煎饼,是创建不出来的,也无法自动计算加个,除非再创建一个类做定制

优化后

Battercake

Battercake类

public abstract class Battercake{
    protected abstract String getMsg();
    protected abstract int getPrice();
}
BaseBattercake

创建一个基本的煎饼(基础套餐)BaseBattercake

public class BaseBattercake extends Battercake{
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){return 5;}
}
BattercakeDecotator

再创建一个扩展套餐的抽象装饰器BattercakeDecotator

public abstract class BattercakeDecorator extends Battercake{
    //静态代理,委派
    private Battercake battercake;
    
    public BattercakeDecorator(Battercake battercake){
        this.battercake=battercake;
    }
    
    protected abstract void doSomething();
    
    @Override
    protected String getMsg(){
        return this.battercake.getMsg();
    }
    
    @Override 
    protected int getPrice(){
        return this.battercake.getPrice();
    }
}
EggDecorator

创建鸡蛋装饰器EggDecorator类

public class EggDecorator extends BattercakeDecorator{
    public EggDecorator(Battercake battercake){
        super(battercake);
    }
    
    protected void doSomething(){}
    
    @Override
    protected String getMsg(){
        return super.getMsg()+"+1个鸡蛋";
    }
    
    @Override
    protected int getPrice(){
        return super.getPrice()+1;
    }
}
SausageDecorator

创建香肠装饰器SausageDecorator

public class SausageDecorator extends BattercakeDecorator{
    public SausageDecorator(Battercake battercake){
        super(battercake);
    }
    
    protected void doSomething(){}
    
    @Override
    protected String getMsg(){
        return super.getMsg()+"+1根香肠";
    }
    
    @Override
    protected int getPrice(){
        return super.getPrice()+2;
    }
}
客户端
public class BattercakeTest{
    public static void main(String[] args){
        //声明基本的
        Battercake battercake;
        //买一个煎饼
        //声明扩展的
        battercake=new BaseBattercake();
        //再加一个鸡蛋
        battercake=new EggDecorator(battercake);
        //再加一个鸡蛋
        battercake=new EggDecorator(battercake);
        //在加根香肠
        battercake=new SausageDecorator(battercake);
        
        //跟静态代理最大的区别就是职责不同
        //静态代理不一定要满足is-a的关系
        //静态代理会做功能增强,同一个职责变得不一样
        
        //装饰器模式更多考虑是扩展
        System.out.println(battercake.getMsg()+",总价:"+battercake.getPrice());
    }
}

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6i8T2Sf4-1647080017426)(D:\文件_Typora\1591854956687.png)]

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CG7IMRzC-1647080017426)(D:\文件_Typora\1591861972765.png)]

业务场景

系统采用的是sls服务监控项目日志,以Json的格式解析,所以需要将项目中的日志封装成json格式再打印。现有的日志体系采用log4j+slf4j框架搭建而成。客户端调用是这样:

private static final Logger logger=LoggerFactory.getLogger(Component.class);
logger.error(String);

这样打印的是无规则字符串,在考虑将其转化成json格式时采用了装饰器模式。目前有的,是统一接口Logger和其具体实现类,我要加的就是一个装饰类和真正封装成Json格式的装饰产品类。

DecotatorLogger

创建装饰器类DecotatorLogger:

public class DecoratorLogger implements Logger{
    public Logger logger;
    //最大的特征
    public DecoratorLogger(Logger logger){
        this.logger=logger;
    }
    public void error(String str){}
    public void error(String s,Object o){}
    //省略其他默认实现
}

JsonLogger

创建具体组件JsonLogger类实现代码如下:

public class JsonLogger extends DecoratorLogger{
    public JsonLogger(Logger logger){
        super(logger);
    }
    @Override
    public void info(String msg){
        JSONObject result=composeBasicJsonResult();
        result.put("MESSAGE",msg);
        logger.info(result.toString());
    }
    @Override
    public void error(String msg){
        JSONObject result=composeBasicJsonResult();
        result.put("MESSAGE",msg);
        logger.error(result.toString());
    }
    public void error(Exception e){
        JSONObject result=composeBasicJsonResult();
        result.put("EXCEPTION",e.getClass().getName());
        String exceptionStackTrace=Arrays.toString(e.getStackTrace());
        result.put("STACKTRACE",exceptionStackTrace);
        logger.error(result.toString());
    }
    //写重载方法,将Exception1也转为Json
    public void error(Exception e){
        JSONObject result=composeBasicJsonResult();
        result.put("exception",e.getClass().getName());
        String trace=Arrays.toString(e.getStackTrace());
        result.put("starckTrace",trace);
        logger.error(result.toString());
    }
    //专门用于包装的方法
    private JSONObject composeBasicJsonResult(){
        //拼装了一些运行时信息
        return new JSONObject();
    }
}

JsonLoggerFactory

public class JsonLoggerFactory{
    public static JsonLogger getLogger(Class clazz){
        Logger logger=LoggerFactory.getLogger(clazz);
        return new JsonLogger(logger);
    }
}

可以看见,在JsonLogger中,对于Logger的各种接口,我都用JsonObject对象进行一层封装。在打印的时候,最终还是调用原生接口logger.error(string),只是这个string参数已经被我修饰过。如果有额外的参数,我们也可以再写一个函数去实现。比如error(Exception e),只传入一个异常对象,这样在调用时就非常方便了。

另外,为了在新老交替的过程中尽量不改变太多的代码和使用方式。我又在JsonLogger中加入了一个内部的工厂类JsonLoggerFactory(这个类转移到DecoratorLogger中可能更好一些),他包含一个镜头方法,用于提供对应的JsonLogger实例。最终在新的日志体系中,使用方式如下:

调用

//private static final Logger logger=LoggerFactory.getLogger(client.class);
private static final Logger logger=JsonLoggerFactory.getLogger(client.class);
public static void main(String[] args){
    logger.error("错误信息");
}

对客户端而言,唯一与原先不同的地方就是将LoggerFactory改成JsonLoggerFactory即可

类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MpR6lYx-1647080017426)(D:\文件_Typora\1591854980057.png)]

装饰器模式最本质的特征是将原有类的附加功能抽离出来,简化原有类的逻辑。

总结

其实抽象的装饰器是可有可无的,具体根据业务模型来选择

源码应用

JDK中的IO相关类,BufferedReader,InputStream(一个一个读),OutputStream,BufferedInputStream(一次读)

BufferedReader br=new BufferedReader(new FileReader());
BufferedReader br=new BufferedReader(new StringReader());

InputStream的类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qEjronw-1647080017426)(D:\文件_Typora\1591856692693.png)]

Spring中的TransactionAwareCacheDecorator类,用来处理事务缓存。TransactionAwareCacheDecorator就是对Cache的一个包装

package org.springframework.cache.transaction;
public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache;

    //它的构造方法一定会传Cache,最大特征
    public TransactionAwareCacheDecorator(Cache targetCache) {
        Assert.notNull(targetCache, "Target Cache must not be null");
        this.targetCache = targetCache;
    }

    public Cache getTargetCache() {
        return this.targetCache;
    }
    ...
}

MVC中的HttpHeadResponseDecorator类

public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator{
    public HttpHeadResponseDecorator(ServerHttpResponse delegate){
        super(delegate);
    }
    ...
}

MyBatis中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHeFr1dC-1647080017427)(D:\文件_Typora\1591860494471.png)]

从名字上看很容易理解。比如FifoCache先入先出算法的缓存;LruCache对最近最少使用的缓存;TransactionICache事务相关的缓存,都是采用装饰器模式。

对比

装饰器模式就是一种特殊的静态代理

从代理模式的UML类图和通用代码实现上看,代理模式与装饰器模式几乎一模一样,代理模式的Subject对应装饰器模式的Component,代理模式的RealSubject对应装饰器模式的ConcreteComponent,代理模式的Proxy对应装饰器模式的Decorator。但这两种模式所面向的功能扩展面试不一样的:

装饰器模式强调自身功能的扩展。Decorator所做的就是增强ConcreteComponent的功能(也有可能减弱功能),主体对象为ConcreteComponent,着重类功能的变化;

代理模式强调对代理过程的控制。Proxy完全掌握对RealSubject的访问控制,因此,Proxy可以决定对RealSubject进行功能扩展,功能缩减甚至功能散失(不调用RealSubject方法),主体对象为Proxy;

简单来讲,假设现在小明想租房,那么势必会有一些事务方式:房源搜索,联系房东谈价格…

假设我们按照代理模式进行思考,那么小明只需找到一个房产中介,让他去干房源搜索,联系房东谈价格,小明只需等待通知然后付中介费

而如若采用装饰器模式进行思考,因为装饰器模式强调的是自身功能扩展,也就是说,小明自身增加这些能力,一个人做完所有的事

优点

  1. 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象的扩展功能,即插即用
  2. 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
  3. 装饰器完全遵守开闭原则

缺点

  1. 会出现更多的代码,更多的类,增加程序复杂性
  2. 动态装饰时,多层装饰时会更复杂

作业

1、请举例3-5个使用门面模式的应用场景。

1. 项目开发中的Dao,Controller,Service
2. 微服务rpc调用时,通过门面模式将不同子系统提供的服务整合
3. 日志框架中将打印DEBUG,INFO,WARN,ERROR不同日志级别的方法通过一个接口对外暴露

2、使用装饰器模式实现一个可根据权限动态扩展功能的导航条。

装饰器代码

//功能导航接口
public interface INav{
    String showNavs();
}
//原始组件
public class CommonNav implements INav{
    @Override
    public String showNavs(){
        return "问答--文章--精品课--冒泡--商城";
    }
}
//抽象装饰器
public abstract class AbsDecorator implements INav{
    protected INav nav;
    protected AbsDecorator(INav nav){
        this nav=nav;
    }
    @Override
    public abstract String showNavs();
}
//题库导航装饰
public class QuestionNav extends AbsDecorator{
    public QuestionNav(INav nav){
        super(nav);
    }
    @Override
    public String showNavs(){
        return nav.showNavs()+"--题库";
    }
}
//作业导航装饰
public class TaskNav extends AbsDecorator{
    public TaskNav(INav nav){
        super(nav);
    }
    @Override
    public String showNavs(){
        return nav.showNavs()+"--作业";
    }
}

权限代码

//权限接口,用于扩展不同权限
public interface IPermission{
    //展示权限拥有的功能导航
    String showPermNavs();
}
//未登陆用户,拥有CommonNav
public class NotLogInUer implements IPermission{
    @Override
    public String showPermNavs(){
        return new CommonNav().showNavs();
    }
}
//已登陆用户
public class LogInUser implements IPermission{
    @Override
    public String showPermNavs(){
        return new QuestionNav(new CommonNav()).showNavs();
    }
}

客户端

public static void main(String[] args){
    IPermission notLogInUser=new NotLogInUser();
    System.out.println(notLogInUser.showPermNavs());
    
    IPermission logInUser=new LogInUser();
    System.out.println(logInUser.showPermNavs());
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值