接上回。。。
依赖注入(DI配置)
依赖注入的两种方式
- setter注入
简单类型
引用类型(很常用) - 构造器注入
简单类型
引用类型
setter方式注入
引用类型:
- 在bean中定义引用类型属性并提供可访问的set方法
- 在
Application.xml
中使用property
标签ref
属性注入引用类型
详情见Spring框架(一)的DI入门案例
简单类型:
- 在bean中定义引用类型属性并提供可访问的set方法
- 在
Application.xml
中使用property
标签value
属性注入简单数据类型
代码演示:
- 新添加UserDao接口和BookDao一样
- BookDaoImpl提供要注入对象的set方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
- 配置文件注入简单数据类型
<?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="bookDao" class="com.yang.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.yang.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
- App主函数实现
public class AppForDISet {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
运行结果:
构造器注入
构造器注入不需要set方法
生成构造方法,与setter方式注入相比,只是换个方法,还是参数往里传
- BookServiceImpl添加构造方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
- 同理BookDaoImpl建立构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
@Override
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
- ApplicationContext使用
constructor-arg
标签
<?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">
<!-- 此方法使形参名不耦合,改形参名不影响type
<bean id = "bookDao" class = "com.yang.dao.Impl.BookDaoImpl">-->
<!-- <constructor-arg type="int" value="100"/>-->
<!-- <constructor-arg type="java.lang.String" value="mysql"/>-->
<!-- </bean>-->
<!-- <bean id = "bookService" class = "com.yang.service.Impl.BookServiceImpl">-->
<!-- <constructor-arg name="bookDao" ref="bookDao"/>-->
<!-- <constructor-arg name="userDao" ref="userDao"/>-->
<!-- </bean>-->
<!-- <bean id="userDao" class="com.yang.dao.Impl.UserDaoImpl"/>-->
<!--解决参数类型重复问题,使用位置解决参数匹配-->
<bean id="bookDao" class="com.yang.dao.Impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
<bean id="userDao" class="com.yang.dao.Impl.UserDaoImpl"/>
<bean id="bookService" class="com.yang.service.Impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans>
- App类不变
依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
依赖自动装配
自动装配概念
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
- 自动装配方式
按类型(常用)
按名称
按构造方法
不启用自动装配
自动装配类型
依赖自动装配
配置中使用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注入与构造器注入,同时出现时自动装配配置失效
集合注入
注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
注入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>标签
注解开发
注解相当于一个接口,程序可以通过反射来获取指定程序元素的注解对象。
注解并不影响程序正常运行。
在Spring中,xml配置Bean对象有些繁琐,可以使用注解简化Bean对象的定义:在类或者方法上加入特定的注解(@XXX),完成特定功能的开发。
通过注解的方式,在功能的调用者和提供者之间达成约定,进而进行功能的调用。因为注解更为方便灵活,所以在现实的开发中,更推荐使用注解的形式完成
代码演示:
- 在类上使用@Component注解定义Bean。
@Component("bookDao") // 这里类似配置了一个bean,name叫“bookDao”
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save....");
}
}
//同理在BookServiceImpl一样定义注解
@Component
public class BookServiceImpl implements ServiceDao {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
- 注解定义后,得让Spring知道有这个注解,就得在xml的配置文件添加,让其扫描这个目录
<context:component-scan base-package="com.yang"/>
- 在测试类中获取bean
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao"); //按id寻找bean
System.out.println(bookDao);
ServiceDao bookService = ctx.getBean(ServiceDao.class); //按类型寻找bean,所以BookServiceImpl类里没有id
System.out.println(bookService);
}
}
- 结果
能够看到两个bean的地址,成功。
在测试类中不要调用bookService的save方法,因为还没有给ServiceImpl中的bookDao赋值(在setter方法当中),调用bookService的save方法会出现空指针异常。
![]()
@Component三个衍生注解
都是Component
注解使得开发难以看懂,所以衍生了三个注解。他们和Component
一样,只是换个名字
@Controller
:用于表现层bean定义@Service
:用于业务层bean定义@Repository
:用于数据层bean定义
纯注解开发模式
没有xml配置文件全是注解。
- Spring3.0开启了纯注解开发模式,使用Java类替代配置文件
- Java类代替Spring核心配置文件
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
代码演示:
- 定义配置类代替配置文件
//声明当前类为Spring配置类
@Configuration
//Spring注解扫描,相当于<context:component-scan base-package="com.yang"/>
@ComponentScan("com.yang")
//设置bean扫描路径,多个路径书写为字符串数组格式
//@ComponentScan({"com.yang.service","com.yang.dao"})
public class SpringConfig {
}
- 在APP测试类中加载配置类,获取Bean对象并使用
public class AppForAnnotation {
public static void main(String[] args) {
//AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
//按类型获取bean
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
未完待续。。。