(四)依赖注入
依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于Spring容器
- 注入 : 指Bean对象中的属性 , 由容器来设置和装配
注入的三种方式:
构造器注入(有参无参)、setter注入、注解注入(自动装配)
1、构造器注入
默认情况下都是通过无参构造器注入,因为User类中存在隐式的无参构造方法,容器配置如下
<bean id="user" class="com.alan.pojo.User"> <property name="name" value="name from bean"/> </bean>
而当User类中重载了有参构造方法时,则必须通过有参构造方法注入(同c命名空间注入) 。如下所示
User类
public class User{ private String name; public User(String name){ this.name = name; } }
spring容器配置
<!-- 第一种根据index参数下标设置 --> <bean id="user" class="com.alan.pojo.User"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="alan"/> </bean> <!-- 第二种根据参数名字设置 --> <bean id="user" class="com.alan.pojo.User"> <!-- name指参数名 --> <constructor-arg name="name" value="alan"/> </bean> <!-- 第三种根据参数类型设置 --> <bean id="user" class="com.alan.pojo.User"> <constructor-arg type="java.lang.String" value="alan"/> </bean>
引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="userp" class="com.alan.pojo.User" p:name="alan" p:age="18"/>
2、setter注入*
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is
set示例:
public class UserServiceImpl implements UserService { private UserDao userDao; // 利用set实现 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
新建student类,并生成相应set方法
private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info;
对于不同类型,在xml中的注入
<bean id="addr" class="com.alan.pojo.Address"> <property name="address" value="上海"/> </bean> <bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> <property name="address" ref="addr"/><!-- bean注入--> <property name="books"> <array> <value>西游记</value> <value>红楼梦</value> <value>水浒传</value> </array> </property> <property name="hobbys"> <list> <value>听歌</value> <value>看电影</value> <value>爬山</value> </list> </property> <property name="card"> <map> <entry key="中国邮政" value="456456456465456"/> <entry key="建设" value="1456682255511"/> </map> </property> <property name="games"> <set> <value>LOL</value> <value>BOB</value> <value>COC</value> </set> </property> <property name="wife"><null/></property> <property name="info"> <props> <prop key="学号">20190604</prop> <prop key="性别">男</prop> <prop key="姓名">小明</prop> </props> </property> </bean>
3、自动装配、注解注入
自动装配
(1) 按名称的自动装配
<bean id="user" class="com.alan.pojo.User" autowire="byName"> <property name="name" value="alan"/> </bean>
小结:
当一个bean节点带有 autowire byName的属性时。
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
(2)按类型的自动装配
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
测试:
将user的bean配置修改一下 : autowire="byType",并添加一个相同类型的bean
<bean id="dog" class="com.alan.pojo.Dog"/> <bean id="cat" class="com.alan.pojo.Cat"/> <bean id="cat2" class="com.alan.pojo.Cat"/> <bean id="user" class="com.alan.pojo.User" autowire="byType"> <property name="str" value="alan"/> </bean>
此时报错:NoUniqueBeanDefinitionException
删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
注解注入
注解注入需要对容器进行一些配置
引入相关约束文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
开启注解支持
<context:annotation-config/>
@Autowired
- @Autowired是按类型自动转配的,不支持id匹配。
- 需要导入 spring-aop的包!
将User类中的set方法去掉,使用@Autowired注解
public class User { @Autowired private Cat cat; @Autowired private Dog dog; private String str; public Cat getCat() { return cat; } public Dog getDog() { return dog; } public String getStr() { return str; } }
此时配置文件内容
<context:annotation-config/> <bean id="dog" class="com.alan.pojo.Dog"/> <bean id="cat" class="com.alan.pojo.Cat"/> <bean id="user" class="com.alan.pojo.User"/>
@Autowired(required=false) 说明:
false,对象可以为null;true,对象必须存对象,不能为null。默认为true。
@Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用。
配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!
<bean id="dog1" class="com.alan.pojo.Dog"/> <bean id="dog2" class="com.alan.pojo.Dog"/> <bean id="cat1" class="com.alan.pojo.Cat"/> <bean id="cat2" class="com.alan.pojo.Cat"/>
没有加Qualifier测试,直接报错,在属性上添加Qualifier注解
@Autowired @Qualifier(value = "cat2") private Cat cat; @Autowired @Qualifier(value = "dog2") private Dog dog;
@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
实体类User
public class User { //如果允许对象为null,设置required = false,默认为true @Resource(name = "cat2") private Cat cat; @Resource private Dog dog; private String str; }
容器配置文件
<bean id="dog" class="com.alan.pojo.Dog"/> <bean id="cat1" class="com.alan.pojo.Cat"/> <bean id="cat2" class="com.alan.pojo.Cat"/> <bean id="user" class="com.alan.pojo.User"/>
测试成功
删除cat2
<bean id="dog" class="com.kuang.pojo.Dog"/> <bean id="cat1" class="com.kuang.pojo.Cat"/>
User中不指定name
@Resource private Cat cat; @Resource private Dog dog;
测试成功
结论:先进行byName查找,失败;再进行byType查找
小结:
@Autowired 根据类型自动装配,效果同byType,如果这些类的类型都相同,此时就需要使用@Qualifier明确指定使用哪个类,
@Autowired和@Qualifier(value=“xx”)一起使用 则根据指定的名称自动装配,效果同byName
@Resource,可以通过类型也可以通过名称自动装配,如果指定名称,则优先通过指定的名称装配,默认通过byName装配,再通过byType装配