三、Spring DI
3.1 什么是依赖注入
依赖注入(Dependency Injection,简称DI),它是Spring控制反转思想的具体实现。
控制反转将对象的创建交给了Spring,但是对象中可能会依赖其他对象。比如service类中要有dao类的属性,我们称service依赖于dao。之前需要手动注入属性值,代码如下:
public interface StudentDao { Student findById(int id); } public class StudentDaoImpl implements StudentDao{ @Override public Student findById(int id) { // 模拟根据id查询学生 return new Student(1,"张三","北京"); } } public class StudentService { // service依赖dao,手动注入属性值,即手动维护依赖关系 private StudentDao studentDao = new StudentDaoImpl(); public Student findStudentById(int id){ return studentDao.findById(id); } }
此时,当StudentService的想要使用StudentDao的另一个实现类如StudentDaoImpl2时,则需要修改Java源码,造成代码的可维护性降低。
而使用Spring框架后,Spring管理Service对象与Dao对象,此时它能够为Service对象注入依赖的Dao属性值。这就是Spring的依赖注入。简单来说,控制反转是创建对象,依赖注入是为对象的属性赋值。
3.2 依赖注入方式
Setter注入
在之前开发中,可以通过setter方法或构造方法设置对象属性值:
// setter方法设置属性 StudentService studentService = new StudentService(); StudentDao studentDao = new StudentDao(); studentService.setStudentDao(studentDao); // 构造方法设置属性 StudentDao studentDao = new StudentDao(); StudentService studentService = new StudentService(studentDao);
Spring可以通过调用setter方法或构造方法给属性赋值:
1、被注入类编写属性的setter方法
public class StudentService { private StudentDao studentDao; public void setStudentDao(StudentDao studentDao) { this.studentDao = studentDao; } }
2、配置文件中,给需要注入属性值的<bean>
中设置<property>
<?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="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentService" class="com.zj.service.StudentServiceImpl"> <!--依赖注入。name:属性名,ref:bean的id--> <property name="studentDao" ref="studentDaoImpl"/> </bean> </beans>
3、测试是否注入成功
StudentServiceImpl studentService = context.getBean("studentService", StudentServiceImpl.class); StudentDao studentDao = studentService.getStudentDao();
构造方法依赖注入
1、被注入类编写有参的构造方法
package com.zj.service; import com.zj.dao.StudentDao; public class StudentServiceImpl implements StudentService { private StudentDao studentDao; public StudentServiceImpl(StudentDao studentDao) { this.studentDao = studentDao; } }
2、给需要注入属性值的<bean>
中设置<constructor-arg>
<bean id="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentService" class="com.zj.service.StudentServiceImpl"> <!--构造方法实现依赖注入--> <constructor-arg name="studentDao" ref="studentDaoImpl"/> </bean>
自动注入
自动注入不需要在<bean>
标签中添加其他标签注入属性值,而是自动从容器中找到相应的bean对象设置为属性值。
自动注入有两种配置方式:
- 全局配置:在
<beans>
中设置default-autowire
属性可以定义所有bean对象的自动注入策略。一般很少用了解即可。 - 局部配置:在
<bean>
中设置autowire
属性可以定义当前bean对象的自动注入策略。
autowire的取值如下:
- no:不会进行自动注入。
- default:全局配置default相当于no,局部配置default表示使用全局配置
- byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
- byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
- constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。
byType
1、为依赖的对象创建set/get方法
package com.zj.service; import com.zj.dao.StudentDao; import com.zj.dao.StudentDaoImpl; public class StudentServiceImpl implements StudentService { /*成员变量*/ private StudentDao studentDao; /*set*/ public void setStudentDao(StudentDao studentDao) { this.studentDao = studentDao; } /*get*/ public StudentDao getStudentDao() { return studentDao; } /*带参构造方法*/ public StudentServiceImpl(int a){ } }
2、在配置文件中配置根据类型自动注入
<!-- 根据bean类型等于属性类型自动注入 --> <bean id="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentServiceImpl" class="com.zj.service.StudentServiceImpl" autowire="byType"/>
3、测试
@Test public void test1(){ //创建spring容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //获取StudentServiceImpl StudentServiceImpl studentServiceImpl = context.getBean("studentServiceImpl", StudentServiceImpl.class); //获取studentServiceImpl中的StudentDaoImpl属性 StudentDao studentDao = studentServiceImpl.getStudentDao(); //调用StudentDaoImpl中的方法 studentDao.m1(); }
byName
<!-- 根据bean名称等于属性类型自动注入 --> <bean id="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentServiceImpl" class="com.zj.service.StudentServiceImpl" autowire="byName"/>
constructor
<!-- 利用构造方法自动注入--> <bean id="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentServiceImpl" class="com.zj.service.StudentServiceImpl" autowire="constructor"/>
注入bean、基本数据类型、字符串
1、准备注入属性的类
public class StudentService { private StudentDao studentDao; // bean属性 private String name; //字符串类型 private int count; //基本数据类型 private List<String> names; // 字符串类型List集合 private List<Student> students1; // 对象类型List集合 private Set<Student> students2; // 对象类型Set集合 private Map<String,String> names2; // 字符串类型Map集合 private Map<String,Student> students3; // 对象类型Map集合 private Properties properties; //Properties类型 // 省略getter/setter/toString }
2、注入bean类型、基本数据类型
<?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="studentDaoImpl" class="com.zj.dao.StudentDaoImpl"/> <bean id="studentServiceImpl" class="com.zj.service.StudentServiceImpl"> <!--注入bean类型的数据--> <property name="studentDao" ref="studentDaoImpl"/> <!--注入基本数据类型--> <property name="count" value="10"/> <!--注入字符串类型的数据--> <property name="name" value="张三"/> <!--注入简单类型(String、Integer……)的list集合--> <property name="names"> <list> <value>李四</value> <value>王五</value> </list> </property> <!--注入对象类型的list集合--> <property name="students1"> <list> <bean class="com.zj.pojo.Student"> <property name="id" value="1"/> <property name="name" value="苏问夏"/> <property name="address" value="甘肃"/> </bean> <bean class="com.zj.pojo.Student"> <property name="id" value="2"/> <property name="name" value="苍映天"/> <property name="address" value="新疆"/> </bean> </list> </property> <!--注入对象类型的对象类型--> <property name="students2"> <set> <bean class="com.zj.pojo.Student"> <property name="id" value="1"/> <property name="name" value="郜梓童"/> <property name="address" value="杭州"/> </bean> <bean class="com.zj.pojo.Student"> <property name="id" value="2"/> <property name="name" value="董孟阳"/> <property name="address" value="福州"/> </bean> </set> </property> <!--注入简单类型的Map--> <property name="names2"> <map> <entry key="1" value="张姣姣"/> <entry key="2" value="林韶仪"/> </map> </property> <!--注入对象类型的map--> <property name="students3"> <map> <entry key="1" value-ref="student1"/> <entry key="2" value-ref="student2"/> </map> </property> <!--注入properties类型的对象--> <property name="properties"> <props> <prop key="配置1">值1</prop> <prop key="配置2">值2</prop> </props> </property> </bean> <bean id="student1" class="com.zj.pojo.Student"> <property name="id" value="1"/> <property name="name" value="段宵月"/> <property name="address" value="廊坊"/> </bean> <bean id="student2" class="com.zj.pojo.Student"> <property name="id" value="2"/> <property name="name" value="富好慕"/> <property name="address" value="邢台"/> </bean> </beans>
3、测试
@Test public void test1(){ //创建spring容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //获取StudentServiceImpl StudentServiceImpl studentServiceImpl = context.getBean("studentServiceImpl", StudentServiceImpl.class); //获取studentServiceImpl中的StudentDaoImpl属性 StudentDao studentDao = studentServiceImpl.getStudentDao(); //调用StudentDaoImpl中的方法 studentDao.m1(); System.out.println(studentServiceImpl); }
StudentDaoImpl!!! StudentService{studentDao=com.zj.dao.StudentDaoImpl@15bfd87, name='张三', count=10, names=[李四, 王五], students1=[Student{id=1, name='苏问夏', address='甘肃'}, Student{id=2, name='苍映天', address='新疆'}], students2=[Student{id=1, name='郜梓童', address='杭州'}, Student{id=2, name='董孟阳', address='福州'}], names2={1=张姣姣, 2=林韶仪}, students3={1=Student{id=1, name='段宵月', address='廊坊'}, 2=Student{id=2, name='富好慕', address='邢台'}}, properties={配置2=值2, 配置1=值1}}
四、注解实现IOC
4.1 准备工作
1、创建一个新的maven项目。
2、编写pojo,dao,service类。
3、编写空的配置文件,如果想让该文件支持注解,需要添加新的约束:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
4.2 @Component
作用:用于创建对象,放入Spring容器,相当于<bean id="" class="">
位置:类上方
注意:
- 要在配置文件中配置扫描的包,扫描到该注解才能生效。
<context:component-scan base-package="com.zj"/>
@Component
注解配置bean的默认id是首字母小写的类名。也可以手动设置bean的id值。
// 此时bean的id为studentDaoImpl @Component public class StudentDaoImpl implements StudentDao { public void m1(){ System.out.println("StudentDaoImpl!!!"); } } // 此时bean的id为studentDao @Component("studentDao") public class StudentDaoImpl implements StudentDao{ public void m1(){ System.out.println("StudentDaoImpl!!!"); } }
4.3 @Repository、@Service、@Controller、@Scope
作用:这三个注解和@Component的作用一样,使用它们是为了区分该类属于什么层。
位置:
- @Repository用于Dao层
- @Service用于Service层
- @Controller用于Controller层
@Repository public class StudentDaoImpl implements StudentDao{} @Service public class StudentService {}
- @Scope
作用:指定bean的创建策略
位置:类上方
取值:singleton prototype request session globalsession
@Service @Scope("singleton") public class StudentService {}
4.4 @Autowired
作用:从容器中查找符合属性类型的对象自动注入属性中。用于代替<bean>
中的依赖注入配置。
位置:属性上方、setter方法上方、构造方法上方。
注意:
1、@Autowired
写在属性上方进行依赖注入时,可以省略setter方法
@Service @Scope("singleton") public class StudentServiceImpl implements StudentService { @Autowired private StudentDao studentDao; public StudentDao getStudentDao() { return studentDao; } public void m1(){ studentDao.m1(); } }
2、容器中没有对应类型的对象会报错
3、容器中有多个对象匹配类型时,会找beanId等于属性名的对象,找不到会报错。
4.5 @Qualifier
作用:在按照类型注入对象的基础上,再按照bean的id注入。
位置:属性上方
注意:@Qualifier必须和@Autowired一起使用。
@Component public class StudentService { @Autowired @Qualifier("studentDaoImpl2") private StudentDao studentDao; public Student findStudentById(int id){ return studentDao.findById(id); } }
4.6 @Value
作用:注入String类型和基本数据类型的属性值。
位置:属性上方
用法:
1、直接设置固定的属性值
@Service public class StudentService { @Value("1") private int count; @Value("hello") private String str; }
2、获取配置文件中的属性值:
- 编写配置文件db.properties
jdbc.username=root jdbc.password=123456
- spring核心配置文件扫描配置文件
<context:property-placeholder location="db.properties"/>
- 注入配置文件中的属性值
@Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password;
4.7 @Configuration、@ComponentScan
此时基于注解的IOC配置已经完成,但是我们依然离不开Spring的xml配置文件。接下来我们脱离bean.xml,使用纯注解实现IOC。
在真实开发中,我们一般还是会保留xml配置文件,很多情况下使用配置文件更加方便。
注解实现IOC需要一个Java类代替xml文件。这个Java类上方需要添加@Configuration,表示该类是一个配置类,作用是代替配置文件。
@Configuration /*表示该类是一个配置类*/ @ComponentScan("com.zj") /*扫描包*/ public class SpringConfig { }
@Test public void test1(){ //创建spring容器 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //获取容器中的对象 StudentServiceImpl studentServiceImpl = context.getBean("studentServiceImpl", StudentServiceImpl.class); StudentDao studentDao = studentServiceImpl.getStudentDao(); studentDao.m1(); }
4.8 @PropertySource
作用:代替配置文件中的<context:property-placeholder>
扫描配置文件
位置:配置类上方
注意:配置文件位置前要加关键字classpath
@Configuration @PropertySource("classpath:db.properties") public class JdbcConfig { @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; }