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两次创建的是同一个对象,也就是所谓的单例模式

结尾

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

相关推荐
hanbarger3 小时前
mybatis框架——缓存,分页
java·spring·mybatis
捕鲸叉3 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~4 小时前
框架专题:设计模式
设计模式·框架
先睡4 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
龙少95435 小时前
【深入理解@EnableCaching】
java·后端·spring
啦啦右一11 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
Damon_X12 小时前
桥接模式(Bridge Pattern)
设计模式·桥接模式
荆州克莱14 小时前
mysql中局部变量_MySQL中变量的总结
spring boot·spring·spring cloud·css3·技术
zquwei15 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
火烧屁屁啦15 小时前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee