前言
本章主要对Spring工厂创建对象,以及Spring框架中的注入做一个#简单的说明。
1. Spring工厂创建对象原理讨论
1. 通过Spring工厂创建对象,重要有如下几步:
# 第一步:创建一个Account实体类
# 第二步:到applicationContext.xml中配置Account
# 第三步:测试-->创建工厂,读取applicationContext.xml配
置文件,并通过工厂对象中的方法getBean()获取Accout这个id属
性所对应的这个对象。
2. 对Spring工厂创建对象原理简单说明
⑴ 首先我们通过"读取Spring的配置文件applicationContext.xml"
来创建Spring的工厂---->其次通过创建的工厂调用'getBean()'获取配
置文件中定义的<bean id="xxx">中的id值获取对象--->注意:它这边
的通过Spring工厂获取的对象实际底层是通过'反射'来创建对象,反射创
建Account对象(Account account = new Account()--->反射在创建
对象的同时也是会调用对象的构造方法的就是上面说的Account account
= new Account();--->所以说Spring工厂它创建对象的底层实际上是通
过反射来调用对象的构造方法,创建对象的。--->所以类对象必须存在构
造方法,才可以实现对象的创建。
------------------------------------------------------
3. 对于实体类是否需要Spring工厂来进行创建呢?
答案就是:所有实体对象是不会交给Spring创建的,他是由持久层框架来
创建的。因为它需要数据,而数据来源于数据库,而Spring是不知道数据
库的。
2. Spring的注入
2.1 什么是注入
注入:通过Spring工厂及配置文件,为所创建对象的成员变量赋值。
2.2 Spring实现注入有两种方式
- Spring实现注入有两种方式:Set注入 + 构造注入。
2.3 方式一:Set注入
2.3.1 Set注入实战
1. 要使用"注入"为成员变量赋值的话,需要做的准备为↓:
⑴ 第一步:在类层面:在类Person中为成员变量提供Get/Set方法。
⑵ 第二步:配置Spring的配置文件(applicationContext.xml)
<bean id="person" class="com.spring5.demo01.entity.Person">
<!--
<property>标签中的name的值应与实体配Person中的属性值对应
-->
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>xiaoming</value>
</property>
</bean>
⑶ 第三步:测试
/**
* 通过Spring配置文件进行赋值
*/
@Test
public void test8() {
//1.指定配置文件,创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//利用bean中name属性指定的别名来获取bean
Person person = (Person)ctx.getBean("person");
System.out.println(person);
}
-----------------------------------------------
2. 注入的好处
⑴ 解耦合:日后只要是对Person对象的值不满意了,我无需去对代码
层面进行修改,只需要直接在applicationContext.xml中去修改
即可。因为我们改的是配置文件,因此它不涉及重新编译以及重新
部署的过程,他就直接将Person中的值修改了。
2.3.2 对Spring注入原理的简单说明
1. 对Spring注入原理的简单说明(底层需要Set()方法):
--->实现Spring的注入过程中,我们主要是在Spring的配置
文件中利用<Property>标签对对象的'成员变量'进行赋值,但是
实际上配置文件applicationContext.xml中的<Property>标签
底层实际上等效于是调用了对象的'Set()'方法来实现'对象成员
变量的赋值的。因此我们要实现Spring注入操作,必须给对象加上
Set()方法才可以实现在Spring配置文件利用<Property>标签
完成对象'成员变量'的赋值操作。
2.3.3 Spring注入-JDK内置类型
1. JDK内置类型
⑴ 若对象中的成员变量的类型属于:String类型 + 8种基本数据类型
--->对于以上的类型在<property>里面使用的是"<value>标签"来实现赋值。
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>xiaoming</value>
</property>
⑵ 若对象中的成员变量的类型属于:数组类型
--->对于以上的类型在<property>里面使用的是<list>中嵌套<value>
<property name="emails">
<list>
<value>aaa@123.com.cn</value>
<value>bbb@123.com.cn</value>
<value>ccc@123.com.cn</value>
</list>
</property>
⑶ 若对象中的成员变量的类型属于:List集合类型
- 注意:我们这里用的<list>标签与字段类型是"数组"用到的<list>是
同一个标签,因为List集合的底层也是用数组来完成的。
--->List集合在配置文件的用法,如下所示↓:
<property name="addresses">
<list>
<value>aaa111</value>
<value>bbb222</value>
<value>ccc333</value>
</list>
</property>
------------------------------------------------
2. 以上涉及的实体类对象和配置文件(applicationContext.xml)以及测试类汇总,如下所示↓:
⑴ 实体类Person
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private Integer id;
private String name;
private String[] emails;
private List<String> addresses;
}
⑵ Spring配置文件(applicationContext.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">
<!--
id:属性名字(唯- )
class:属性配置全限定名(包名.类名)
-->
<bean id="person" class="com.spring5.demo01.entity.Person">
<!--
<property>标签中的name的值应与实体配Person中的属性值对应
-->
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>xiaoming</value>
</property>
<property name="emails">
<list>
<value>aaa@123.com.cn</value>
<value>bbb@123.com.cn</value>
<value>ccc@123.com.cn</value>
</list>
</property>
<property name="addresses">
<list>
<value>aaa111</value>
<value>bbb222</value>
<value>ccc333</value>
</list>
</property>
</bean>
</beans>
⑶ 测试类
/**
* 用于测试:JDK类型成员变量的赋值
*/
@Test
public void test9() {
//1.指定配置文件,创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//利用bean中name属性指定的别名来获取bean
Person person = (Person)ctx.getBean("person");
String[] emails = person.getEmails();
for (String email : emails) {
System.out.println("eamil = " + email);
}
System.out.println("-----------------------");
List<String> addresses = person.getAddresses();
for (String a : addresses) {
System.out.println("addresses = " + a);
}
}
2.3.4 Spring注入-用户自定义类型
1. 由于UserDAO是我们自定义的一个类型,因此下面演示如何
使用Set实现自定义类型的注入的两种方式。
➀ 第一种方式:
⑴ 第一步:声明对象下自定义的成员变量"UserDAO"。通过Spring的方式完成
Set的注入。
/**
* 通过Spring的Set方式完成自定义类型的注入
*/
private UserDAO userDAO;
⑵ 第二步:声明完自定义的成员变量"UserDAO",提供UserDAO的
Get和Set方法。
▲ 注意:我们给"自定义的成员变量"UserDAO",提供UserDAO的Get
和Set方法"的目的是因为Spring底层在帮我们利用Set实现注入的
过程中需要用到Get和Set方法。因此我们需要提供"成员变
量"UserDAO"的Get和Set方法"。
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
⑶ 第三步:通过Spring的配置文件(applicationContext.xml)为
我们自定义的类型"UserDAO"进行赋值。
<bean id="userService" class="com.spring5.demo01.service.UserServiceImpl">
<property name="userDAO">
<!-- 表示:将UserDAOImpl对象赋值给上面property标签中的userDAO成员变量的方式 -->
<!--下面没有写id,是因为这个bean只在这里使用一次,且没有人去引用这个bean,因此id不写也可以-->
<bean class="com.spring5.demo01.dao.UserDAOImpl" />
</property>
</bean>
⑷ 第四步:测试:
/**
* 用于测试:用户自定义类型成员变量的赋值
*/
@Test
public void test10() {
//1.指定配置文件,创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//利用bean中name属性指定的别名来获取bean
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User(1,"hhh","hhh123456"));
userService.login("admin","123123");
}
⑸ 具体的UserServiceImpl类的代码,如下所示↓:
public class UserServiceImpl implements UserService {
// private UserDAO userDAO = (UserDAO) BeanFactory.getBean("userDAO");
/**
* 通过Spring的Set方式完成自定义类型的注入
*/
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
//注册
public void register(User user) {
userDAO.save(user);
}
//登录
public void login(String name, String password) {
userDAO.queryUserByNameAndPassword(name,password);
}
}
-----------------------------------------------------------
➁ 第二种方式
--->为什么要增加自定义类型注入的第二种方式呢?是因为
第一种方式实现注入存在问题
⑴ 配置文件代码冗余
-->配置文件中相同代码被配置多次。
⑵ 被注入的对象(UserDAO),多次创建,浪费(JVM)内存资源
-->相同的类配置多次,以此造成JVM虚拟机的内存资源的浪费问题。
⑶ 以上两个问题,如图所示-"图-方式一问题"
⑷ 第二种方式代码演示,如下所示↓:
- 步骤一:为成员变量提供Get和Set方法
/**
* 通过Spring的Set方式完成自定义类型的注入
*/
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
- 步骤二:配置文件中进行配置
<bean id="userDAO" class="com.spring5.demo01.dao.UserDAOImpl" ></bean>
<bean id="userService" class="com.spring5.demo01.service.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
- 步驟三:測試
/**
* 用于测试:用户自定义类型成员变量的赋值
*/
@Test
public void test10() {
//1.指定配置文件,创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//利用bean中name属性指定的别名来获取bean
UserService userService = (UserService) ctx.getBean("userService");
userService.register(new User(1,"hhh","hhh123456"));
userService.login("admin","123123");
}
2.4 方式二:构造注入
1. Spring构造注入的开发步骤分为两步,如下所示↓:
⑴ 第一步:提供有参构造方法
# 创建Customer对象,提供有参构造
public class Customer implements Serializable {
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
⑵ 第二步:Spring配置文件的配置
# 利用配置文件applicationContext.xml对属性赋值,实现注入
<bean id="customer" class="com.spring5.demo02.constructer.Customer">
<!--
一个<constructor-arg>标签对应构造方法当中的一个参数。
意思就是说:若构造方法有两个入参,就需要在这边定义两个"<constructor-arg>标签"
-->
<constructor-arg>
<!-- <constructor-arg>便签里面写上具体的赋值 -->
<!--给name赋值-->
<value>aaa</value>
</constructor-arg>
<constructor-arg>
<!--给age赋值-->
<value>102</value>
</constructor-arg>
</bean>
⑶ 第三步:测试给Customer对象注入的值是否成功
/**
* 用于测试:构造注入
*/
@Test
public void test11() {
//1.指定配置文件,创建Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//2.获取bean定义的对象
Customer customer = (Customer) ctx.getBean("customer");
System.out.println(customer);
}
--->测试结果,如图所示-"图-构造注入结果"。
图-构造注入结果
3. 注入总结
1. Spring的注入方式有两种:Set注入 + 构造注入
--->虽然这两种注入的方式有所不同,但是它们最终达到的效果都
是一样的,都是通过Spring的配置文件给对象的'成员变量'赋值。
----------------------------------------------
2. 未来实战当中,使用'Set注入'还是'构造注入'中的哪一种呢?
答案:使用'Set注入'会更多,原因有如下几点↓:
⑴ 原因一:构造注入很麻烦,因为它有重载
⑵ 原因二:Spring框架的底层也是大量的应用了'set注入'