文章目录
IOC的概念与作用
一般来说我们创建对象都是通过new的方式来创建对象的示例的,这可以说是主动创建。
在三层架构中通过这种方式创建对象层与层之间耦合性高,UserDao依赖UserDaoImpl,当实现类改变时,我们就要重新编写代码创建该实现类。
//UserDao依赖UserDaoImpl
UserDao userDao=new UserDaoImpl();
使用IOC(控制反转)可以降低程序的耦合
(解耦),即降低代码中的依赖关系。
IOC的核心思想是使用工厂模式通过读取配置文件,得到全限定类名,通过反射创建对象。再进一步优化,提供一个容器对象,使用静态代码块,把所有创建的对象存放在容器中。
读取配置文件 (beans.properties)
userDao=com.mycode.dao.impl.UserDaoImpl
工厂对象(BeanFactory )只要一读取配置文件,就会创建配置文件配置了的所有对象,并存放在容器中
public class BeanFactory {
//创建一个Properties,存储配置文件信息
private static Properties props = new Properties();
//存储所有需要创建对象实例的容器对象
private static Map<String,Object> beans = new HashMap<String,Object>();
static {
try {
//加载配置文件数据,获得文件数据的IO流对象
InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
//将IO流对象载入到props中
props.load(is);
//获取所有key
for (Object key : props.keySet()) {
//循环key,根据key获取类的全限定名
String clazz = (String) props.get(key);
//创建对象实例
Object instance = Class.forName(clazz).newInstance();
//将对象实例存入到Map
beans.put(key.toString(),instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//根据key获取容器中存放的实例对象
public static <T> T getBean(String key){
return (T) beans.get(key);
}
}
这时我们要创建对象则可以直接通过工厂对象提供的方法来获取相应实例对象,这种被动接收获取对象的思想就是IOC(控制反转),由原来通过new主动创建对象变为通过工厂被动接受对象(把创建对象的控制权给第三方)。引申到Spring,IOC是spring框架两大核心之一,使用Spring框架即把创建对象的控制权给了spring框架,让它帮我们创建对象,给SpringIOC容器管理,我们只要在配置文件中配置要创建的对象即可.(通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合)。
//UserDao userDao=new UserDaoImpl();
UserDao userDao= BeanFactory.getBean("userDao");
使用Spring基于Xml配置的IOC解决程序耦合
引入依赖
<!--Spring包的依赖-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
在类的根路径下创建一个任意名称的xml文件(beans.xml),让spring管理资源,在配置文件中配置service和dao
<?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">
<!--使用spring的IOC解决程序耦合 基于XML的配置-->
<!--
bean标签:用于配置让spring创建对象,并存入SpringIOC容器中,给SpringIOC容器管理
id属性:对象的唯一标识。
class属性:指定要创建对象的全限定类名
-->
<!-- 配置UserServiceImpl -->
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl"></bean>
<!--相当于java代码:
UserService UserService = new com.mycode.dao.impl.UserDaoImpl()
但是本质上并不是直接new 而是 创建的是对象实例的代理对象
-->
<!-- 配置UserDaoImpl -->
<bean id="userDao" class="com.mycode.dao.impl.UserDaoImpl"></bean>
</beans>
测试 通过Spring提供的工厂对象的方法获取相应示例对象
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService);
//com.mycode.service.impl.UserServiceImpl@1877ab81
}
Spring框架中的工厂,BeanFactory和ApplicationContext的区别
BeanFactory是Spring容器中的顶层接口
ApplicationContext是BeanFactory的子接口
BeanFactory和ApplicationContext的区别:
-
BeanFactory
:按需创建,即什么使用什么时候创建对象 -
ApplicationContext
:读取配置文件,就会创建对象
ApplicationContext接口的实现类:
ClassPathXmlApplicationContext
:从类的根路径下加载配置文件FileSystemXmlApplicationContext
: 从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。AnnotationConfigApplicationContext
:当我们使用注解配置容器对象时,使用此类来创建spring容器,用来读取注解。
IOC中Bean的详解
1.Bean标签与Bean的作用范围
<?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标签:用于配置对象让spring来创建对象,并存入ioc容器中.(注意:默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则无法创建。)
id属性:给对象在容器中提供一个唯一标识,用于获取对象。
class属性:指定类的全限定类名。用于反射创建对象.(默认情况下调用无参构造函数)
scope属性:指定对象的作用范围
scope属性值:
singleton:默认值,单例的,一个应用只有一个对象的实例,它的作用范围就是整个引用。
单例对象生命周期:
对象出生:当应用加载,创建容器时,对象就被创建。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁。
prototype:多例的,即每次获取对象,都将创建一个新对象。
多例对象生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被java的垃圾回收器回收。
request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中,作用于一次请求,请求结束就被销毁
session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中,作用于一次请求,会话结束就被销毁
global session:WEB项目中,应用在Portlet环境.如果没有Portlet环境那么globalSession相当于session.
init-method属性:指定类中的初始化方法名称,对象创建之后,调用该方法
destroy-method属性:指定类中销毁方法名称,容器销毁之前调用该方法
-->
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl" scope="singleton" init-method="initMethod" destroy-method="destroyMethod" ></bean>
</beans>
配置了init-method
属性与destroy-method
属性要在对应类中增加相应
public class UserServiceImpl implements UserService {
//初始化时调用
public void initMethod(){
System.out.println("UserServiceImpl已经初始化了");
}
//销毁时调用
public void destroyMethod(){
System.out.println("UserServiceImpl 要被销毁了");
}
}
2. 实例化Bean的三种方式
①构造器实例化bean(使用默认无参构造函数)
<!--默认情况下根据无参构造函数来创建类对象,如果bean中没有默认无参构造函数,则无法创建 -->
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl" ></bean>
②spring管理静态工厂(使用静态工厂的方法创建对象)
- 提供静态工厂及方法(StaticFactory)
public class StaticFactory {
//创建对象的静态方法
public static UserServiceImpl createUserService(){
return new UserServiceImpl();
}
}
- xml配置文件中的配置
<!-- 使用StaticFactory类中的静态方法createAccountService创建对象,并存入spring容器
id属性:给对象在容器中提供一个唯一标识,用于获取对象。
class属性:指定静态工厂的全限定类名
factory-method属性:指定创建对象的静态方法
-->
<bean id="userService"
class="com.mycode.factory.StaticFactory"
factory-method="createUserService"></bean>
③spring管理实例工厂(使用实例工厂的方法创建对象)
- 提供实例工厂及方法(StaticFactory)
public class BeanFactory {
//创建对象的方法
public UserServiceImpl createUserService(){
return new UserServiceImpl();
}
}
- xml配置文件中的配置
<!-- 分析:之前使用静态工厂的方式,静态工厂的静态方法可以直接调用,
而这个实例工厂,其方法不是静态的,则这里必须先创建实例工厂的实例对象,在调用其方法。
-->
<!-- 1.先把实例工厂的创建交给spring来管理
实例工厂的对象如何创建
java代码:
BeanFactory beanFactory= new com.mycode.factory.BeanFactory()
在xml配置文件中相当于
-->
<bean id="instancFactory" class="com.mycode.factory.BeanFactory"></bean>
<!-- 2.然后在使用工厂的bean来调用里面的方法
factory-bean属性:用于指定实例工厂bean的id。
factory-method属性:用于指定实例工厂中创建对象的方法。
-->
<bean id="accountService"
factory-bean="instancFactory"
factory-method="createUserService"></bean>
Spring的依赖注入,使用配置文件的方式
依赖注入(DI),spring框架核心IoC的具体实现,给Bean的属性赋值(依赖被注入到对象中)
1.set方法注入——本质是调用类中的set方法实现注入功能
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
//类中提供需要注入成员的set方法
public void setName(String name) {
System.out.println("调用了set方法,传入的值是:"+name);
this.name = name;
}
public void setAge(Integer age) {
System.out.println("调用了set方法,传入的值是:"+age);
this.age = age;
}
public void setBirthday(Date birthday) {
System.out.println("调用了set方法,传入的值是:"+birthday);
this.birthday = birthday;
}
@Override
public String toString() {
return "UserServiceImpl{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
xml配置
<!-- 通过配置文件给bean中的属性传值:使用set方法的方式
property标签:调用set方法赋值
name属性:找的是类中set方法后面的部分(写法:去掉第一个set,剩余的第一个字母小写)
value属性:相当于给set方法传入的参数
ref属性:引用其他的Bean对象
-->
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl">
<property name="age" value="18"></property>
<property name="name" value="张三三"></property>
<property name="birthday" ref="userdate"></property>
</bean>
<!--相当于new java.util.Date();-->
<bean id="userdate" class="java.util.Date"></bean>
测试:
public class IOCTest {
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserService userService1 = (UserService) applicationContext.getBean("userService");
System.out.println(userService1);
}
}
输出:
2.使用p名称空间注入数据——本质是调用类中的set方法实现注入功能
<bean id="userService3" class="com.mycode.service.impl.UserServiceImpl"
p:name="李四四"
p:age="19"
p:birthday-ref="userdate"
/>
<bean id="userdate" class="java.util.Date"></bean>
3.构造函数注入
使用类中的相应构造函数给成员变量赋值。赋值的操作是通过配置的方式,让spring框架来帮我们注入。
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
public UserServiceImpl(String name, Integer age, Date birthday) {
System.out.println("构造函数注入");
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
①根据构造函数中参数的名字注入
<!--根据构造函数中参数的名字注入-->
<bean id="userService4" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg name="name" value="王五五_根据构造函数中参数的名字注入"/>
<constructor-arg name="age" value="21"/>
<constructor-arg name="birthday" ref="userdate"/>
</bean>
②根据下标注入
<!-- 根据下标注入-->
<bean id="userService5" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg index="0" value="王五五_根据下标注入"/>
<constructor-arg index="1" value="20"/>
<constructor-arg index="2" ref="userdate"/>
</bean>
③根据构造函数中参数的类型注入
<!--根据构造函数中参数的类型注入-->
<bean id="userService6" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg type="java.lang.String" value="王五五_根据构造函数中参数的类型注入" />
<constructor-arg type="java.lang.Integer" value="23" />
<constructor-arg type="java.util.Date" ref="userdate" />
</bean>
4.集合属性的注入
public class UserDaoImpl implements UserDao {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public String toString() {
return "UserDaoImpl{" +
"myStrs=" + Arrays.toString(myStrs) +
", myList=" + myList +
", mySet=" + mySet +
", myMap=" + myMap +
", myProps=" + myProps +
'}';
}
}
xml配置
<!--
创建Bean对象
-->
<bean id="userDaoImpl" class="com.mycode.dao.impl.UserDaoImpl">
<!--注入数组-->
<property name="myStrs">
<array>
<value>array1</value>
<value>array2</value>
<value>array3</value>
</array>
</property>
<!--注入List集合-->
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!--注入Set-->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 注入map-->
<property name="myMap">
<map>
<entry key="map1" value="map1value" />
<entry key="map2" value="map2value" />
<entry key="map3" value="map3value" />
</map>
</property>
<!--注入properties数据-->
<property name="myProps">
<props>
<prop key="prop1">prop1value</prop>
<prop key="prop2">prop2value</prop>
<prop key="prop3">prop3value</prop>
</props>
</property>
</bean>
测试:
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserDao UserDao = (UserDao) applicationContext.getBean("userDaoImpl");
System.out.println(UserDao);
}
}
输出:
使用Spring基于xml配置的IOC实现CRUD,Spring集成DBUtils(实现业务层和持久层解耦)
把配置文件与Java关联起来可以这样理解:
创建Bean
<bean id="usertService" class=" com.mycode.service.impl.UsertServiceImpl">
<!--相当于-->
UsertService usertService= new com.mycode.service.impl.UsertServiceImpl()
set方法注入
<property name="name" value="张三三" />
<!--相当于-->
usertService.setName("张三三")
构造函数注入
<constructor-arg name="name" value="张三三" />
<!--相当于-->
new UsertServiceImpl("张三三");