1. IOC | 控制反转
削减程序间的耦合,例如用工厂模式来创建对象,但工厂模式要自己编写工厂类,Sping中完成了对此类的封装。
Spring的IOC容器是一个Map结构,Map<String, Object>
2. Spring只负责解耦
3. Sping配置
3.1 配置bean.xml
官网cv配置代码
<?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对象的名称和全限定类名
<bean id="accountService" class="com.alan.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.alan.dao.impl.AccountDaoImpl"></bean>
获取bean对象时可以通过两种方式指定类:
//获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
//两种得到特定类型对象的方法,进行类型强转或者传入一个类型的字节码如下
IAccountDao adao = ac.getBean("accountDao", IAccountDao.class);
3.2 ApplicationContext接口
ApplicationContext是BeanFactory的子接口
ApplicationContext接口的三个常用实现类
ClassPathXmlApplicationContext() //可以加载类路径下的配置文件,要求配置文件必须在类路径下,直接传入配置文件名
FileSystemXmlApplicationContext() //可以加载磁盘任意路径下的配置文件(必须有访问权限),需传入配置文件的绝对路径
AnnotationConfigApplicationContext() //读取注解创建容器
3.3 ApplicationContext和BeanFactory的区别
ApplicationContext:在构建核心容器时,创建对象采取的策略是立即加载,只要一读取完配置文件马上创建配置文件中的配置的对象。适用于单例对象。
可以根据对象的单例或多例采用立即加载还是延迟加载
BeanFactory:创建对象采用延迟加载策略,根据id获取对象时才会真正创建对象。适用于多例对象。
4. 创建Bean对象细节
4.1 Bean创建的三种方式
<!--创建bean的三种方式-->
<!--1. 使用默认构造函数,spring配置中使用bean标签,配以id和class且无其他属性和标签,类中必须有默认构造函数才可以创建成功-->
<bean id="accountService" class="com.alan.service.impl.AccountServiceImpl"></bean>
<!--2.使用普通工厂中的方法创建对象,并存入spring对象-->
<bean id="instanceFactory" class="com.alan.factory.InstanceFactory"></bean>
<!--使用创建的工厂类创建所需对象-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
<!--3.使用工厂(或某个类)中的静态对象创建方法并存入spring容器-->
<bean id="accountService" class="com.alan.factory.staticFactory" factory-method="getAccountService"></bean>
方法二对应以下工厂类文件:
public class InstanceFactory {
public IAccountService getAccountService() {
return new AccountServiceImpl();
}
}
//由于重写了构造函数,InstanceFActory中没有默认构造函数
方法三对于以下工厂类文件:
public class staticFactory {
public static IAccountService getAccountService() {
return new AccountServiceImpl();
}
}
//通过静态方法返回所需对象,因此需要factory-method来调用此静态方法
4.2 Bean对象的作用范围
Bean对象默认情况下是单例的,可通过bean标签的scope属性来调整bean的作用范围
取值:
singleton:单例的(default)
prototype:多例的
request:作用与web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围,但不是集群环境时,它就是session
拓展:session的作用范围
4.3 bean对象的生命周期
单例:
创建:容器创建时对象创建,即读取配置文件时对象就被创建了
销毁:容器销毁时对象销毁
多例:
创建:使用对象时spring框架创建对象
销毁:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
5. Spring的依赖注入
5.1 依赖注入(dependency Injection):
5.1.1 IOC降低耦合,依赖管理交给Spring维护,在当前类需要用到其他类的对象,由spring提供,只需在配置文件中说明,这种关系的维护称为依赖注入
常见于为类的属性进行初始化
5.1.2 能够注入的数据类型:
基本类型和String
其他bean类型(在配置文件或者注解配置过的bean)
复杂类型/集合类型
5.1.3 注入方式:
第一种:构造函数提供
第二种:使用set方法提供
第三种:使用注解提供
注:如果是经常变化的数据,不适合用注入的方式
5.2 构造函数注入:
方法:
使用标签<constructor-arg></constructor-arg>,置于bean标签内
适用:有含参构造函数的类
属性包括:
type: 用于指定要注入的数据类型,该数据类型是构造函数中某些参数的类型
index:指定要注入的数据给构造函数种指定索引位置的参数赋值,索引的位置从0开始
name:用于指定构造函数中指定名称中的参数赋值
================以上三个用于指定给构造函数中哪个参数赋值(一般直接用name)==================
value:要赋的值,都以字符串形式出现,用于给基本类型和String类型赋值,spring自动转类型
ref:引用其他bean类型,用其他bean类型数据的id赋值,要引用的对象需在bean中配置过
<bean id="accountService" class="com.alan.service.impl.AccountServiceImpl" >
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="12"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
弊端:有时候并不需要每个属性都赋值,但此方法不给每个方法赋值就无法创建对象。
5.3 set方法注入
方法:property标签,位于bean标签内部
适用:类无含参构造函数,且存在默认构造函数,且有属性对应的setter()方法
<bean id="accountService2" class="com.alan.service.impl.AccountServiceImpl2">
<property name="age" value="12"></property>
<property name="name" value="Alan"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
优势:创建对象时没有明确限制,可以直接使用默认构造函数
弊端:如果某个成员必须有值,则获取对象有可能set方法无法保证一定注入
5.4 复杂数据类型注入
比如List,Map,Set,Properties,String[]
方法:<property></property>
<bean id="accountService3" class="com.alan.service.impl.AccountServiceImpl3">
<property name="myStr">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>DDDD</value>
</list>
</property>
<property name="myMap">
<map>
<entry key="123" value="456"></entry>
<entry key="abc">
<value>efg</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testc">ccc</prop>
<prop key="testb">bbb</prop>
</props>
</property>
</bean>
注:用于给List结构集合注入的标签:
<list></list>
<array></array>
<set></set>
用于给Map结构集合注入的标签:
<map></map>
<props></props>
以上,结构相同,标签可以互换。
5.5 注解注入
@Component
@Controller //一般用在表现层
@Service //一般用在业务层
@Repository //一般用在持久层
以上作用和属性完全一致,分别用于三层框架,如果不属于任何一层,一般用@Component
5.5.1 @Component
/**
* 账户的业务层实现类
* <bean id="accountService" class="com.alan.service.impl.AccountServiceImpl"
* scope="" init-method="" destory-method="">
* <property name="" value="" | ref=""></property>
*</bean>
* Component用于把当前类对象存入spring容器中,属性value:用于指定bean的id,不写时,
* 默认值是当前类名且首字母改小写
*/
@Component
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void saveAccount() {
accountDao.saveAccount();
}
}
@Component也可自己指定要得到对象的id:
@Component(value = "accountService")
bean.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是在一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.alan"></context:component-scan>
<!--会扫描所有类上和接口上的注解-->
</beans>
5.5.2 注解注入类属性
@Autowired
/** Autowired:
* 自动按照类型注入,只要容器中有唯一(必须是唯一)的
* 一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
* 如果有多个类型匹配,要先根据类型圈定匹配对象,
* 会使用变量名称作为id在map中的bean中进行选择
* 位置:变量上,方法上
* 优势:使用注解注入时,set方法不是必要的
*/
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao2;
public void saveAccount() {
accountDao2.saveAccount();
}
}
注:当IOC容器中有多个匹配的bean(同一接口的不同实现类)时,会根据属性名和实现类名一致的原则进行匹配,如以下情况中就会将AccountDaoImpl2类型的对象注入
IOC容器中的bean
5.5.2.1 @Qualifier | @Resource
@Qualifier 解决了@Autowired在有多个实现相同接口的类的bean时不能指定注入对象的问题
但要和@Autowired配合使用
/** Qualifier:
* 在按照类型注入的基础上再按照名称注入,在给类成员注入时不能单独使用,但给方法参数注入时可以
* 属性:
* value:指定注入bean的id
* 注:必须和@Autowired配合使用
*/
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao;
public void saveAccount() {
accountDao.saveAccount();
}
}
@Resource
可以单独使用,属性为name=""
/** Resource:
* 直接按照bean的id注入,可以独立使用,属性为name,指定bean的id
*/
@Service(value = "accountService")
public class AccountServiceImpl implements IAccountService {
@Resource(name="accountDao1")
private IAccountDao accountDao2;
public void saveAccount() {
accountDao2.saveAccount();
}
}
小结:@Autowired @Qualifier @Resource都只能注入其他bean类型的数据,基本类型和String类型无法用上述注解实现,集合类型的注入只能通过XML实现。
5.5.3 @Scope | @PreDestroy | @PostConstruct
/**Scope
* 指定单例或多例
* 属性 value,取值为singleton或prototype
* PreDestroy
* 用于指定销毁方法
* PostConstruct
* 用于指定初始化方法
*/
@Service(value = "accountService")
@Scope(value = "singleton")
public class AccountServiceImpl implements IAccountService {
@Resource(name="accountDao1")
private IAccountDao accountDao2;
@PostConstruct
public void init() {
System.out.println("init");
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
public void saveAccount() {
accountDao2.saveAccount();
}
}