SSM框架学习——工厂模式、Spring核心容器与Bean

news2024/11/24 22:55:05

工厂模式、核心容器与Spring Bean

工厂模式

工厂模式是Java中常用的一种设计模式,这种类型的设计模式属于创建型模式。说白了在代码层面就是取消了new的使用。

工厂模式有三种:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

举个例子,我们去买手机,假设手机品牌有两种,分别是Xphone和Luwei,你显然不用关心手机是怎么生产的,手机零件怎么组装的,这都是工厂干的活。下面我们以这个例子来讲解三种工厂模式。

简单工厂模式

简单工厂模式示意图如下:

简单工厂模式示意图

我们用Java代码来表示,首先需要定义一个接口,来表示手机(Phone),不妨声明一个方法,用来获取品牌名称。

public interface Phone{
    String getBrand();
}

接下来我们该写这两种品牌的手机类,它们都实现Phone这个接口。

Xphone手机类如下

public class Xphone implements Phone{
    @Override
    public String getBrand(){
        return "Xphone";
    }
}

同理Luwei手机类如下

public class Luwei implements Phone{
    @Override
    public String getBrand(){
        return "Luwei";
    }
}

我们需要一个手机工厂PhoneFactory

public class PhoneFactory{
    public static Phone getPhone(String phoneBrand){
        if(phoneBrand.equals("Xphone")){
            return new Xphone();
        }

        if(phoneBrand.equals("Luwei")){
            return new Luwei();
        }
    }
}

作为消费者的你,想要获取手机只需要通过工厂即可

public class Customer{
    public static void main(String[] args){
        //获取手机实例对象
        Phone xphone = PhoneFactory.getPhone("Xphone");
        System.out.println(xphone.getBrand());
    }
}

但是细心观察你可能会发现,我们目前只有两个手机品牌,但是当品牌增多的时候,工厂内部的代码也需要修改,我们不得不面临一个麻烦——内部代码也会增加。这违反了设计模式的一个原则,即对扩展开放,对修改关闭

工厂方法模式

为了解决简单工厂模式的问题,聪明的你可能会想到,干脆将工厂也变为抽象的接口,我们让每个手机厂商去实现它们各自的工厂不久行啦!没错,这正是工厂方法模式

工厂方法模式示意图

我们在上文的基础上将PhoneFactory从类变为接口

public interface PhoneFactory{
    Phone getPhone();
}

然后为Xphone类实现它的工厂XphoneFactory

public class XphoneFactory implements PhoneFactory{
    @Override
    public Phone getPhone(){
        return new Xphone();
    }
}

同理为Luwei手机类也实现它的工厂

public class LuweiFactory implements PhoneFactory{
    @Override
    public Phone getPhone(){
        return new Luwei();
    }
}

而对于消费者我们可以这样调用

public class Customer{
    public static void main(String[] args){
        Phone xphone = (new XphoneFactory).getPhone();
        System.out.println(xphone.getBrand());
    }
}

这样我们就不必去更改Factory的代码就能增加品牌了,但工厂方法模式会带来另外一个问题,当我们增加品牌的时候就需要增加工厂,而且我们如果新增一款产品,那么每个工厂就必须增加相应的方法,这大大降低了代码的可维护性。

抽象工厂模式

抽象工厂模式相对来讲比较复杂,在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

假设我们前面提到的两个品牌都新增了一样产品——个人电脑。

我们来定义手机接口和电脑接口。

public interface Phone{
    String getBrand();
}
public interface Pc{
    String getBrand();
}

然后两家供应商都各自实现这两个产品

对于Luwei品牌

public class LuweiPhone implements Phone{
    @Override
    public String getBrand(){
        return "Phone_Luwei";
    }
}
public class LuweiPc implements Pc{
    @Override
    public String getBrand(){
        return "PC_Luwei";
    }
}

对于Xphone品牌

public class Xphone implements Phone{
    @Override
    public String getBrand(){
        return "Phone_Xphone";
    }
}
public class XphonePc implements Pc{
    @Override
    public String getBrand(){
        return "PC_Xphone";
    }
}

接下来就是工厂了,这两个品牌必须提供工厂来实现这两个产品。

我们定义抽象的工厂,即工厂接口IFactory

public interface IFactory{
    Phone getPhone();
    Pc getPc();
}

各自实现这个接口

public class LuweiFactory implements IFactory{
    @Override
    public Phone getPhone(){
        return new LuweiPhone();
    }

    @Override
    public Pc getPc(){
        return new LuweiPc();
    }
}
public class XphoneFactory implements IFactory{
    @Override
    public Phone getPhone(){
        return new Xphone();
    }

    @Override
    public Pc getPc(){
        return new XphonePc();
    }
}

