哈尔滨建站模板厂家网页制作公司印章怎么弄
- 作者: 五速梦信息网
- 时间: 2026年04月20日 11:01
当前位置: 首页 > news >正文
哈尔滨建站模板厂家,网页制作公司印章怎么弄,室内设计效果图用什么软件最好,百度域名值多少钱单例模式 概念 一个类只允许创建一个对象#xff08;或实例#xff09;#xff0c;那这个类就是单例类#xff0c;这种设计模式就叫做单例模式。对于一些类#xff0c;创建和销毁比较复杂#xff0c;如果每次使用都创建一个对象会很耗费性能#xff0c;因此可以把它设…单例模式 概念 一个类只允许创建一个对象或实例那这个类就是单例类这种设计模式就叫做单例模式。对于一些类创建和销毁比较复杂如果每次使用都创建一个对象会很耗费性能因此可以把它设置为单例类。有的地方会用数据库连接池来举例实际上一些数据库连接池、线程池是没有被设计成单例类的这点在下面单例模式存在的问题中会讲。 如何实现一个单例类 需要关注以下几点 构造函数需要时private访问权限这样才能避免外部通过new创建实例对象创建是否是线程安全是否支持懒加载getInstance()性能是否达标有无加锁能否通过反射破坏通过反射创建实例 饿汉式 public class Singleton {private Singleton() {} // 构造器私有private static final Singleton instance new Singleton();public static Singleton getInstance() {return instance;} } 在类加载的时候instace静态实例就已经创建并初始化好了所以饿汉式是线程安全的。不过饿汉式不支持懒加载在真正用到Singleton的时候才创建实例。 有人说这种实现方式不好认为懒加载的好处是只有真正使用到的时候才会创建防止一些对象的构建比较耗费性能比如需要加载各种配置文件且在下一次创建对象之前从没有被使用过会造成资源浪费。 不过我更赞同另一种观点如果初始化耗时长那么我们最好不要等到真正要用它的时候才去执行这个初始化过程比如在响应客户端请求的时候做这个初始化操作会导致请求的响应时间变长。并且如果实例占用资源多按照fail-fast设计原则有问题及早暴露我们也希望在程序启动时就将这个实例初始化好如果资源不够就会在程序启动时触发报错也可以避免一些程序运行一段时间后因为初始化实例占用资源过多而报错的情况。 懒汉式 public class Singleton {private Singleton() {} // 构造器私有private static Singleton instance null;// 初始化对象为nul1public static synchronized Singleton getInstance() {if (instance null) {instance new Singleton();}return instance;} } 懒汉式的缺点很明显就是synchronized锁粒度太粗同一时间只能有一个线程访问getInstace()方法去尝试获取实例如果这个实例被频繁用到那么加锁释放锁、以及方法的并发度问题会导致性能瓶颈。 双检锁 public class Singleton {private AtomicLong id new AtomicLong(0);private static Singleton instance null;private Singleton() {} // 构造器私有public static Singleton getInstance() {if (instance null) { // 1synchronized (Singleton.class) { // 2if (instance null) { // 虽然只能有一个线程进入2但可能有其他线程在1处等待释放锁因此需要二次校验instance new Singleton();}}}return instance;}public long getId(){return id.incrementAndGet();} } 这种实现方式还有一个问题因为指令重排序可能会导致Singleton对象被new出来并且赋值给instance后还没来得及初始化在指令层面赋值那行被分为三步1、分配内存2、初始化对象3、对象指向内存地址就被另一个线程使用了。 要解决这个问题instance实例需要加上volatile关键字禁止指令重排序。 不过还有人说只有很低版本的Java才会有这个问题高版本的Java已经在jdk内部实现中解决了这个问题通过把前面三步改为原子操作。这块博主问了gpt后无果暂时先放在这里。 静态内部类 public class IdGenerator {private AtomicLong id new AtomicLong(0);private IdGenerator() {}private static class SingletonHolder{private static final IdGenerator instance new IdGenerator();}public static IdGenerator getInstance() {return SingletonHolder.instance;}public long getId() {return id.incrementAndGet();} } 静态内部类在程序启动的时候不会加载只有在第一次被调用的时候才会加载。instance的唯一性、线程安全都由JVM来保证。 枚举 public enum IdGenerator {INSTANCE;private AtomicLong id new AtomicLong(0);public long getId() { return id.incrementAndGet();} } 上面4种写法都是会被反射破坏的不过反射是一种人为的方式不会有太大影响。而这个枚举方式是不能通过反射进行构建的在效果上类似饿汉式通过Java枚举类型的特性在类加载的时候就会创建对应的实例。 单例模式存在的问题 大部分情况下我们在项目中使用单例都是用它来表示一些全局唯一类比如配置信息类、连接池类、ID生成器类。单例模式书写简洁、使用方便在代码中我们不需要创建对象直接通过类似IdGenerator.getInstance().getId()这样的方法来调用就可以了。但是这种使用方法有点类似硬编码hard code会带来诸多问题。接下来我们就具体看看到底有哪些问题。 1.单例对OOP特性的支持不友好 我们知道OOP的四大特性是封装、抽象、继承、多态。单例这种设计模式对于其中的抽象、继承、多态都支持得不好。为什么这么说呢我们还是通过IdGenerator这个例子来讲解。 public class Order {public void create(…) {//…long id IdGenerator.getInstance().getId();//…} }public class User {public void create(…) {// …long id IdGenerator.getInstance().getId();//…} } IdGenerator的使用方式违背了基于接口而非实现的设计原则也就违背了广义上理解的OOP的抽象特性。如果未来某一天我们希望针对不同的业务采用不同的ID生成算法。比如订单ID和用户ID采用不同的ID生成器来生成。为了应对这个需求变化我们需要修改所有用到IdGenerator类的地方这样代码的改动就会比较大。 public class Order {public void create(…) {//…long id IdGenerator.getInstance().getId();// 需要将上面一行代码替换为下面一行代码long id OrderIdGenerator.getIntance().getId();//…} }public class User {public void create(…) {// …long id IdGenerator.getInstance().getId();// 需要将上面一行代码替换为下面一行代码long id UserIdGenerator.getIntance().getId();} } 除此之外单例对继承、多态特性的支持也不友好。这里我之所以会用“不友好”这个词而非“完全不支持”是因为从理论上来讲单例类也可以被继承、也可以实现多态只是实现起来会非常奇怪会导致代码的可读性变差。不明白设计意图的人看到这样的设计会觉得莫名其妙。所以一旦你选择将某个类设计成到单例类也就意味着放弃了继承和多态这两个强有力的面向对象特性也就相当于损失了可以应对未来需求变化的扩展性。 2.单例会隐藏类之间的依赖关系 我们知道代码的可读性非常重要。在阅读代码的时候我们希望一眼就能看出类与类之间的依赖关系搞清楚这个类依赖了哪些外部类。 通过构造函数、参数传递等方式声明的类之间的依赖关系我们通过查看函数的定义就能很容易识别出来。但是单例类不需要显示创建、不需要依赖参数传递在函数中直接调用就可以了。如果代码比较复杂这种调用关系就会非常隐蔽。在阅读代码的时候我们就需要仔细查看每个函数的代码实现才能知道这个类到底依赖了哪些单例类。 3.单例对代码的扩展性不友好 我们知道单例类只能有一个对象实例。如果未来某一天我们需要在代码中创建两个实例或多个实例那就要对代码有比较大的改动。你可能会说会有这样的需求吗既然单例类大部分情况下都用来表示全局类怎么会需要两个或者多个实例呢 实际上这样的需求并不少见。我们拿数据库连接池来举例解释一下。 在系统设计初期我们觉得系统中只应该有一个数据库连接池这样能方便我们控制对数据库连接资源的消耗。所以我们把数据库连接池类设计成了单例类。但之后我们发现系统中有些SQL语句运行得非常慢。这些SQL语句在执行的时候长时间占用数据库连接资源导致其他SQL请求无法响应。为了解决这个问题我们希望将慢SQL与其他SQL隔离开来执行。为了实现这样的目的我们可以在系统中创建两个数据库连接池慢SQL独享一个数据库连接池其他SQL独享另外一个数据库连接池这样就能避免慢SQL影响到其他SQL的执行。 如果我们将数据库连接池设计成单例类显然就无法适应这样的需求变更也就是说单例类在某些情况下会影响代码的扩展性、灵活性。所以数据库连接池、线程池这类的资源池最好还是不要设计成单例类。实际上一些开源的数据库连接池、线程池也确实没有设计成单例类。 4.单例对代码的可测试性不友好 单例模式的使用会影响到代码的可测试性。如果单例类依赖比较重的外部资源比如DB我们在写单元测试的时候希望能通过mock的方式将它替换掉。而单例类这种硬编码式的使用方式导致无法实现mock替换。 除此之外如果单例类持有成员变量比如IdGenerator中的id成员变量那它实际上相当于一种全局变量被所有的代码共享。如果这个全局变量是一个可变全局变量也就是说它的成员变量是可以被修改的那我们在编写单元测试的时候还需要注意不同测试用例之间修改了单例类中的同一个成员变量的值从而导致测试结果互相影响的问题。 5.单例不支持有参数的构造函数 单例不支持有参数的构造函数比如我们创建一个连接池的单例对象我们没法通过参数来指定连接池的大小。针对这个问题我们来看下都有哪些解决方案。 第一种解决思路是创建完实例之后再调用init()函数传递参数。需要注意的是我们在使用这个单例类的时候要先调用init()方法然后才能调用getInstance()方法否则代码会抛出异常。具体的代码实现如下所示 public class Singleton {private static Singleton instance null;private final int paramA;private final int paramB;private Singleton(int paramA, int paramB) {this.paramA paramA;this.paramB paramB;}public static Singleton getInstance() {if (instance null) {throw new RuntimeException(Run init() first.);}return instance;}public synchronized static Singleton init(int paramA, int paramB) {if (instance ! null){throw new RuntimeException(Singleton has been created!);}instance new Singleton(paramA, paramB);return instance;} }Singleton.init(10, 50); // 先init再使用 Singleton singleton Singleton.getInstance(); 第二种解决思路是将参数放到getIntance()方法中。具体的代码实现如下所示 public class Singleton {private static Singleton instance null;private final int paramA;private final int paramB;private Singleton(int paramA, int paramB) {this.paramA paramA;this.paramB paramB;}public synchronized static Singleton getInstance(int paramA, int paramB) {if (instance null) {instance new Singleton(paramA, paramB);}return instance;} }Singleton singleton Singleton.getInstance(10, 50); 不知道你有没有发现上面的代码实现稍微有点问题。如果我们如下两次执行getInstance()方法那获取到的singleton1和signleton2的paramA和paramB都是10和50。也就是说第二次的参数2030没有起作用而构建的过程也没有给与提示这样就会误导用户。这个问题如何解决呢留给你自己思考你可以在留言区说说你的解决思路。 Singleton singleton1 Singleton.getInstance(10, 50); Singleton singleton2 Singleton.getInstance(20, 30); 第三种解决思路是将参数放到另外一个全局变量中。具体的代码实现如下。Config是一个存储了paramA和paramB值的全局变量。里面的值既可以像下面的代码那样通过静态常量来定义也可以从配置文件中加载得到。实际上这种方式是最值得推荐的。 public class Config {public static final int PARAM_A 123;public static final int PARAM_B 245; }public class Singleton {private static Singleton instance null;private final int paramA;private final int paramB;private Singleton() {this.paramA Config.PARAM_A;this.paramB Config.PARAM_B;}public synchronized static Singleton getInstance() {if (instance null) {instance new Singleton();}return instance;} } 有何替代解决方案 刚刚我们提到了单例的很多问题你可能会说即便单例有这么多问题但我不用不行啊。我业务上有表示全局唯一类的需求如果不用单例我怎么才能保证这个类的对象全局唯一呢 为了保证全局唯一除了使用单例我们还可以用静态方法来实现。这也是项目开发中经常用到的一种实现思路。比如上一节课中讲的ID唯一递增生成器的例子用静态方法实现一下就是下面这个样子 // 静态方法实现方式 public class IdGenerator {private static AtomicLong id new AtomicLong(0);public static long getId() { return id.incrementAndGet();} } // 使用举例 long id IdGenerator.getId(); 不过静态方法这种实现思路并不能解决我们之前提到的问题。实际上它比单例更加不灵活比如它无法支持延迟加载。我们再来看看有没有其他办法。实际上单例除了我们之前讲到的使用方法之外还有另外一种使用方法。具体的代码如下所示 // 1. 老的使用方式 public demofunction() {//…long id IdGenerator.getInstance().getId();//… }// 2. 新的使用方式依赖注入 public demofunction(IdGenerator idGenerator) {long id idGenerator.getId(); } // 外部调用demofunction()的时候传入idGenerator IdGenerator idGenerator IdGenerator.getInsance(); demofunction(idGenerator); 基于新的使用方式我们将单例生成的对象作为参数传递给函数也可以通过构造函数传递给类的成员变量可以解决单例隐藏类之间依赖关系的问题。不过对于单例存在的其他问题比如对OOP特性、扩展性、可测性不友好等问题还是无法解决。 所以如果要完全解决这些问题我们可能要从根上寻找其他方式来实现全局唯一类。实际上类对象的全局唯一性可以通过多种不同的方式来保证。我们既可以通过单例模式来强制保证也可以通过工厂模式、IOC容器比如Spring IOC容器来保证还可以通过程序员自己来保证自己在编写代码的时候自己保证不要创建两个类对象。这就类似Java中内存对象的释放由JVM来负责而C中由程序员自己负责道理是一样的。 总结 上面写出了五种单例模式的经典实现方案其中懒汉式由于线程不安全是不可取的其他几种实现在功能上都没有太大问题可以根据需求选择。 单例存在哪些问题 单例对OOP特性的支持不友好单例会隐藏类之间的依赖关系单例对代码的扩展性不友好单例对代码的可测试性不友好单例不支持有参数的构造函数 单例有什么替代解决方案 为了保证全局唯一除了使用单例我们还可以用静态方法来实现。不过静态方法这种实现思路并不能解决我们之前提到的问题。如果要完全解决这些问题我们可能要从根上寻找其他方式来实现全局唯一类了。比如通过工厂模式、IOC容器比如Spring IOC容器来保证由程序员自己来保证自己在编写代码的时候自己保证不要创建两个类对象。 有人把单例当作反模式主张杜绝在项目中使用。我个人觉得这有点极端。模式没有对错关键看你怎么用。如果单例类并没有后续扩展的需求并且不依赖外部系统那设计成单例类就没有太大问题。对于一些全局的类我们在其他地方new的话还要在类之间传来传去不如直接做成单例类使用起来简洁方便。 工厂模式 概念 一般工厂模式分为简单工厂、工厂方法、抽象工厂在Gof-23中简单工厂被看做是工厂方法的一种特例抽象工厂在实际项目中不算常见。 简单工厂 日常开发中一些场景会遇到根据不同的条件创建不同的对象比如根据配置文件的后缀创建不同的解析器对象等把这些if判断和创建对象的代码单独提出一个函数可以使逻辑更清晰如果进一步把这个函数放到一个独立的类中那么这个类就是简单工厂模式的类。示例 public class RuleConfigParserFactory {public static IRuleConfigParser createParser(String configFormat) {IRuleConfigParser parser null;if (json.equalsIgnoreCase(configFormat)) {parser new JsonRuleConfigParser();} else if (xml.equalsIgnoreCase(configFormat)) {parser new XmlRuleConfigParser();} else if (yaml.equalsIgnoreCase(configFormat)) {parser new YamlRuleConfigParser();} else if (properties.equalsIgnoreCase(configFormat)) {parser new PropertiesRuleConfigParser();}return parser;} } 一般这个类会用Factory后缀命名但也不是必须的比如DateFormat、Calender。另外工厂类中创建对象的方法一般是create开头有的也会命名为get。。new。。甚至String类的valueOf()函数。 上面的实现中每次调用create方法都要创建一个新的parser可以通过先创建好缓存起来来节省内存和对象创建的时间这可以说是单例模式和简单工厂模式的结合示例 public class RuleConfigParserFactory {private static final Map cachedParsers new HashMap();static {cachedParsers.put(json, new JsonRuleConfigParser());cachedParsers.put(xml, new XmlRuleConfigParser());cachedParsers.put(yaml, new YamlRuleConfigParser());cachedParsers.put(properties, new PropertiesRuleConfigParser());}public static IRuleConfigParser createParser(String configFormat) {if (configFormat null || configFormat.isEmpty()) {return null;//返回null还是IllegalArgumentException全凭你自己说了算}IRuleConfigParser parser cachedParsers.get(configFormat.toLowerCase());return parser;} } 对于上面两种实现方式如果要添加新的解析器就需要改动工厂类的代码会违反开闭原则。不过实际上如果不是频繁地改动这部分代码稍微不符合开闭原则也是可以接受的。 工厂方法 如果非要不改动Factory类的代码该怎么做呢一个经典的方式是利用多态示例如下 public interface IRuleConfigParserFactory {IRuleConfigParser createParser(); }public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {Overridepublic IRuleConfigParser createParser() {return new JsonRuleConfigParser();} }public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {Overridepublic IRuleConfigParser createParser() {return new XmlRuleConfigParser();} }public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {Overridepublic IRuleConfigParser createParser() {return new YamlRuleConfigParser();} }public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {Overridepublic IRuleConfigParser createParser() {return new PropertiesRuleConfigParser();} } 这样实现如果新增parser只需要新增一个实现了IRuleConfigParserFactory 接口的Factory类即可。所以工厂方法模式比简单工厂模式更符合开闭原则。 但实际上上面的工厂方法在实现上有挺大的问题因为工厂类没有实现if判断所以需要先if判断类型后再决定创建哪个具体的类对象这就和不使用工厂模式几乎没有区别了。下面这段是包括了创建Parser解析器之后使用他的一段代码示例 public class RuleConfigSource {public RuleConfig load(String ruleConfigFilePath) {String ruleConfigFileExtension getFileExtension(ruleConfigFilePath);IRuleConfigParserFactory parserFactory null;// 工厂方法中只是创建了解析器需要在前面if判断创建哪个类对象if (json.equalsIgnoreCase(ruleConfigFileExtension)) {parserFactory new JsonRuleConfigParserFactory();} else if (xml.equalsIgnoreCase(ruleConfigFileExtension)) {parserFactory new XmlRuleConfigParserFactory();} else if (yaml.equalsIgnoreCase(ruleConfigFileExtension)) {parserFactory new YamlRuleConfigParserFactory();} else if (properties.equalsIgnoreCase(ruleConfigFileExtension)) {parserFactory new PropertiesRuleConfigParserFactory();} else {throw new InvalidRuleConfigException(Rule config file format is not supported: ruleConfigFilePath);}IRuleConfigParser parser parserFactory.createParser();String configText ;//从ruleConfigFilePath文件中读取配置文本到configText中RuleConfig ruleConfig parser.parse(configText);return ruleConfig;}private String getFileExtension(String filePath) {//…解析文件名获取扩展名比如rule.json返回jsonreturn json;} } 这个问题的一般解决思路是为工厂类再创建一个简单工厂这个简单工厂用来创建工厂类对象代码示例如下 public class RuleConfigSource {public RuleConfig load(String ruleConfigFilePath) {String ruleConfigFileExtension getFileExtension(ruleConfigFilePath);IRuleConfigParserFactory parserFactory RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);if (parserFactory null) {throw new InvalidRuleConfigException(Rule config file format is not supported: ruleConfigFilePath);}IRuleConfigParser parser parserFactory.createParser();String configText ;//从ruleConfigFilePath文件中读取配置文本到configText中RuleConfig ruleConfig parser.parse(configText);return ruleConfig;}private String getFileExtension(String filePath) {//…解析文件名获取扩展名比如rule.json返回jsonreturn json;} }//因为工厂类只包含方法不包含成员变量完全可以复用 //不需要每次都创建新的工厂类对象所以简单工厂模式的第二种实现思路更加合适。 public class RuleConfigParserFactoryMap { //工厂的工厂private static final Map cachedFactories new HashMap();static {cachedFactories.put(json, new JsonRuleConfigParserFactory());cachedFactories.put(xml, new XmlRuleConfigParserFactory());cachedFactories.put(yaml, new YamlRuleConfigParserFactory());cachedFactories.put(properties, new PropertiesRuleConfigParserFactory());}public static IRuleConfigParserFactory getParserFactory(String type) {if (type null || type.isEmpty()) {return null;}IRuleConfigParserFactory parserFactory cachedFactories.get(type.toLowerCase());return parserFactory;} } 其实也很好理解就是把之前简单工厂的迭代过程重演了一遍目标实例从解析器对象变成了类对象。 抽象工厂 不算常用在上面两个例子中类只有一种分类方式IRuleConfigParser 。假设现在场景需要另一种解析规则 针对规则配置的解析器基于接口IRuleConfigParser JsonRuleConfigParser XmlRuleConfigParser YamlRuleConfigParser PropertiesRuleConfigParser 针对系统配置的解析器基于接口ISystemConfigParser JsonSystemConfigParser XmlSystemConfigParser YamlSystemConfigParser PropertiesSystemConfigParser 创建的类就需要翻倍了此时可以改为以下写法 public interface IConfigParserFactory {IRuleConfigParser createRuleParser();ISystemConfigParser createSystemParser();//此处可以扩展新的parser类型比如IBizConfigParser }public class JsonConfigParserFactory implements IConfigParserFactory {Overridepublic IRuleConfigParser createRuleParser() {return new JsonRuleConfigParser();}Overridepublic ISystemConfigParser createSystemParser() {return new JsonSystemConfigParser();} }public class XmlConfigParserFactory implements IConfigParserFactory {Overridepublic IRuleConfigParser createRuleParser() {return new XmlRuleConfigParser();}Overridepublic ISystemConfigParser createSystemParser() {return new XmlSystemConfigParser();} } 工厂类中增加另一个解析规则的对象的实现方法。 总结 如果某个代码块变的比较复杂为了让代码更清晰就可以考虑单独拆除一个工厂类。此外如果想避免if-else逻辑就可以考虑工厂方法。简单工厂的单例实现在if-else方面和工厂方法没什么不同感觉两个模式最大的区别是工厂方法可以在去除if-else的基础上定制化createParser()方法。 因此当对象的创建逻辑比较简单时推荐用简单工厂模式当创建逻辑比较复杂的时候为了避免出现一个过于庞大的简单工厂类推荐用工厂方法模式。 扩展-DI框架中的应用 一个简单的DI容器的核心功能一般有三个配置解析、对象创建和对象生命周期管理。 配置解析 在上面的工厂模式中要创建哪个类对象是事先确定好的并且是写死在工厂类代码中的。作为一个通用的框架框架代码跟应用代码应该是高度解耦的DI容器并不知道应用将会创建哪些对象。所以我们需要通过一种形式让应用告知DI容器要创建哪些对象这种形式就是配置解析。 比如Spring中通过依赖注入的方式让DI容器解析配置。 对象创建 在DI容器中如果我们给每个类都创建一个工厂类是不现实的我们只需要将所有类对象的创建都放到一个工厂类中完成就可以了比如BeanFactory。 你可能会说要创建的类非常多BeanFactory中的代码会不会线性膨胀实际上不会原因就是反射机制在程序运行的过程中动态地加载类、创建对象。 生命周期管理 在上面的简单工厂模式有两种实现方式一种是每次都返回新创建的对象另一种是每次都返回同一个实现创建好的对象也就是所谓的单例对象。比如Spring中通过scope来配置。 除此之外还可以配置对象是否支持懒加载。另外还可以配置对象的初始化方法和销毁前方法。 实现 最后我们来看BeansFactory是如何设计和实现的。这也是我们这个DI容器最核心的一个类了。它负责根据从配置文件解析得到的BeanDefinition来创建对象。 如果对象的scope属性是singleton那对象创建之后会缓存在singletonObjects这样一个map中下次再请求此对象的时候直接从map中取出返回不需要重新创建。如果对象的scope属性是prototype那每次请求对象BeansFactory都会创建一个新的对象返回。 实际上BeansFactory创建对象用到的主要技术点就是Java中的反射语法一种动态加载类和创建对象的机制。我们知道JVM在启动的时候会根据代码自动地加载类、创建对象。至于都要加载哪些类、创建哪些对象这些都是在代码中写死的或者说提前写好的。但是如果某个对象的创建并不是写死在代码中而是放到配置文件中我们需要在程序运行期间动态地根据配置文件来加载类、创建对象那这部分工作就没法让JVM帮我们自动完成了我们需要利用Java提供的反射语法自己去编写代码。 搞清楚了反射的原理BeansFactory的代码就不难看懂了。具体代码实现如下所示 public class BeansFactory {private ConcurrentHashMap singletonObjects new ConcurrentHashMap();private ConcurrentHashMap beanDefinitions new ConcurrentHashMap();public void addBeanDefinitions(List beanDefinitionList) {for (BeanDefinition beanDefinition : beanDefinitionList) {this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition);}for (BeanDefinition beanDefinition : beanDefinitionList) {if (beanDefinition.isLazyInit() false beanDefinition.isSingleton()) {createBean(beanDefinition);}}}public Object getBean(String beanId) {BeanDefinition beanDefinition beanDefinitions.get(beanId);if (beanDefinition null) {throw new NoSuchBeanDefinitionException(Bean is not defined: beanId);}return createBean(beanDefinition);}VisibleForTestingprotected Object createBean(BeanDefinition beanDefinition) {if (beanDefinition.isSingleton() singletonObjects.contains(beanDefinition.getId())) {return singletonObjects.get(beanDefinition.getId());}Object bean null;try {Class beanClass Class.forName(beanDefinition.getClassName());List args beanDefinition.getConstructorArgs();if (args.isEmpty()) {bean beanClass.newInstance();} else {Class[] argClasses new Class[args.size()];Object[] argObjects new Object[args.size()];for (int i 0; i args.size(); i) {BeanDefinition.ConstructorArg arg args.get(i);if (!arg.getIsRef()) {argClasses[i] arg.getType();argObjects[i] arg.getArg();} else {BeanDefinition refBeanDefinition beanDefinitions.get(arg.getArg());if (refBeanDefinition null) {throw new NoSuchBeanDefinitionException(Bean is not defined: arg.getArg());}argClasses[i] Class.forName(refBeanDefinition.getClassName());argObjects[i] createBean(refBeanDefinition);}}bean beanClass.getConstructor(argClasses).newInstance(argObjects);}} catch (ClassNotFoundException | IllegalAccessException| InstantiationException | NoSuchMethodException | InvocationTargetException e) {throw new BeanCreationFailureException(, e);}if (bean ! null beanDefinition.isSingleton()) {singletonObjects.putIfAbsent(beanDefinition.getId(), bean);return singletonObjects.get(beanDefinition.getId());}return bean;} } 建造者模式 建造者模式可以简单概括为 为了防止一个对象中属性过多构造函数冗长如果用set赋值无法把控有些参数必填的逻辑如果必填参数放到构造函数中同样会有第一点问题如果几个属性之间有依赖关系或约束条件校验逻辑会变得无处安放如果希望对象是不可变对象也就是创建好之后就不能在修改内部的属性那么就不能暴露set方法在有些场景还能避免对象存在无效状态比如定义一个长方形类在创建类对象和set第一个值时这个对象都是不可用的只有在第二个值也set后才是可以用的状态。 为了解决这些问题建造者模式就派上用场了代码示例 public class ResourcePoolConfig {private String name;private int maxTotal;private int maxIdle;private int minIdle;private ResourcePoolConfig(Builder builder) {this.name builder.name;this.maxTotal builder.maxTotal;this.maxIdle builder.maxIdle;this.minIdle builder.minIdle;}//…省略getter方法…//我们将Builder类设计成了ResourcePoolConfig的内部类。//我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。public static class Builder {private static final int DEFAULT_MAX_TOTAL 8;private static final int DEFAULT_MAX_IDLE 8;private static final int DEFAULT_MIN_IDLE 0;private String name;private int maxTotal DEFAULT_MAX_TOTAL;private int maxIdle DEFAULT_MAX_IDLE;private int minIdle DEFAULT_MIN_IDLE;public ResourcePoolConfig build() {// 校验逻辑放到这里来做包括必填项校验、依赖关系校验、约束条件校验等if (StringUtils.isBlank(name)) {throw new IllegalArgumentException(…);}if (maxIdle maxTotal) {throw new IllegalArgumentException(…);}if (minIdle maxTotal || minIdle maxIdle) {throw new IllegalArgumentException(…);}return new ResourcePoolConfig(this);}public Builder setName(String name) {if (StringUtils.isBlank(name)) {throw new IllegalArgumentException(…);}this.name name;return this;}public Builder setMaxTotal(int maxTotal) {if (maxTotal 0) {throw new IllegalArgumentException(…);}this.maxTotal maxTotal;return this;}public Builder setMaxIdle(int maxIdle) {if (maxIdle 0) {throw new IllegalArgumentException(…);}this.maxIdle maxIdle;return this;}public Builder setMinIdle(int minIdle) {if (minIdle 0) {throw new IllegalArgumentException(…);}this.minIdle minIdle;return this;}} }// 这段代码会抛出IllegalArgumentException因为minIdlemaxIdle ResourcePoolConfig config new ResourcePoolConfig.Builder().setName(dbconnectionpool).setMaxTotal(16).setMaxIdle(10).setMinIdle(12).build(); 与工厂模式的区别 建造者模式是让建造者类来负责对象的创建工作工厂模式是让工厂类来负责对象的创建工作他们之间的区别是工厂模式是创建不同但是相关类型的对象继承同一父类或者接口的一组子类由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象通过设置不同的可选参数来具体创建不同的对象。
相关文章
-
哈尔滨建设工程招聘信息网站wordpress更改ico
哈尔滨建设工程招聘信息网站wordpress更改ico
- 技术栈
- 2026年04月20日
-
哈尔滨公司网站长沙市建设网站
哈尔滨公司网站长沙市建设网站
- 技术栈
- 2026年04月20日
-
哈尔滨公司网站建设多少钱网站开发项目需求方案
哈尔滨公司网站建设多少钱网站开发项目需求方案
- 技术栈
- 2026年04月20日
-
哈尔滨建站模板用网站模板给人做网站挣钱吗
哈尔滨建站模板用网站模板给人做网站挣钱吗
- 技术栈
- 2026年04月20日
-
哈尔滨教育学会网站建设南阳logo设计公司
哈尔滨教育学会网站建设南阳logo设计公司
- 技术栈
- 2026年04月20日
-
哈尔滨教育学会网站建设长春精神文明建设网站
哈尔滨教育学会网站建设长春精神文明建设网站
- 技术栈
- 2026年04月20日
