详细的描述了Spring中Bean的实例化与依赖注入。
Bean的实例化
构造方法
Spring创建Bean的时候调用无参构造。(有参构造会报错,私有无参也可以,底层使用了反射)
注意:Spring中的报错从下向上看,直到找到有用的信息。
注意:若不存在无参构造方法,会抛出异常:BeanCreationException
添加无参构造后:
运行结果:
静态工厂
创建对象不需要自己new,创建一个工厂创建对象达到解耦的效果
样例代码:
package wang.factory;
import wang.dao.Dao; import wang.dao.impl.DaoImpl;
public class DaoFactory { public static Dao getDao() { System.out.println("factory set up ..."); return new DaoImpl(); } }
|
配置文件添加如下代码:
<bean id="daoFactory" class="wang.factory.DaoFactory" factory-method="getDao"/>
|
主函数:
package wang;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import wang.dao.Dao;
public class App3 { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Dao dao = (Dao) ctx.getBean("daoFactory"); dao.save(); } }
|
运行结果:
实例工厂
工厂代码同上
配置文件修改为如下代码:
<bean id="daoFactorytemp" class="wang.factory.DaoFactory"/> <bean id="daoFactory" factory-bean="daoFactorytemp" factory-method="getDao"/>
|
第一行为一个无用的bean,改良后即为第四种方法
主函数不变
运行结果:
FactoryBean(重要!)
在第三种方法存在两个问题
一、factory-bean属性使用时会创建一个无用的Bean
二、factory-method属性方法名不固定,非动态实现
因此,对此方法做了改良
factory实现一个接口FactoryBean
工厂代码
package wang.factory;
import org.springframework.beans.factory.FactoryBean; import wang.dao.Dao; import wang.dao.impl.DaoImpl;
public class DaoFactoryBean implements FactoryBean<Dao> { @Override public Dao getObject() throws Exception { return new DaoImpl(); } @Override public Class<?> getObjectType() { return Dao.class; }
}
|
配置文件修改为如下代码:
<bean id="daoFactory" class="wang.factory.DaoFactoryBean"/>
|
主函数不变
运行结果:
补充:Bean的生命周期
控制生命周期
方法一:
控制方法代码:
public static DaoImpl implements Dao { public void save() { System.out.println("Dao save ... ") } public void init() { System.out.println("Dao init ... ") } public void destroy() { System.out.println("Dao destroy ... ") } }
|
配置控制方法:
<bean id="Dao" class="wang.dao.impl.DaoImpl" init-method="init" destroy-method="destory"/>
|
方法二
实现InitializingBean, DisposableBean接口
public static DaoImpl implements Dao, InitializingBean, DisposableBean { public void save() { System.out.println("Dao save ... ") } public void init() throws Exception { System.out.println("Dao init ... ") } public void destroy() throws Exception { System.out.println("Dao destroy ... ") } }
|
生命周期
初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入( set操作)
4.执行bean初始化方法
使用bean
1.行业务操作
关闭/销毁容器
1.执行bean销毁方法
注:销毁的时机:容器关闭前触发bean的销毁
关闭容器方式:
手工关闭容器
ConfigurableApplicationContext接口close()
操作注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationcontext接口registerShutdownHook ()操作
依赖注入
setter注入
引用类型
详情见博客 SSM01-Spring中的Bean
步骤如下
1.在bean中定义引用类型属性并提供可访问的set方法
2.配置中使用property标签ref属性注入引用类型对象
简单类型
步骤如下
1.在bean中定义引用类型属性并提供可访问的set方法
2.配置中使用property标签value属性注入简单类型数据
构造器注入
引用类型(仅做了解)
步骤如下
1.在bean中定义引用类型属性并提供可访问的构造方法
2.配置中使用constructor-arg标签ref属性注入引用类型对象
简单类型
步骤如下
1.在bean中定义引用类型属性并提供可访问的set方法
2.配置中使用property标签value属性注入简单类型数据
注意:
配置中使用constructor-arg标签type属性设置按形参类型注入
配置中使用constructor-arg标签index属性设置按形参位置注入
依赖注入方式的选择(总结自黑马程序员,侵删)
1.强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
2.可选依赖使用setter注入进行,灵活性强
3.Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4.如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
5.实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6.自己开发的模块推荐使用setter注入
自动装配(优先级低于setter注入与构造器注入)
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式
按类型(常用)(不能对简单类型装配)
<bean id="daoFactory" class="wang.dao.impl.DaoImpl" autowire="byType"/>
|
注意:需要set方法
注意:按类型(byType)时,同类型的应该只有一个bean,否则分不清。
按名称(set方法第一个字母改为小写的名称,即标椎的变量名)
<bean id="daoFactory" class="wang.dao.impl.DaoImpl" autowire="byName"/>
|
注意:因为变量名与byName耦合度高,不建议使用。
按构造方法
集合注入(主要学习格式)
Dao接口
package wang.dao;
public interface DataDao { public void save(); }
|
Dao实现
package wang.dao.impl;
import wang.dao.DataDao;
import java.util.*;
public class DataDaoImpl implements DataDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setArray(int[] array) { this.array = array; }
public void setList(List<String> list) { this.list = list; }
public void setSet(Set<String> set) { this.set = set; }
public void setMap(Map<String, String> map) { this.map = map; }
public void setProperties(Properties properties) { this.properties = properties; }
public void save() { System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties); } }
|
配置文件:
<?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 id="dataDao" class="wang.dao.impl.DataDaoImpl"> <property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property> <property name="list"> <list> <value>java</value> <value>c++</value> <value>python</value> <value>php</value> </list> </property> <property name="set"> <set> <value>java</value> <value>python</value> <value>java</value> <value>php</value> </set> </property> <property name="map"> <map> <entry key="country" value="china"/> <entry key="province" value="shanxi"/> <entry key="city" value="xian"/> </map> </property> <property name="properties"> <props> <prop key="country">china</prop> <prop key="province">shanxi</prop> <prop key="city">xian</prop> </props> </property> </bean> </beans>
|
主函数:
package wang.dao;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App6 { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); DataDao dataDao = (DataDao) ctx.getBean("dataDao"); dataDao.save(); } }
|
运行结果:
第三方资源注入
容器
两种加载容器的方式
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
|
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\ssm\\ssm\\springtest\\springtest\\src\\main\\resources\\applicationContext.xml")
|
两种获取bean的方式
Dao dao = (DataDao) ctx.getBean("dao");
|
Dao dao = ctx.getBean("dao", Dao.class);
|
对比ApplicationContext和BeanFactory
BeanFactory是顶级接口的Bean是延迟加载
ApplicationContext是子接口(常用)也可以延迟加载,在bean中加入参数 lazy-init=“true” 即可
加载第三方Bean
加载Properties文件
步骤如下:
- 开启Context命名空间
- 使用context加载properties文件
- 使用属性占位符${}读取properties配置文件中的属性
样例代码如下:
<?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 "> <context:property-placeholder location="xxxx.properties"/>
<bean class="xxxx"> <property name="xxxx" value="${ 配置中的变量名称 }"/> </bean> </beans>
|
注意:加载时可以关闭系统属性,因为系统属性的优先级高于
注意:多个配置文件用逗号隔开
注意:专业的写法加载所有配置文件如下
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
|
总结(来自黑马程序员,侵删)
容器相关
bean相关
依赖注入相关