我们为了统一它们的工厂,还需要一个超级工厂,即工厂的工厂,者有些类似于供应商与代理商之间的关系,供应商有自己的品牌和工厂生产自己的产品,然后交给代理商售卖给客户,同时代理商可以代理多家供应商的产品。

为了方便,我们直接在工厂接口里定义一个静态方法来完成这个“代理商”

public interface IFactory{
    public static IFactory createFactory(String factoryName){
        if(factoryName.equals("Xphone")){
            return new XphoneFactory();
        }

        if(factoryName.equals("Luwei")){
            return new LuweiFactory();
        }
    }

    Phone getPhone();
    Pc getPc();
}

这样我们就完成了多个品牌多个产品统一对客户的供应。

当然这个模型还可以更复杂一些,比如每个品牌每个商品都有自己的工厂,然后这些品牌创建自己商品工厂的工厂来对外,与客户之间还有一个超级工厂来创建大工厂,不过我们仅是演示原理没必要这么麻烦。

Spring核心容器

Spring容器会负责控制程序之间的关系,而不是由程序代码直接控制。

我们在上一节中,我们提到了IoC,而Spring的Ioc依赖于Spring的核心容器。

框架为我们提供了两种核心容器——BeanFactoryApplicationContext

前者位于spring-beans模块中,后者位于spring-context模块。(我们曾在pom.xml里引入过后者)

BeanFactoryBean都是懒加载的方式,也就是说只有你去调用的时候才被实例化,而ApplicationContext容器启动时Bean就被实例化了。如果不是性能要求特别苛刻或者有其它限制,一般会用后者。

BeanFactory与FactoryBean

看到BeanFactoryFactoryBean,你是不是就明白我们刚才为什么要花这么大的篇幅来讲解工厂模式了。

BeanFactory的重要程度我们用框架源代码中的一句话来讲——IoC的根接口!!!

但是我们只是个简单版本的教程,BeanFactory创建Bean的流程以及生命周期非常复杂,我们篇幅有限,而且并非是框架的文档,所以这里仅会简单描述一下它。

对于BeanFactory的主要作用就是:处理的BeanDefinition经过BeanFactory来生成Bean

BeanFactory作用

BeanFactory是一个大工厂,IoC的根基,可以生产各种Bean;而FactoryBean是一个小工厂,自己也是一个Bean,可以生产其它Bean

看不懂没关系,我们来了解下BeanFactory怎么用。

还记得我们前两章创建的top.cairbin.test1项目吗,现在打开它的App.java文件,就是主类所在的文件,我们目前内容如下:

package top.cairbin.test1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class App 
{
    public static void main( String[] args )
    {
    	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("AppCtx.xml");
        User user = (User)applicationContext.getBean("user");
        user.testSay();
    }
}

可以看到我们是用的ApplicationContext来加载的AppCtx.xml文件,现在我们可以使用BeanFactory来加载它(不过我们一般不这么做),不要忘记导入相应的包。

BeanFactory beanFactory = new BeanFactory(new FileSystemResource("这里写AppCtx.xml的绝对路径"));

ApplicationContext

BeanFactory相对ApplicationContext比较低层,后者是前者的一个子接口,在开发中我们经常用后者。

获取ApplicationContext的方法这里举两个例子,分别是ClassPathXmlApplicationContext创建和FileSystemXmlApplicationContext创建。

前者形式如下

ApplicationContext app = new ClassPathXmlApplicationContext(String configLocation);

也就是说ClassPathXmlApplicationContext是从类路径来找XML的配置文件。

如果你想要使用绝对路径来寻找可以使用FileSystemXmlApplicationContext

ApplicationContext app = new FileSystemXmlApplicationContext(String configLocation);

什么,你问我啥是相对路径绝对路径?前者是相对于你当前位置的;后者是目标文件在文件系统下真实路径,比如Windows中以盘符开始(例如C:\\),在Mac/Linux下以/开始。

Spring Bean

Bean是被实例化的、组装的以及被Spring容器接管的Java对象。听起来很费解,但是没关系我们往下看。

我们在编写AppCtx.xml文件的时候就注意到,根元素为<beans></beans>,其中又包含了若干个子元素<bean>,这些子元素通常携带一些属性来帮助Spring实现依赖注入。

Bean的实例化

Bean的实例化方法大致有三种:

  • 构造器实例化(最常用)
  • 静态工厂实例化
  • 实例工厂实例化
构造器实例化

构造器实例化需要类中拥有一个默认构造方法来实例化Bean,实际上你如果不写构造方法也会有一个默认的构造方法。

你只要创建符合条件的类,然后在AppCtx.xml文件里配置下就行了。我们之前章节就是使用构造器实例化的,所以这里不给出代码。

静态工厂实例化

我们要自己创建一个工厂,并实现一个用于实例化的静态方法,假设我们的Bean对应类名为MyBean

