学习内容:学习Spring框架(Day48)
1、Spring框架
2、Bean管理
3、IOC和DI
1、Spring框架
(1)Spring框架可以简化用户编码过程。Spring相当于一个容器,可以帮助管理项目中各种类,需要使用的时候不用自己去定义,可以调用Spring中的方法去它所管理的组件中进行查找,然后直接给你所需要的组件。Spring构成如下:
(2)需要的依赖,这几个依赖的版本要对应:
spring-core.jar
spring-beans.jar
spring-context.jar
2、Bean管理
(1)Bean代表项目中的Java类,要管理这些Bean,先要创建applicationContext.xml。在Maven项目的main目录下的resource目录下创建applicationContext.xml。
<?xml version="1.0" encoding="UTF-8"?>
<!--添加schema验证-->
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
给applicationContext.xml文件添加<bean>节点,将类交给Spring管理
<!--通过id获取bean,一个类只能有一个id,class是类的位置-->
<bean id="UserDao" class="com.hisoft.dao.impl.UserDaoImp"/>
<!--通过name获取bean,一个类可以有多个name-->
<bean name="User,UserDaoImp" class="com.hisoft.dao.impl.UserDaoImp"/>
<!-- 不加id和name可以写多个相同的bean -->
<bean class="com.hisoft.dao.impl.UserDaoImp"/>
<bean class="com.hisoft.dao.impl.UserDaoImp"/>
(2)在测试类中获取bean
public class SpringTest {
public static void main(String[] args) {
//构建Spring实例,此时创建了所有bean的实例,执行所有bean实例的构造方法
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean的实例,根据id获取
UserDaoImp userDao = (UserDaoImp) context.getBean("UserDao");
//获取bean的实例,根据name获取
UserDaoImp userDao2 = (UserDaoImp) context.getBean("UserDaoImp");
//获取bean的实例,根据完全限定名获取,可以在完全限定名后面加 #0 来获取第1个UserDaoImp实例
UserDaoImp userDao3 = (UserDaoImp) context.getBean("com.hisoft.dao.impl.UserDaoImp#0");
//调用实例的方法
userDao.save();
}
}
(3)初始化和销毁方法
在User类中定义init()方法,并在bean中添加属性init-method=“init”,就可以在创建bean实例的时候执行init()方法。
<bean id="UserDao" class="com.hisoft.dao.impl.UserDaoImp" init-method="init"/>
在User类中定义destroy()方法,并在bean中添加属性destroy-method=“destroy”,就可以在调用close方法时,即容器关闭时执行destroy()方法。
<bean id="UserDao" class="com.hisoft.dao.impl.UserDaoImp" destroy-method="destroy"/>
(4)Bean的延迟加载
如果不想在创建Spring实例的时候创建bean实例,可以在bean中添加lazy-init=“true”(默认值是false)来延迟创建bean实例,只有在获取bean实例的时候才创建bean实例。
<bean class="com.hisoft.dao.impl.UserDaoImp" lazy-init="true"/>
(5)单例和多例
增加scope属性,将bean设置为单例(默认)或者多例的
<!--单例-->
<bean class="com.hisoft.dao.impl.UserDaoImp" scope="singleton"/>
<!--多例-->
<bean class="com.hisoft.dao.impl.UserDaoImp" scope="prototype"/>
3、IOC和DI
(1)IoC(Inversion of Control,控制反转):Spring的核心,对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。在对象A中要使用对象B时,称A依赖于B,B就是A的依赖对象。平常我们会使用new object()这样的方式在对象A中将依赖对象B创建出来,这样两个对象就产生了耦合。但是在Spring中,创建依赖对象B是由Spring完成的,Spring创建好B对象,然后存储到Spring容器里面,当A对象需要使用B对象时,Spring就从容器中取出,然后交给A对象使用。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
DI(Dependency Injection,依赖注入):Spring在系统运行中,动态的向某个对象提供它所需要的其他对象,这就是通过依赖注入实现的。IoC和DI从不同的角度描述了Spring容器创建对象并提供对象的过程。依赖注入有set注入、构造方法注入、自动注入三种。
(2)set注入
UserServiceImpl类中需要用到UserDao对象和一些全局变量,这些对象和变量值可以通过set注入的方式来获取。
public class UserServiceImpl {
//userDao是一个接口对象,面向接口编程:解耦(降低耦合度)
//例如连接数据库有oracleDao和mysqlDao两种对象,使用一个userDao接口对象就可以实现接收不同的数据库对象
private UserDao userDao;
public String name;
public Integer age;
public List<String> stringList;
public Map<String,Object> map;
public Properties properties;
//创建set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
在applicationContext.xml文件中添加<property>节点实现注入,将UserDaoImp对象注入UserServiceImpl对象中。
<bean name="userDao" class="com.hisoft.dao.impl.UserDaoImp"/>
<bean id="userService" class="com.hisoft.service.UserServiceImpl">
<!-- name表示UserServiceImpl类中的set方法的名称,ref表示Spring容器中bean的id或name -->
<property name="userDao" ref="userDao"/>
</bean>
其它配置方式
<bean name="userDao" class="com.hisoft.dao.impl.UserDaoImp"/>
<bean id="userService" class="com.hisoft.service.UserServiceImpl">
<property name="userDao">
<ref bean="userDao"/>
</property>
</bean>
<bean id="userService" class="com.hisoft.service.UserServiceImpl">
<property name="userDao">
<!--此时这个bean的作用域只在property中-->
<bean class="com.hisoft.dao.impl.UserDaoImp"/>
</property>
</bean>
将变量的值注入UserServiceImpl对象中
<bean id="userService" class="com.hisoft.service.UserServiceImpl">
<!--注入String和int型-->
<property name="name" value="Tom"/>
<property name="age" value="12"/>
<!--注入List集合-->
<property name="stringList">
<list>
<value>Jerry</value>
<value>Jack</value>
<value>Jone</value>
</list>
</property>
<!--注入Map集合-->
<property name="map">
<map>
<entry key="j">
<value>J</value>
</entry>
<entry key="k">
<value>K</value>
</entry>
</map>
</property>
<!--注入properties对象-->
<property name="properties">
<props>
<prop key="r">R</prop>
</props>
</property>
</bean>
(3)构造方法注入
BookServiceImpl类中创建一个构造方法,参数是需要用到的BookDao对象和一些变量。
public class BookServiceImpl {
private BookDao bookDao;
public BookServiceImpl(BookDao bookDao, String name, String address){
System.out.println(name);
System.out.println(address);
this.bookDao = bookDao;
}
}
在applicationContext.xml文件中添加<constructor-arg>节点实现构造方法注入
<bean id="bookDao" class="com.hisoft.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.hisoft.service.BookServiceImpl">
<!--name表示BookServiceImpl对象中构造方法的参数名称,value表示要注入的参数值,ref表示Spring容器中bean的id或name-->
<constructor-arg name="address" value="beijing"/>
<constructor-arg name="name" value="Rose"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
其它配置方式
<!--type表示注入的数据类型,根据数据类型匹配,无法保证顺序正确-->
<constructor-arg type="com.hisoft.dao.BookDao" ref="bookDao"/>
<constructor-arg type="java.lang.String" value="Beijing"/>
<constructor-arg type="java.lang.String" value="Jack"/>
<!--index表示注入的第几个参数-->
<constructor-arg index="0" ref = "bookDao"/>
<constructor-arg index="2" value="beijing"/>
<constructor-arg index="1" value="jack" />
<!--只设置注入的value和ref时会根据数据类型自动匹配,无法保证顺序正确-->
<constructor-arg value="beijing"></constructor-arg>
<constructor-arg value="tom"></constructor-arg>
<constructor-arg ref="bookDao"/>
<!--注入的参数是一个集合时-->
<constructor-arg>
<list>
<value>Jack</value>
<value>Rose</value>
</list>
</constructor-arg>
(4)实际开发中经常使用的是set注入方法
使用构造方法注入的理由:
• 构造方法注入使用强依赖规定,如果不给足够的参数,对象则无法创建。
• 由于Bean 的依赖都通过构造方法设置了,那么就不用写更多的 set 方法,有助于减少代码量。
使用 set 注入的理由:
• 如果Bean有很多的依赖,那么构造方法的参数列表会变的很长。
• 如果一个对象有多种构造方法,构造方法会造成代码量增加。
• 如果构造方法中有两个以上的参数类型相同,那么将很难确定参数的用途。
(5)自动注入
1.需要注入的对象中有set方法时
public class NewsServiceImpl {
private NewsDao newsDao;
//创建set方法
public void setNewsDao(NewsDao newsDao) {
this.newsDao = newsDao;
}
}
applicationContext.xml文件,在要注入的对象的bean中添加autowire属性
<bean id = "newsDao" class="com.hisoft.dao.impl.NewsDaoImpl" />
<bean id = "newsDao2" class="com.hisoft.dao.impl.NewsDaoImpl2" />
<!--此时autowire的属性不能是byType,因为有两个相同类型的bean,匹配的类型只有一个时才能使用byType-->
<!--byName查找的是NewsServiceImpl类中的set方法的名字进行匹配-->
<bean id = "newsService" class="com.hisoft.service.NewsServiceImpl" autowire="byName"/>
2.需要注入的对象中有构造方法时
public class NewsServiceImpl {
private NewsDao newsDao;
public NewsServiceImpl(NewsDao newsDao){
this.newsDao = newsDao;
}
}
<bean id = "newsDao" class="com.hisoft.dao.impl.NewsDaoImpl" />
<bean id = "newsDao2" class="com.hisoft.dao.impl.NewsDaoImpl2" />
<!--与byType的方式类似,不同之处在于它应用于构造器参数-->
<bean id = "newsService" class="com.hisoft.service.NewsServiceImpl" autowire="constructor"/>
autowire的值:
• no 默认值,不进行自动注入
• byName 根据需要注入的属性名在容器内寻找名称相同的Bean,如果找到就注入,找不到就不注入
• byType 根据需要注入的属性类型在容器找类型相同的Bean,如果找到就注入,找不到就不注入,如果找到多个 类型相同的Bean,则抛出异常
• constructor 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。