Bean的实例化
(与service层无关,换句话 与DI无关,主要讲解IoC创建bean的原理)
Spring创建bean时调用的是无参构造方法,而私有方法能被调用 因为底层用了反射。用构造方法来实例化对象。
对于Spring的报错,从下往上看
1 Bean是如何创建的【理解】
bean本质上就是对象,创建bean使用构造方法完成
2 实例化Bean的三种方式
2.1 构造方法方式【重点】
- BookDaoImpl实现类
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}
- applicationContext.xml配置
<!--方式一:构造方法实例化bean-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
- AppForInstanceBook测试类
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
- 运行结果
注意:**无参构造方法如果不存在,将抛出异常BeanCreationException
**
2.2 静态工厂方式实例化bean【了解】
我们需要的是工厂里面的OrderDao对象===>配置工厂类名和方法名
- OrderDao接口和OrderDaoImpl实现类
public interface OrderDao {
public void save();
}
public class OrderDaoImpl implements OrderDao {
public void save() {
System.out.println("order dao save ...");
}
}
- OrderDaoFatory工厂类
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("static factory setup....");
return new OrderDaoImpl();
}
}
- applicationContext.xml配置
<!--方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
- AppForInstanceOrder测试类
public class AppForInstanceOrder {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}
- 运行结果
2.3 实例工厂方式【了解】
和静态工厂类似,多一个配置步骤 factory-bean="userFactory
- UserDao接口和UserDaoImpl实现类
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
- UserDaoFactory工厂类
//实例工厂创建对象
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
- applicationContext.xml配置
<!--方式三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
- AppForInstanceUser测试类
public class AppForInstanceUser {
public static void main(String[] args) {
// //创建实例工厂对象
// UserDaoFactory userDaoFactory = new UserDaoFactory();
// //通过实例工厂对象创建对象
// UserDao userDao = userDaoFactory.getUserDao();
// userDao.save();
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
}
- 运行结果
2.4 实现FactoryBean<T>方式【扩展,了解】
- 定义UserDaoFactoryBean实现FactoryBean<UserDao>
UserDaoFactoryBean中实例化什么类型的对象泛型就是该类型。
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
- applicationContext.xml配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
使用之前的AppForInstanceUser测试类去运行看结果就行了。注意配置文件中id="userDao"是否重复。
代码
package com.itheima;
import com.itheima.dao.OrderDao;
import com.itheima.dao.UserDao;
import com.itheima.factory.OrderDaoFactory;
import com.itheima.factory.UserDaoFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForInstanceOrder {
//使用工厂创建对象
public static void main(String[] args) {
//方法二 静态工厂
//造对象不要直接new 用工厂的方式做适当的解耦
/* OrderDao orderDao = OrderDaoFactory.getOrderDao();
orderDao.save();*/
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao =(OrderDao) ctx.getBean("orderDao");
orderDao.save();
/* //方法三 实例工厂 先找到工厂的对象,再用对象调用方法
UserDaoFactory userDaoFactory = new UserDaoFactory();
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();*/
//Spring 的形式来运行
/* ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao =(UserDao) ctx.getBean("userDao");
userDao.save();*/
}
}
<?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">
<!--1.导入Spring坐标spring-context,对应的版本号是5.2.10.RELEASE -->
<!--2.配置bean id随便起-->
<!--方式一 构造方法实例化bean 构造方法默认时无参构造,写不写均可-->
<!--使用IoC 和 DI -->
<!-- <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!–7.配置server与dao的关系 把dao放到server中–>
<!–property代表当前bean的属性 name中 代表BookServiceImpl private BookDao bookDao–>
<!–关系绑定–>
<property name="bookDao" ref="bookDao" />
</bean>-->
<!--方式二:静态工厂实例化bean-->
<!--这样造出来的对象时factory对象而不是到对象 需要进一步进缩小范围(factory中的dao对象)-getOrderDao() -->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
<!--方式三:实例工厂实例化bean -->
<!--工厂的bean造出来 -->
<!-- <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<!–静态工厂直接写class类名,实例化工厂不写class –>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>-->
<!--方式三的变种 去掉了 <bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/> 的臃肿-->
<!--方式四:使用FactoryBean实例bean -->
<!--id给个名称,class写定义新的UserFactoryBean 造非单例对象时,接口方法isSingleton false -->
<!--<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>-->
</beans>
依赖注入(DI配置)
1 依赖注入方式【重点】
1.1 依赖注入的两种方式
- setter注入
- 简单类型
- 引用类型(很常用)
- 构造方法注入
1.2 setter方式注入
问题导入
setter方式注入使用什么子标签?
引用类型
- 标签中:使用ref
- 完整代码:
public class BookServiceImpl implements BookService{
private BookDao bookDao;
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
<!--注入引用类型-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
</bean>
- 测试
public class AppForDISet {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
简单类型
- 简单类型包含:基本类型(int, char, long…)+ 特殊类型String
- 标签中:使用value
- 完整代码
public class BookDaoImpl implements BookDao {
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+connectionNum);
}
}
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
</bean>
总结
#简单类型使用value,引用类型使用ref
1.3 构造方式注入
问题导入
构造方式注入使用什么子标签?
引用类型
- 完整代码
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
- 测试
public class AppForDIConstructor {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
简单类型
- 完整代码
public class BookDaoImpl implements BookDao {
private int connectionNum;
private String databaseName;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数名称注入-->
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
参数适配【了解】
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数名称注入-->
<!--<constructor-arg name="connectionNum" value="10"/>-->
<!--<constructor-arg name="databaseName" value="mysql"/>-->
<!--根据构造方法参数类型注入-->
<!--<constructor-arg type="int" value="10"/>-->
<!--<constructor-arg type="java.lang.String" value="mysql"/>-->
<!--根据构造方法参数位置注入-->
<!--<constructor-arg index="0" value="mysql"/>-->
<!--<constructor-arg index="1" value="100"/>-->
</bean>
1.4 依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
代码
BookDaoImpl
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
private int connectionNum;
private String databaseName;
//注入一:set注入
/* public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}*/
//注入二:构造器注入
public BookDaoImpl(int connectionNum, String databaseName) {
this.connectionNum = connectionNum;
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao..."+ databaseName+","+connectionNum);
}
}
BookServiceImpl
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.dao.UserDao;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
//注入方法一:set注入
/* public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}*/
//注入方法二:构造器注入
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service...");
bookDao.save();
userDao.save();
}
}
AppForDISet
package com.itheima;
import com.itheima.service.BookService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForDISet {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
ctx.close();
}
}
applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--注入一:set注入 -->
<!-- <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" >
<property name="connectionNum" value="10"/>
<property name="databaseName" value="mySql"/>
</bean>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao" />
</bean>-->
<!--注入二:构造器注入 标准书写 推荐使用。其他方法会存在问题-->
<!-- <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="MySql"/>
</bean>
<bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>-->
<!--注入二-1:进一步解耦,消除参数捆绑 与形参名不耦合了-->
<!-- <bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="MySql"/>
</bean>
<bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>-->
<!--注入二-2:参数位置注入,消除二-1出现两个同类型的数据-->
<bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="MySql"/>
</bean>
<bean name="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
2 依赖自动装配【理解】
问题导入
如何配置按照类型自动装配?
2.1 自动装配概念
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
- 自动装配方式
按类型(常用)
按名称:按构造方法不启用自动装配
2.2 自动装配类型
依赖自动装配
配置中使用bean标签autowire属性设置自动装配的类型
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
依赖自动装配特征
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
<?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 name="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<bean name="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>-->
<!--自动注入 -->
<bean name="bookDao" class="com.itheima.dao.impl.BookDaoImpl" />
<!--那边无论写了多少,set方法给够,这边一个autowire 结束-->
<bean name="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
3 集合注入-07’32
准备工作
在类中添加集合类型属性:添加get,set方法
public class BookDaoImpl implements BookDao {
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);
}
}
3.1 注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
3.2 注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
3.3 注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
3.4 注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
3.5 注入Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
说明:property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签
3.6 验证结果
public class AppForDICollection {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}