spring5(6) ---Ioc和DI
spring5(6) ---Ioc和DI
目录
一.基础
正控:若调用者需要使用某个对象,其自身就得负责该对象及该对象所依赖对象的创建和组装
反控:调用得只管理从Spring容器中获取需要使用的对象,不关心对象的创建过程,也不关心该对象依赖对象的创建及依赖关系的组装,也就是把创建对象的控制权反转给了Spring框架。
Ioc:Inversion of Control(控制反转/反转控制),这不是什么新技术,而是一种设计思想,好比如MVC,其本意是将原本在程序中手动创建对象的控制,交由Spring框架来管理
DI:Dependency Injecttion(依赖注入)从字面上分析,Ioc指将对象的创建权反转给了Spring容器;即指Spring创建对象的过程中,将对象依赖属性(常量,对象,集合)通过配置设值给该对象
例子:
实体类:
-
public class HelloWorld{
-
private String name;
-
private int age;
-
...相应的get和set方法
-
}
XML配置:
-
<bean id="helloWorld" class="com.bigfong.HelloWorld">
-
<property name="name" value="bigfong">对应HelloWorld中的setName方法
-
<property name="age" value="20">对应HelloWorld中的setAge方法
-
</bean>
测试代码:
-
HelloWorld helloWorld = null;
-
//加载Spring配置文件applicationContext.xml
-
Resource resource = new ClassPathResource("applicationContext.xml");
-
//创建Spring容器对象
-
BeanFactory factory = new XmlBeanFactory(resource);
-
//从Spring容器中获取制定名为helloWorld的bean
-
helloWorld = (HelloWorld)factory.getBean("helloWorld");//xml对应bean配置的id值
-
helloWorld.sayHello();
BeanFactory是Spring最古老的接口,表示Spring Ioc容器------生产bean对象的工厂,负责配置,创建和管理bean,被Spring Ioc容器管理的对象称之为bean
Spring Ioc容器如何得知哪些是它管理的对象:
此时需要配置文件,Spring Ioc容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配
二.Spring Ioc管理bean的原理:
1.通过Resource对象加载配置文件
2.解析配置文件,得到指定名称的bean
3.解析bean元素,id作为bean的名字,class用于反射得到bean的实例;(此时,bean类必须存在一个无参构造器【和访问权限无关】);
4.调用getBean方法的时候,从容器中返回对象实例
结论:就是把代码从java文件中转移到了XML中
模拟Spring Ioc容器操作
-
HelloWorld helloWorld = null;
-
String className = "com.bigfong.HelloWorld";
-
Class clz = Class.forName(className);
-
Constructor con = clz.getDeclaredConstructor();
-
con.setAccessible(true);
-
helloWorld = (HelloWorld)con.newInstance();
-
BeanInfo beanInfo = Introspector.getBeanInfo(helloWorld.getClass());
-
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
-
for(PropertyDescriptor pd:pds){
-
String propertyName = pd.getName();
-
Method setterMethod = pd.getWriteMethod();
-
if("name".equals(propertyName)){
-
setterMethod.invoke(helloWorld,"bigfong");
-
}elseif("age".equals(propertyName)){
-
setterMethod.invoke(helloWorld,20);
-
}
-
}
-
helloWorld.sayHello();
三.getBean方法的三种签名
1.按照bean的名字拿bean
helloWorld = (HelloWorld)factory.getBean("helloWorld");
2.按照类型拿bean,要求在Spring中只配置一个这种类型的实例
helloWorld = (HelloWorld)factory.getBean(HelloWorld.class);
3.按照名字和类型拿bean(推荐)
helloWorld = (HelloWorld)factory.getBean("helloWorld",HelloWorld.class);
四.创建bean时机
1.使用BeanFactory
-
Resource resource = new ClassPathResource("com/bigfong/container/container.xml");//在这里不需要添加"classpath:"开头
-
BeanFactory factory = new XMLBeanFactory(resource);
-
HelloWorld helloWorld = factory.getBean("helloWorld",HelloWorld.class);
BeanFactory有延迟初始化的特点,在创建Spring容器的时候,不会立马去创建容器中管理的Bean对象,而是要等到从容器中去获取对象的时候,才去创建对象。
2.使用ApplicationContext (Web应用建议)
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/bigfong/container/container.xml");//在这里不需要添加"classpath:"开头
-
HelloWorld helloWorld = ctx .getBean("helloWorld",HelloWorld.class);
在创建Spring容器的时候,就会把容器中管理的bean立马初始化,而不会等到获取bean的时候才去初始化.
五.bean实例化的方式:
1.构造实例化(无参数构造器),最标准,使用最多
-
<bean id="cat1" class="com.bigfong.createbean.Cat1"/>
-
com.bigfong.createbean.Cat1类有无参构造器
-
根当于 Cat1 cat2 = new Cat1();
2.静态工厂方法实例化:解决系统遗留问题
-
<bean id="cat2" class="com.bigfong.createbean.Cat2Factory" factory-method="createInstance"/>
-
其中com.bigfong.createbean.Cat2Factory
-
public static Cat2 createInstance(){
-
Cat2 cat2 = new Cat2();
-
return cat2;
-
}
-
相当于 Cat2 cat2 = Cat2Factory.createInstance();
3.实例工厂实例化:解决系统遗留问题
-
<bean id="cat3Factory" class="com.bigfong.createbean.Cat3Factory"/>
-
<bean id="cat3" factory-bean="cat3Factory" factory-method="createInstance"/>
-
其中com.bigfong.createbean.Cat3Factory
-
public Cat3 createInstance(){
-
Cat2 cat2 = new Cat2();
-
return cat2;
-
}
-
相当于 Cat3 cat3 = new Cat3Factory().createInstance();
4.实现BeanFactory接口实例化:实例工厂变种,如集成MyBatis框架使用:
org.mybatis.spring.SqlSessionFactoryBean
-
<bean id="cat4" class="com.bigfong.createbean.Cat4Factory"/>
-
其中com.bigfong.createbean.Cat4Factory
-
public class Cat4Factory implements FactoryBean<Cat4>{
-
//实例工厂方法
-
public Cat4 getObject() throws Exception{
-
Cat4 cat4 = new Cat4();
-
return cat4;
-
}
-
public Class<?> getObjectType(){
-
return Cat4.class;
-
}
-
}
-
相当于 Cat4 cat4 = new Cat4Factory().getObject();
六.bean作用域:
在spring容器中指创建的Bean对象相对于其他Bean对象的请求可见范围
<bean id="" class="" scope="作用域"/>
1.singleton:单例,在spring Ioc容器中仅存在一个Bean实例(默认的scope)
2.prototype:多例,每次从容器中调用Bean时,都返回一个新的实例,即每次调用 getBean()时,相当于new XxxBean();不会在容器启动的时候创建对像
3.request:用于web开发,将Bean放入request范围,request.setAttribute("xxx"),在同一个request获得同一个bean
4.session:用于web,将Bean放入Session范围,同一个Session获得同一个Bean
5.globalSession:一般用于Porlet应用环境,分布式系统存在全局session概念(单点登录),如果不是porlet环境,globalSession等同于Session
6.application:
7.websocket:
spring5开始出现:websocket,globalSession作废
在开发中主要使用:singletom,prototype
总结:对于Struts1中的Action使用request,Struts2中的Action使用prototype类型,其他使用singleton
七.bean初始化和销毁
<bean id="user" class="com.bigfong.user" scope="prototype" init-method="open" destroy-method="close"/>
init-method:定义初始化方法,在构造器执行之后立马执行
destroy-method:定义销毁之前的方法,在销毁之前调用
测试:
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/bigfong/context.xml");
-
User user = ctx.getBean("user",User.class);
此时因为没有正常关闭spring容器,所以不会执行释放资源的方法,可以使用@Cleanup注解
-
@Cleanup
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/bigfong/context.xml");
-
User user = ctx.getBean("user",User.class);
最好的方式是把spring线程作为JVM的子线程,ctx.registerShutdownHook();
-
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/bigfong/context.xml");
-
User user = ctx.getBean("user",User.class);
-
ctx.registerShutdownHook();
原理:如果bean的scope="prototype",容器只负责创建和初始化,它并不会被spring容器管理,交给用户自己调用 。
八.bean实例化过程
bean的完整生命周期经历了各种方法,这些方法可以划分为改下几类:
1.Bean自身的方法: 包括Bean本身调用 的方法和通过配置文件中<bean>的init-method和destrory-method指定的方法
2.Bean级生命周期接口方法: 包括了BeanNameAware、BeanFactoryAware、InitializingBean和DisposibleBean这些接口方法
3.容器级生命周期接口方法:包括InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现类,一般称它们的实现类为“后处理器”
4.工厂后处理器接口方法:包括AspectJWeavingEnabler,ConfigurationClassPostProcessor,CustomAutowireConfigurer等非常有用的工厂后处理器接口方法。工厂后处理器也是容器级的。在应用上下文装配配置文件后立即调用。
DI基础
注入方式:1.setter注入,2.构造器注入
注入的值类型:
1.简单类型: value元素
2.对象: ref元素
3.集合:对应集合类型元素<set> <list>
通过XML装配
1.XML自动装配(不推荐)
<bean id="somebean" class="com.bigfong.SomeBean" autowire="byType">
autowire:让spring按照一定的规则方式自己去找合适的对像,并完成DI操作
default:不要自动注入,缺省default列示no
no:不要自动注入
byName:按照名字注入(近照属性名字在srping中找bean)factory.getBean(beanName)
byType:按照依赖对像的类型注入(factory.getBean(Class requiredType))
constructor:按照对象的构造器上面的参数类型注入
2.setter注入
-
<bean id="cat1" class="com.bigfong.Cat">
-
<bean id="user" class="com.bigfong.User">
-
<property name="username" value="bigfong"/>//常量
-
<property name="cat" ref="cat1"/>//对象
-
<property name="set">集合:set
-
<set>
-
<value>set1</value>
-
<value>set2</value>
-
<value>set3</value>
-
</set>
-
</property>
-
<property name="list">集合:list
-
<list>
-
<value>list1</value>
-
<value>list2</value>
-
<value>list3</value>
-
</list>
-
</property>
-
<property name="array">集合:Array
-
<array>
-
<value>array1</value>
-
<value>array2</value>
-
<value>array3</value>
-
</array>
-
</property>
-
<property name="map">集合:Map
-
<map>
-
<entry key="key1" value="value1"/>
-
<entry key="key2" value="value2"/>
-
<entry key="key3" value="value3"/>
-
</map>
-
</property>
-
<property name="prop">集合:Properties类型 方式一
-
<props>
-
<prop key="key1" ">value1</prop>
-
<prop key="key2" ">value2</prop>
-
<prop key="key3" ">value3</prop>
-
</props>
-
</property>
-
<property name="prop">集合:Properties类型 方式二
-
<value>
-
p1=v1
-
p2=v2
-
p3=v3
-
</props>
-
</property>
-
</bean>
3.构造器注入
-
<bean id="cat1" class="com.bigfong.Cat">
-
<bean id="user" class="com.bigfong.User">
-
<constructor-arg name="username" value="bigfong">
-
<constructor-arg name="age" value="20">
-
<constructor-arg name="weight">
-
<null/>
-
</constructor-arg>
-
<constructor-arg name="cat" ref="cat1"/>
-
<constructor-arg bane="set">集合:set
-
<set>
-
<value>set1</value>
-
<value>set2</value>
-
<value>set3</value>
-
</set>
-
</constructor>
-
</bean>
3.bean元素继承
-
//多个bean工内配置的抽取
-
<bean id="base" abstract="true">
-
<property name="username" vaue="bigfong"/>
-
<property name="age" vaue="20"/>
-
</bean>
-
<bean id="somebean" class="com.bigfong.user" parent="base">
-
<property name="weight" value="50">
-
</bean>
-
<bean id="somebean2" class="com.bigfong.user" parent="base">
-
<property name="color" value="yellow">
-
</bean>
4.property place holder
方法一:数据库连接池
-
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
-
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
-
<property name="url" value="jdbc:mysql://localhost:3306/springDemo"/>
-
<property name="username" value="root"/>
-
<property name="password" value="admin"/>
-
<property name="initialSize" value="2"/>
-
</bean>
方法二:使用property place holder
db.properties
-
jdbc.driverClassName=com.mysql.jdbc.Driver
-
jdbc.url=jdbc:mysql://localhost:3306/springdemo
-
jdbc.username=root
-
jdbc.password=admin
-
jdbc.initialSize=2
App-context.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:p="http://www.springframework.org/schema/p" 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-4.3.xsd">
-
<!-- 从classpath的根路径去加载db.properties文件 -->
-
<context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER" />
-
<!-- 配置一个druid的连接池 -->
-
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
-
<property name="driverClassName" value="${jdbc.driverClassName}" />
-
<property name="url" value="${jdbc.url}" />
-
<property name="username" value="${jdbc.username}" />
-
<property name="password" value="${jdbc.password}" />
-
<property name="initialSize" value="${jdbc.initialSize}" />
-
</bean>
-
</beans>