public class MyBeanStaticFactory{
    public static MyBean createBean(){
        return new MyBean();
    }
}

我们需要在AppCtx.xml<beans></beans>里添加

<bean id="myBean" class="top.cairbin.test1.MyBean" factory-method="createBean"/>

这个标签的factory-method属性指定了工厂实例化Bean的静态方法。

实例工厂实例化

实例工厂实例化Bean使用的是非静态方法,即没有static关键字修饰的方法。

public class MyBeanFactory{
    public MyBean createBean(){
        return new MyBean();
    }
}

只不过这种情况下,配置文件的标签要配置两个属性,除了指定工厂的方法外还要通过factory-bean指定实例化后的工厂。

<bean id="myBean" class="top.cairbin.test1.MyBean" factory-bean="myBeanFactory" factory-method="createBean"/>

Bean的属性

我们来介绍下之前涉及的几个属性:

  • id:Bean的唯一标识符。
  • name:该属性可以为Bean指定多个名称,用英文逗号,隔开。
  • class:指定Bean对应的类,它必须是一个完整的类名,从包名一直到类。
  • scope:用来设置Bean的作用域,至于作用域有哪些我们之后讲。

Bean的作用域与生命周期

Bean的作用域用下图来描述

Bean的作用域

至于Bean的生命周期,可讲解的点太多了,这里仅放一张部分的示意图

Bean声明周期

Bean的装配方式

Bean的装配方式对应我们前面的Spring实现DI的几种方式。Spring Bean的方式有以下几种:

  • 基于XML装配
  • 基于注解装配
  • 自动装配

看完这里你就应该明白IoC、DI、工厂、Bean以及Bean的装配它们之间的关系了。

基于XML装配

基于XML装配主要常用于两种注入方式——Setter注入构造器注入

Setter注入要求有Setter和一个无参的构造方法,在XML配置文件中用<property>来为每个属性注入值。

构造器注入必须提供有参数的构造方法,在XML配置文件中使用<constructor-arg>来为参数注入值。

基于注解的装配

用于装配的注解主要有以下几种

自动装配

自动装配要求属性有对应的Setter方法,在XML文件里进行属性设置,实际上是在<bean>元素标签上添加属性autowire,其值可以如下。

@Autowired注解与@Resource注解

@Resource的作用相当于@Autowired,只不过@AutowiredbyType自动注入,而@Resource默认按 byName自动注入。

Spring将@Resourcename属性解析为bean的名字,而type属性则解析为bean的类型。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

另外需要注意@Resource这个注解不属于Spring,它是属于J2EE的。

自动扫描与装配的关系

自动扫描主要用到了后面两种装配方式,按照基于注解的装配方式将相应的注解转换成Bean纳入Spring容器后,会继续扫描@Resource@Autowired注解,然后按自动装配的方式进行关系建立。

然而后两种装配方式本身就是对XML的一种自动化处理,可能借助了Java的某些机制,例如反射,但实际上这几种装配方式在根本上还是一样的,不要将它们割裂来看。

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

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

相关文章

Ubuntu20.04安装MatlabR2018a

一、安装包 安装包下载链接 提取码&#xff1a;kve2 网上相关教程很多&#xff0c;此处仅作为安装软件记录&#xff0c;方便后续软件重装&#xff0c;大家按需取用。 二、安装 1. 相关文件一览 下载并解压文件后&#xff0c;如下图所示&#xff1a; 2. 挂载镜像并安装 2…

C++11---右值引用(深度讲解)

简要介绍 右值引用是C11的新特性,无论左值引用还是右值引用&#xff0c;都是在给对象取别名 什么是左值 什么是右值 1.左值,左值引用 左值是一个数据的表达式(例如变量或者解引用后的指针),我们可以对其进行取地址和修改赋值,左值可以出现在赋值符号的左边,而右值不能出现在…

深度解析C语言——预处理详解

对C语言有一定了解的同学&#xff0c;相信对预处理一定不会陌生。今天我们就来聊一聊一些预处理的相关知识。预处理是在编译之前对源文件进行简单加工的过程&#xff0c;主要是处理以#开头的命令&#xff0c;例如#include <stdio.h>、#define等。预处理是C语言的一个重要…

leetcode721. 合并账户【两种方法;并查集;dfs】

文章目录 并查集&#xff08;方法一&#xff09;dfs&#xff08;方法二&#xff09;dfs换一种写法 并查集&#xff08;方法一&#xff09; class Solution {unordered_map<string, int> index; // 每个邮箱都有一个唯一编号int root[10010]; // 并查集…

C++算法——滑动窗口

一、长度最小的子数组 1.链接 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 2.描述 3.思路 本题从暴力求解的方式去切入&#xff0c;逐步优化成“滑动窗口”&#xff0c;首先&#xff0c;暴力枚举出各种组合的话&#xff0c;我们先让一个指针指向第一个&…

