1、IoC 依赖注入(控制反转) 方便解耦
耦合:
业务层的A类中要使用持久层B类对象,于是在A类中new了一个B类对象来用,这就导致了A类会强依赖于B类,产生耦合,耦合只能减弱不能消除,消除相当于这两个类毫无关系了。
减弱耦合:
不在A类中newB类的对象,建立一个对象容器(工厂),这个容器里面就产生各种对象给别的类调用,对象的生成只在这个容器中,可以把要生成的对象path写在properties或xml文件中,在容器中用静态代码块读取properties或xml中的内容,通过反射生成对象,保存在容器中,比如一个Map中,再向外界提供一个根据map的key获取对象的方法。
1、1那么spring是如何做到减弱耦合的呢?
原理和上面的对象容器工厂差不多,容器由spring完成了,我们只需要在xml中配置业务层和持久层的对象信息:说明要创建哪个对象、用什么方式去取:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”><bean/>
<bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
id:对象的唯一标示;class:要创建对象的全限定类名
spring有一个核心容器,xml中读取的对象都生成放在核心容器中;下面我们只需要获取到核心容器后,再根据bean的id获取对象:
核心容器类:
ApplicationContext:
是BeanFactory下的子接口的再子接口。
特点是创建bean对象时,采用的是立即加载的策略。(当读取完xml配置文件,配置文件中所有的bean对象都已经创建完成了,实测)
BeanFactory:
它是springioc容器的顶层接口。
特点是创建bean对象时,采用的是延迟加载的策略。(当真正要从容器获取bean对象时才创建,读完配置文件时不创建)
单例对象可以用ApplicationContext,多例对象用BeanFactory,不过spring会帮我们处理,一般都用ApplicationContext,设置属性可以选择单例多例。
- 用bean标签创建bean对象三种创建方式:
1、通过调用默认构造函数来创建bean对象,默认情况下,在bean标签中写了class属性,spring就会调用默认构造函数创建对象,如果没有默认构造函数,则对象创建失败:
<bean id=“唯一标示” class=“全限定类名”></bean>
2、静态工厂创建,项目中已经有一个创建对象的工厂类,其中有创建对象的静态方法可以使用:
<bean id=“唯一标示” class=“全限定类名(工厂类)” factory-method=“方法名”></bean>
3、实例工厂创建,项目中已经有一个创建对象的工厂类,其中有创建对象的非静态方法可以使用:
<bean id=“唯一标示” factory-bean=“工厂bean的id” factory-method=“方法名”></bean>
-
bean对象的作用范围:
可以通过一个配置属性来指定:scope属性: 比如 scope=“singleton”
有几个值:
singleton:单例的 (默认值,也最常用)
prototype:多例的
request:请求范围
session:会话范围
global-session:全局会话范围 -
Bean对象的生命周期:
单例对象
出生:容器创建,对象出生
活着:只要容器在,对象就一直可用
死亡:容器销毁,对象消亡
多例对象
出生:每次使用时,容器才会为我们创建对象
活着:只要对象在使用过程中就一直活着
死亡:spring不知道对象你什么时候用完,spring把这件事交给了java的gc:对象长时间不用,并且也没有其他对象引用时,java的垃圾回收器回收
1、2对象创建出来了,那么依赖关系要怎么构建呢,spring提供了依赖注入:
场景:展示层调用AccountServiceImpl(业务层)的saveAccount方法,这个方法中又调用AccountDao(持久层)类的保存账号方法,那这个时候AccountServiceImpl类中就需要引用到AccountDao对象,我们前面已经把这两个对象都用spring创建出来了,那么现在开始往AccountServiceImpl中依赖AccountDao对象,为了简单我们先用简单类型的属性代替AccountDao对象引入到AccountServiceImpl中:
1、构造函数注入:
使用constructor-arg标签,该标签是写在bean标签内部的子标签:
标签的属性:
type:指定要注入的参数在构造函数中的类型
index:指定要注入的参数在构造函数中的索引位置
name:指定要注入的参数在构造函数中的名称
value:指定要注入数据内容,他只能指定基本类型数据和String类型数据
ref:指定其他bean类型数据。写的是其他bean的id。其他bean指的是存在于spring容器中的bean。
例子:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<constructor-arg name=“name” value=“xc”></constructor-org>
<constructor-arg name=“age” value=“27”></constructor-org>
<constructor-arg name=“birthday” ref=“now”></constructor-org>
<bean/>
<bean id=“now” class=“java.util.Date”></bean>
这种方式需要AccountServiceImpl类中有带有这三个参数的构造函数,否则报错。
这时候在展示层通过spring获取的AccountServiceImpl对象其中的三个属性就已经有了上面设置的值,这感觉有点像是初始化对象赋值呢?不过好像确实是这样,因为是用构造函数注入的。
2、使用setter方法注入:(常用)
涉及的标签:property。也是要写在bean标签内部的子标签
标签属性:
name:指定的是setter方法的名称。匹配的是类中setter方法set后面的部分:setAppName(),那么这里就取appName,首字母要小写。
value:指定要注入数据内容,他只能指定基本类型数据和String类型数据
ref:指定其他bean类型数据。写的是其他bean的id。其他bean指的是存在于spring容器中的bean。
例子:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<property name=“name” value=“xc”></property>
<property name=“age” value=“27”></property>
<property name=“birthday” ref=“now”></property>
<bean/>
这种方式需要类中的属性有set方法,可以没有get方法,但是一定要有set方法.
用这种方式往AccountServiceImpl中引用AccountDao:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl”>
<property name=“accountDao” ref=“accountDao”></constructor-org>
<bean/>
<bean id=“accountDao” class=“com.demo.dao.impl.AccountDaoImpl”><bean/>
这样写完,业务层和持久层的依赖关系就交给了spring来维护了。
- 2.1使用p名称空间注入:它的本质仍然是需要类中提供set方法,同时在配置文件中要导入p名称空间:
<bean id=“accountService” class=“com.demo.service.impl.AccountServiceImpl” p:name=“张三”>
<bean/>
- 2.2复杂类型注入:注入的方式采用set方法,注入的类型都是集合类型:
注意:结构相同标签可以互换<!— 给数组注入数据—> <property name=“myStrs” > <array> <value>AAA</value> <value>BBB</value> <value>ccc</value> </array> </property> <!— 给list注入数据—> <property name=“myList” > <list> <value>AAA</value> <value>BBB</value> <value>ccc</value> </list> </property> <!— 给set注入数据—> <property name=“mySet” > <set> <value>AAA</value> <value>BBB</value> <value>ccc</value> </set> </property> <!— 给map注入数据—> <property name=“myMap” > <map> <entry key=“testA” value=“AAA”></value> <entry key=“testB” value=“BBB”></value> </map> </property> <!— 给properties注入数据—> <property name=“myProps” > <props> <prop key=“testa”>AAA</value> <prop key=“testB”>BBB</value> </props> </property> <bean/>
1.3用spring注解的方式再做一遍上面的工作:
和上面xml的目的都是为了降低耦合,只不过是形式不一样
* 用于创建对象的:
*
* @Component :
* 作用:就相当于在spring的xml配置文件中写了一个bean标签。
* 属性:
* value:用于指定bean的Id。当不写时,默认值是当前类名,首字母改小写。
* 由此注解衍生的三个注解:
* @Controller, 用于表现层
* @Service, 用于业务层
* @Repository 用于持久层
* 他们的作用以及属性和@Component的作用是一样的。它们的出现时spring框架为我们提供更明确的语义话来指定不同层的bean对象。
* <p>
* 用于注入数据的:
* @Autowired
* 作用:自动按照类型注入。只要容器中有唯一的类型匹配,则可以直接注入成功。
* 细节:当使用此注解注入时,set方法就可以省略了。
* 属性:required :是否必须注入成功。取值true(默认值)/false。当取值是true时,没有匹配的对象就会报错。写不写无所谓,反正没有匹配到总会报错,只不过报错信息不一样。
* 用于改变作用范围的:
* 和生命周期相关的:
*/
@Component(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired(required = false)
IAccountDao accountDao;
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = ac.getBean("accountService",IAccountService.class);
IAccountDao accountDao = ac.getBean("accountDao",IAccountDao.class);
accountService.saveAccount();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--告知spring创建容器时要扫描的包-->
<context:component-scan base-package="com.ncz.nczupkeep.biz"></context:component-scan>
</beans>
网友评论