IOC(Inversion of Control)控制反转
控制反转(Inversion of Control),把创建对象的权力交给框架,是框架的重要特征,并非面向对象变成的专用术语,包括依赖注入(Dependency Injection)和依赖查找Dependency Lookup
1.传统的操作实例化Bean(手动初始化Bean)
private IAccountDao accoutDao = new AccountDaoImpl();
交给Spring管理后(声明在xml里面的Bean)
private IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
这种被动接收的方式获取对象的思想就是控制反转,是Spring框架的核心之一
IOC的作用就是帮我们削弱计算机程序的耦合(减少我们代码中的依赖关系)
实战演习
1.准备Spring依赖包(5.0.2)
解压后
- docs 顾名思义API文档
- libs 所用的工具jar包(aop,aspects,beans,context,core,expression)
- schema 所有Spring合集的xsd约束文件
2.操作
2.1Maven中引入pom
2.2创建xml文件(bean.xml)
- 添加文件头约束描述文件
- 声明想要创建的Bean
- IDEA创建项目结构
- 创建模拟操作的类
- 结果
由此可以说明Spring帮我们创建实例成功,如果你写个for循环会发现打印多次的类和类的hash码是
相同的,也说明了Spring帮我们创建的是单例的实例。。。。(PS:挖个小坑。。。。)
ApplicationContext的三个常用实现类
1.ClassPathXmlApplicationContext(类路径加载)
它可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,便加载不了
上面图片便是,不在重复
2.FileSystemXmlApplicationContext(文件路径加载)
它可以加载磁盘任意路径下的配置文件(必须有访问权限)
3.AnnotationConfigApplicationContext(注解加载)
它是用于读取注解创建容器的配置类信息
被@Configuration声明的Spring配置类,详见请看SpringConfigration.class
作用:当采用配置类配置Spring常用的工具集时,采用该注解进行测试SpringBoot大部分都采用配置类
public class AccountServiceTest{
@Test
public void testFindAll(){
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
IAccountService as = ac.getBean("accountService",IAccountService.class);
System.out.println(as.findAllAccount());
}
// 省略...
}
!!!废话不多说,上代码!!!
@Configuration
@ComponentScan(value="com.heartskihigh")
//ComponentScan(basePackages="")
public class SpringConfiguration{
@Bean("runner")
public QueryRunner createQueryRunmer(DataSource dataSource){
return new QueryRunner(dataSource);
}
@Bean("dataSource")
public DataSource createDataSource(){
try{
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/user");
ds.setUser("root");
ds.setPassword("123456");
} catch(Exception e){
log.error("【数据源异常】,{}",e);
throw new NsiOperationException("初始化数据源失败...");
}
}
// 省略...
}
核心容器的两个接口的区别
1.ApplicationContext(单例对象适用)
它是在构建容器时,创建对象采取的策略是采用立即加载方式,也就是只要读完配置文件马上就创建文件
中配置的对象
- Debug后发现,读取完配置文件,对象就被创建并存入容器(类中添加一个无参构造,并打印一句话)
2.BeanFactory(多例对象适用)
它是在构建核心容器时,创建对象采取的策略时延迟加载的方式,什么时候根据Id获取对象,什么时候在创
建对象
- Debug后发现,读取完配置文件,并没有立即创建,而是当执行完getBean方法时,对象才被创建
Spring对Bean的管理细节
1.创建Bean的
使用默认构造函数创建
在Spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时
采用的就是默认无参构造函数创建对象,此时如果类中没有默认构造函数,则对象无法闯将
<bean id="instanceFactory" class="com.heartskyhigh.factory.InstanceFactory"></bean>
2.工厂中的普通方法创建对象
使用xml配置普通方法创建对象
public class InstanceFactory{
public void getAccountService(){
return new AccountServiceImpl();
}
}
<bean id="instanceFactory" class="com.heartskyhigh.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
3.工厂中的静态方法创建对象
使用xml配置静态方法创建对象
public class StaticFactory{
public static IAccountService getAccountService(){
return new AccountServiceImpl();
}
}
<bean id="accountService" class="com.heartskyhigh.factory.StaticFactory" factory-method="getAccountService"></bean>
Spring Bean对象的作用范围和生命周期
bean标签的scope属性,用于指定bean的作用范围
取值singleleton,prototype,request,session,global-session
- singleton:默认单例,无论创建多少次,整个spring应用上下文只有一个实例
当容器创建时对象出生,只要容器存在,对象一直存活,容器销毁对象消亡
- prototype:多例,创建多少次就有多少个对象,相当于new了两次
使用对象时spring为我们创建对象出生,对象使用过程中一直存活,当对象长时间不用且没有别的对象引用
时,由java的GC回收
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService ac1 = (IAccountService)ac.getBean("accountService");
IAccountService ac2 = (IAccountService)ac.getBean("accountService");
System.out.println(ac1==ac2);
}
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的绘画范围(全局绘画范围,当不是集群环境,它就是session)
DI(Dependency Injection)依赖注入
1.可以注入的类型三种
- 基本数据类型和String
- 其他bean类型(配置文件里面的bean)
- 复杂类型/集合类型
2.注入的方式
- 使用构造函数提供
使用constructor-arg标签,出现在bean标签内部
取值type,index,name,value,ref
type:用于指定注入数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置从0开始
name:用于指定给构造函数中指定名称的参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据,它指的就是在Spring的IOC核心容器中出现锅的bean对象
<bean id="accountService" class="com.heartskyhigh.factory.StaticFactory" factory-method="getAccountService">
<constructor-arg name="name" value="TuoMaSi"></constructor-arg>
<constructor-arg name="age" value="17"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
优势:获取bean对象,注入数据时是必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们创建对象时,如果用不到这些数据,也必须提供
- 使用set方法提供
使用property标签,出现在bean标签内部
属性name,value,ref
<bean id="accountService" class="com.heartskyhigh.factory.StaticFactory" factory-method="getAccountService">
<property name="name" value="TuoMaSi"></property>
<property name="age" value="17"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
优势:创建对象没有限制,可以直接使用默认构造函数
弊端:如果某个成员必须有值,则获取对象是有可能set方法没有执行
- 给数组,集合,Map赋值
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
// set方法省略。。。
<bean id="accountService" class="com.heartskyhigh.factory.StaticFactory" factory-method="getAccountService">
<!-- 定义数组 -->
<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="test1" value="AAA"></entry>
<entry key="test2">
<value>BBB</value>
</entry>
</map>
</property>
<!-- 定义Properties -->
<property name="myProps">
<props>
<prop key="test1">AAA</prop>
<prop key="test2">BBB</prop>
</props>
</property>
</bean>
<bean id="now" class="java.util.Date"></bean>
- 使用注解提供
后续补充。。。。