手机销量分析案例

项目背景 某电商商城随着业务量的发展&#xff0c;积累了大量的用户手机销售订单数据。决策层希望能够通过对这些数据的分析了解更多的用户信息及用户的分布&#xff0c;从而可以指导下一年的市场营销方案以及更加精准的定位市场&#xff0c;进行广告投放。 数据说明 数据时…

JAVA基础02-Java语言基础以及编译准备工作

什么是JAVA语言 Java是一门面向对象的编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用的两个特征。 &#xff08;可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式…

洛谷P1000超级玛丽游戏题解[Python, Rust, Go]

题目 打印超级玛丽字符图像 小技巧 直接复制题目的超级玛丽符号首行会有空格问题&#xff0c;一直AC不过&#xff0c;一行一行地复制就OK了&#x1f44c;。 Rust 题解 fn main() {println!(" ********************####....#.#..###.....##....###...…

什么是 Nginx?(一)

前段时间在网上看到一个有意思的话题&#xff1a;只知道 Nginx 牛逼&#xff0c;却不知道它怎么支持百万并发&#xff1f; 无论是运维、开发、测试&#xff0c;Nginx 技术栈的学习总是必不可少的&#xff0c;只是不同的岗位掌握的深度与广度不同而已。 Nginx 是开源、高性能、…

MATLAB科研绘图与学术图表绘制从入门到精通

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

算法学习——LeetCode力扣图论篇1(797. 所有可能的路径、200. 岛屿数量、695. 岛屿的最大面积)

算法学习——LeetCode力扣图论篇1 797. 所有可能的路径 797. 所有可能的路径 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特…

golang语言系列:Web框架+路由 之 Gin

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是golang语言学习系列&#xff0c;本篇对Gin框架的基本使用方法进行学习 1.Gin框架是什么 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快&#xff0c;如果你是性能和高效的追求者…

java项目基于Springboot和Vue的高校心理教育辅导系统的设计与实现

今天要和大家聊的是基于Springboot和Vue的高校心理教育辅导系统的设计与实现 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&…

C++完美转发(适合小白)

我们知道&#xff0c;C中有左值引用和右值引用&#xff0c;首先我们要知道什么是左值什么是右值。 左值&#xff1a;表达式结束后依然存在的持久对象。左值可以出现在赋值语句的左边或右边。例如&#xff0c;变量和函数返回的引用都是左值。左值通常有持久的地址&#xff0c;可…

Python深度学习034:cuda的环境如何配置

文章目录 1.安装nvidia cuda驱动CMD中看一下cuda版本:下载并安装cuda驱动2.创建虚拟环境并安装pytorch的torch_cuda3.测试附录1.安装nvidia cuda驱动 CMD中看一下cuda版本: 注意: 红框的cuda版本,是你的显卡能装的最高的cuda版本,所以可以选择低于它的版本。比如我的是11…

Go项目结构整洁实现|GitHub 3.5k

一、前言 hi&#xff0c;大家好&#xff0c;这里是白泽。今天给大家分享一个GitHub &#x1f31f; 3.5k 的 Go项目&#xff1a;go-backend-clean-arch https://github.com/amitshekhariitbhu/go-backend-clean-architecture 这个项目是一位老外写的&#xff0c;通过一个 HTT…

HTML基本元素

文章目录 如何制作标题如何制作文字如何做粗体字检查我们程序码给输出文字添加属性 HTML 一个HTML标签包含着&#xff1a; 起始标签&#xff1a;它包含了元素的名字&#xff0c;夹在一对 <、>&#xff08;尖括号&#xff09;之间。它指明元素从何处开始生效。结束标签&am…

《Git版本控制管理》笔记

第三章 git --version查看版本号git --help查看帮助文档裸双破折号分离参数 git diff -w master origin – tools/Makefile将当前目录或任何目录转化为Git版本库 git init 初始化之后项目目录中&#xff0c;有名为.git的文件git status 查看git状态git commit 提供日志消息和作…

整型之韵,数之舞:大小端与浮点数的内存之旅

✨✨欢迎&#x1f44d;&#x1f44d;点赞☕️☕️收藏✍✍评论 个人主页&#xff1a;秋邱’博客 所属栏目&#xff1a;人工智能 &#xff08;感谢您的光临&#xff0c;您的光临蓬荜生辉&#xff09; 1.0 整形提升 我们先来看看代码。 int main() {char a 3;char b 127;char …

Java基础核心Map

在Java中&#xff0c;Map是一种用于存储键值对&#xff08;key-value pairs&#xff09;的集合类型。它提供了一种将键映射到值的方式&#xff0c;其中每个键在Map中都是唯一的。Map接口是java.util包中的一部分。 常用实现类&#xff1a; HashMap: 基于哈希表实现的Map&#…