前言
这是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两次创建的是同一个对象,也就是所谓的单例模式
结尾
好的,工厂模式到这里就结束了,如果有什么疑问,欢迎评论区留言。