Spring系列(一) 工厂模式

前言

这是Spring系列的第一篇,我们来讲解Spring是如何创建对象的。

但是要想理解Spring是如何创建对象的,我们就绕不开工厂这一设计模式。

所以这篇文章将从直接new对象,一个简单工厂,通用工厂类,Spring中的工厂这几个方面来讲解工厂模式。

正文

直接创建对象

如果不考虑设计模式的话,我们要创建一个对象,第一反应是什么呢?

我想大部分人都会说:new 一个对象

是的,在Java基础阶段,我们写任何代码都离不开 new 一个对象

java 复制代码
// 首先创建一个接口
public interface UserService {

    void insert();

    void update();
}

// 然后编写实现类
public class UserServiceImpl implements UserService {
    UserDao userDao = new UserDao();
    public void insert() {
        userDao.insert();
    }

    public void update() {
        userDao.update();
    }
}

// 这里dao层我们直接模拟输出一句话
public class UserDao {
    public void insert() {
        System.out.println("新增成功");
    }

    public void update() {
        System.out.println("更新成功");
    }
}

ok,编码完成,那么如何测试代码呢?你是否还在使用main方法呢?

这里偏题一下,讲解如何优雅的创建测试类测试代码的正确性

  • 首先我们引入依赖
pom 复制代码
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.9.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
  • 然后选择你要测试的类,快捷键 ctrl+shift+t创建对应的测试类,勾选要测试的方法

生成的测试类代码(方法中的内容是自己填写的要测试的内容)

测试结果如下

到这里,我们成功的创建对象,并调用了对象中的方法

也许这里就会有人产生疑问,这不是挺好的嘛?我直接new一个对象,调用对应的方法,代码也不会报错,我写起来也很丝滑,有什么问题呢?

那么请大家思考一个问题,如果有一天,我不再使用 UserServiceImpl这个类,那么我们是不是需要重新new一个新的对象,然后重新打包,重新编译呢?这就是硬编码不好的地方。

为了解决这个问题,有大佬设计出了工厂模式。

简单工厂创建对象

首先,我们创建一个工厂类

java 复制代码
public class BeanFactory {

    public static UserService getUserService() {
        return new UserServiceImpl();
    }
}

然后修改测试类中的代码

java 复制代码
class UserServiceImplTest1 {

    @Test
    void insert() {
        UserService userService = BeanFactory.getUserService();
        userService.insert();
    }

    @Test
    void update() {
        UserService userService = BeanFactory.getUserService();
        userService.update();
    }
}

你会发现,我们一样创建了对象

看到这里,也许会有人说,从直接在测试类中写死,变为在工厂里面写死,有什么区别?这个工厂类没有任何意义。

不要急,你就说这里是不是不再硬编码了吧

哈哈,作为一个绝不水文的笔者,怎么可能把文章断在这里,接下来我们解决工厂类中硬编码的问题。

首先来思考一个问题,我们在java基础阶段,除了new 对象,还能如何创建对象呢?

哦,是反射,是传说中的反射!!!

老规矩,上代码

php 复制代码
public static UserService getUserService() {
    UserService userService;
    try {
        Class clazz = Class.forName("com.jt.spring.factorydesign.UserServiceImpl");
        userService = (UserService) clazz.newInstance();
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
    return userService;
}

到这里,我们已经初步解决了硬编码的问题

接下来,我们还要想办法解决全限定类名的问题

思考一下,我们能从什么地方读取配置呢?

创建一个配置文件

在配置文件中以键值对的方式,声明全限定类名

修改工厂类的代码

ini 复制代码
public static Properties env = new Properties();

static {
    try {
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/application.properties");
        env.load(inputStream);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Class clazz = Class.forName(env.getProperty(beanName));

到这里,一个简单的工厂类就完成了,如果需要修改创建对象的类,只需要修改配置文件中的全限定类名

通用工厂类

接下来我们继续优化上文的工厂类,写一个通用方法

代码如下

java 复制代码
public class BeanFactory {


    public static Properties env = new Properties();

    static {
        try {
            InputStream inputStream = BeanFactory.class.getResourceAsStream("/application.properties");
            env.load(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Object getBean(String beanName) {
        Object o;
        try {
            Class clazz = Class.forName(env.getProperty(beanName));
            o = clazz.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return o;
    }
}

测试结果

java 复制代码
@Test
void insert() {
    UserService userService = (UserService) BeanFactory.getBean("userService");
    userService.insert();
}

@Test
void update() {
    UserService userService = (UserService) BeanFactory.getBean("userService");
    userService.update();
}

Spring中的工厂设计模式

今天,我们先不看具体的源码,我们直接大胆的猜测一下,Spring会如何帮我们创建对象呢?

  • 创建配置文件
  • 指明id和全限定类名
  • 工厂类创建对象

这是我们自己的思路,接下来看一下这样能不能让Spring帮我们创建对象

1、创建配置文件

2、指明id和全限定类名

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.jt.spring.factorydesign.UserDao"/>

    <bean id="userDao2" class="com.jt.spring.factorydesign.UserDao"/>

    <bean id="userService" class="com.jt.spring.factorydesign.UserServiceImpl"/>

</beans>

3、工厂类创建对象

java 复制代码
ApplicationContext cxt = new ClassPathXmlApplicationContext("/applicationContext.xml");
// Spring只会创建一个对象
System.out.println(cxt.getBean("userService"));
System.out.println(cxt.getBean(UserService.class));

哦,不要忘了,我们需要导入Spring的依赖哦

xml 复制代码
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>

测试结果

这里我们看到,Spring和我们预想的一样,帮我们创建了对象

还要注意,Spring两次创建的是同一个对象,也就是所谓的单例模式

结尾

好的,工厂模式到这里就结束了,如果有什么疑问,欢迎评论区留言。

相关推荐
java_heartLake1 小时前
设计模式之代理模式
java·设计模式·代理模式
姜西西_2 小时前
[Spring]Spring MVC 请求和响应及用到的注解
java·spring·mvc
dawn1912282 小时前
SpringMVC 入门案例详解
java·spring·html·mvc
极客先躯2 小时前
高级java每日一道面试题-2024年9月16日-框架篇-Spring MVC和Struts的区别是什么?
java·spring·面试·mvc·struts2·框架篇·高级java
尘浮生5 小时前
Java项目实战II基于Java+Spring Boot+MySQL的大型商场应急预案管理系统(源码+数据库+文档)
java·开发语言·数据库·spring boot·spring·maven·intellij-idea
被拯救的威尼斯5 小时前
设计模式-结构型-11-代理模式
设计模式·代理模式
尘浮生6 小时前
Java项目实战II基于Java+Spring Boot+MySQL的校园社团信息管理系统(源码+数据库+文档)
java·开发语言·数据库·spring boot·mysql·spring·maven
尘浮生7 小时前
Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)
java·开发语言·数据库·spring boot·后端·mysql·spring
LB_bei9 小时前
设计模式-行为型模式-命令模式
设计模式·命令模式
拉玛干13 小时前
社团周报系统可行性研究-web后端框架对比-springboot,django,gin
数据库·python·spring·golang