spring框架的两个重要功能就是IOC控制反转 和属性注入。这里要说一下,控制反转也就是 将创建对象的任务交给spring来完成,这种功能有两种实现方式,一是通过xml配置bean标签的方式,一种是通过扫描包(注解)的方式。
然而在用两种方式的过程中,难免会有疑惑,可不可以同一个bean配置不同的标识呢?@Autowired注解和@Resource注解有什么区别呢?还有其他的一些疑问,我都做了一个小小的验证。
下面我们来看一下这些验证。
一.所需要的代码部分
1.UserDaoImpl代码
package com.java.DaoImpl;
import org.springframework.stereotype.Repository;
import com.java.Dao.UserDao;
@Repository
public class UserDaoImpl2 implements UserDao {
@Override
public void save() {
System.out.println("UserDaoImpl2 中的save()方法被调用");
}
}
2.UserServiceImpl代码
package com.java.serviceImpl;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import com.java.Dao.UserDao;
import com.java.DaoImpl.UserDaoImpl2;
import com.java.service.UserService;
public class UserServiceImpl2 implements UserService{
//用接口来接收实现类
//@Autowired
@Resource
private UserDao userDao;
@Autowired
private UserDaoImpl2 userDaoImpl2;
@Override
public void text() {
System.out.println("UserServiceImpl2 中的text() 方法被执行");
System.out.println(userDao);
System.out.println(userDaoImpl2);
}
}
3.applicationContext代码
<?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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd"
>
<!-- 验证Autowired注解和Resource注解的差别 -->
<!-- 配置UserDaoImpl,是该类交给spring管理,
创建该类的bean 并放到IOC容器中。
-->
<bean id="userServiceImpl2" class="com.java.serviceImpl.UserServiceImpl2">
<!--
<property name="userDaoImpl2" ref="userDaoImpl2"></property>
<property name="userDao" ref="userDaoImpl2"></property>
-->
</bean>
<!--
<bean id="userDao" class="com.java.DaoImpl.UserDaoImpl2">
</bean>
-->
<bean id="userDao1" class="com.java.DaoImpl.UserDaoImpl2">
</bean>
<!-- 用扫描包 的形式取代xml的形式,来将UserDaoImpl2来注入到容器中
<context:component-scan base-package="com.java.DaoImpl" use-default-filters="true">
</context:component-scan>
-->
</beans>
4.测试代码
package com.java.text;
import javax.annotation.Resource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java.Dao.UserDao;
import com.java.DaoImpl.UserDaoImpl2;
import com.java.serviceImpl.UserServiceImpl2;
//测试Autowired注解和Resource注解的区别
public class Text2 {
//读取(加载)spring的核心配置文件,为成员变量进行初始化
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext2.xml");
public static void main(String[] args) {
Text2 text2= new Text2();
text2.text();
}
public void text() {
UserServiceImpl2 userServiceImpl2 = applicationContext.getBean(UserServiceImpl2.class);
System.out.println(applicationContext.getBean("userServiceImpl2"));
userServiceImpl2.text();
System.out.println((UserDaoImpl2)applicationContext.getBean("userDao1"));
}
}
/*如果在xml文件中,没有定义一个bean,或者定义了一个bean,但是没有为其类中的对象属性
*赋值,那么这个属性就为空。
*在用xml配置bean的时候,要给这个属性提供set方法,否则对象属性不会被注入进去,会出现
*异常。
*如果用注解方式配置bean,会使拥有注解的类直接变成bean放入IOC容器中。默认的getBea
*n的参数名为类的首字母小写。并且,即使属性没有set函数,那么也可以将对象注入进去。
*再用@Autowired注解进行属性注入时,注解所修饰的属性类型一定要在IOC容器中存在,否则
*会出现类型找不到的异常。
*如果用@Resource注解时,注解所修饰的属性类型如果在IOC容器中不存在,并且属性名也不再
*容器中,那么就会出现类型找不到的异常。
*在ioc容器中的类型bean,一个类型只能由一个索引,也就是ID值,否则会出现No qualify
*ing bean of type 'com.java.DaoImpl.UserDaoImpl2' available:
* expected single matching bean but found 2: userDao,UserDao
* Impl2 这个异常。
*注解修饰的属性,如果想要注入属性,那么只能是通过扫包方式将bean放进IOC容器中的,用<bean>
*标签的方式使bean放进IOC容器中的对象,不能通过注解注入进对象。
*可以通过分号,或逗号的方式,来扫描多个包。
*用@Autowired注解时,如果一个注解修饰了一个父类或接口,如果IOC容器中用子类类型,那么就
*会将子类类型对象给接口或父类类型。但是如果又两个实现类类型,会出现接口不知道用哪个实现类的异常。
*这里并不需要扫描接口所在的包,只需要扫实现类的包即可。
*用@Resource注解时,是先找名称,如果ID标识没有找到,再去找类型,但是如果这个时候满足的类型
*比较多,会发生异常。
* */
二.总结
1)如果在xml文件中,没有为一个类定义一个bean,或者定义了一个bean,但是没有为其类中的对象属性赋值(进行关联),那么访问这个类的这个属性时就为空。
2)在用xml方式配置bean的时候,要给这个属性提供set方法,否则对象属性是不会被注入进去的,会出现要我们提供setter的异常。
3)如果用扫描包的方式为类配置bean,会使被注解修饰的类直接变成bean放入IOC容器中。默认的getBean()的参数名为类的首字母小写。并且,即使属性没有set函数,那么也可以将对象注入进去。用@Autowired注解进行属性注入时,注解所修饰的属性类型一定要在IOC容器中存在(实现类类型存在也可以),否则会出现类型找不到的异常。
4)通过bean标签的方式将bean放到IOC容器时,并不能是通过注解的方式将对象注入到属性中,只有通过扫描包的方式配置bean的时候才可以将IOC中的bean注入到注解修饰的属性中。
5)如果用@Resource注解时,JVM会先通过属性名来去IOC容器中找bean,如果没有找到则在通过属性类型来寻找(包括实现类类型)找到了就将bean注入到属性中。@Resource注解所修饰的属性类型如果在IOC容器中不存在,并且属性名也不再容器中,那么就会出现类型找不到的异常。
6)在通过bean标签的方式配置到容器中的类型bean,一个类型只能yo一个有,也就是ID值,在getBean()的时候通过ID值来获取。但是通过否则会出现No qualifying bean of type com.java.DaoImpl.UserDaoImpl2' available: expected single matching bean but found 2: userDao,UserDao Impl2 这个异常。
注意:这个 7) 是一个重点!!!!!
7)对于所谓的单例和多例对象,只是对于在IOC容器的一个bean而言,但是一旦类型一样,但是id标识不一样,那么就认为这几个bean不同。所以在通过 id标识进行getBean()时,它们是不同的对象,这是可以验证的。这里用@Autowired时,由于@Autowired注解是通过类型来进行查找的,所以一旦IOC容器中有相同的类型bean(无论是通过xml配置的bean,还是通过扫描包配置的bean,还是两者混杂),那么就会发生异常。但是@Resource就不发生异常,这是因为@Resource首先是按属性名进行查找的,所以就算有相同的类型bean,只要属性名有对应的id标识,那么就会将属性注入进去。总的来说,就是@Autowired注解只按类型来查找,而@Resource注解是先按属性名来进行查找,如果查找不到,在用类型进行查找。
8)在扫描包时,可以通过分号,或逗号的方式,来扫描多个包。
9)用@Autowired或者@Resource注解时,如果一个注解修饰了接口,如果IOC容器中用实现类类型,那么就会将实现类类型对象给接口类型。但是如果有两个实现类类型,会出现接口不知道用哪个实现类的异常(这里@Autowired会发生异常,而@Resource却不会,原因如第7条)。这里并不需要扫描接口所在的包,只需要扫实现类的包即可(因为在实现类中已经和接口有联系了)。
10)在用注解方式注入时,我们可以指定类被getBean()时的别名,和类是否是单例或者多例。
Over----------------------------------------------