(II)第十四节:使用@Autowired 注解自动装配

一、分析

        Controller 组件中往往需要用到 Service 组件的实例,Service 组件往往需要用到 Repository 组件的实例。
        既然通过注解可以标识一个 Spring 组件,那么Spring 也应该可以通过注解的方式帮我们实现属性的装配。

二、实现依据

        在指定要扫描的包时,<context:component-scan> 元素会自动注册一个 bean 的后置处理器:AutowiredAnnotationBeanPostProcessor 的实例。该后置处理器可以自动装配标记了 @Autowired、@Resource 或 @Inject 注解的属性。
 

三、@Autowired 注解使用

  applicationContext.xml

<context:component-scan base-package="com.achang"></context:component-scan>

  BookDao

@Repository
public class BookDao {
    public void saveBook() {
        System.out.println("保存了一本书");
    }
}

  BookService

@Service
public class BookService {
    @Autowired
    private BookDao bookDao;

    public void save(){
        System.out.println("正在调用Dao保存图书。。。。");
        bookDao.saveBook();
    }
}

  BookServlet

@Controller
public class BookServlet {
    //自动装配:自动为这个属性赋值
    @Autowired
    private BookService bookService;
    
    public void doGet(){
        bookService.save();
    }
}

  测试:

public class IOCTest {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    @Test
    public void test2(){
        BookServlet bookServlet = (BookServlet) ioc.getBean("bookServlet");
        bookServlet.doGet();
        //正在调用Dao保存图书。。。。
        //保存了一本书

    }

  

  使用 @Qualifier 可以指定要装配的组件的名字 id;

  @Autowired 的自动装配就是一定装配上,可以设置 required 属性,默认为 true,设置为 false 后,如果在容器中找不到就不进行装配,且不会报错。

@Controller
public class BookServlet {

    @Qualifier(value = "bookServiceExt")
    @Autowired(required = false)
    private BookService bookService;

    public void doGet() {
        bookService.save();
    }
}

四、原理和区别

  (1)先按照类型去找,如果找到了,装配成功;

  (2)如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配;

  (3)如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id;

  (4)@Autowired注解的required属性指定某个属性允许不被设置;

  @Autowired 原理【***】

    <!--@Autowired原理
    @Autowired
    private BookService bookService;
        1、先按照类型去容器找到对应的组件; bookService = ioc.getBean("BookService.class");
            ①匹配成功;装配
            ②匹配失败;抛出异常报错(NoSushBeanDefinitionException)
            ③匹配多个;
                按照变量名作为id继续匹配
                    1)匹配成功;装配
                    2)匹配失败;抛出异常报错
                        失败原因:Spring会使用id在容器中匹配
                        【可使用@Qualifier("xxxXxx")指定一个新id匹配】
                            1)匹配成功;装配
                            2)匹配失败;抛出异常报错

          发现AutoWired标注的自动装配的属性默认是一定装配上,任何情况下没匹配就是抛出异常报错
                找到就装配,找不到就拉倒
                @Autowired(required=false):默认为true,给为匹配上的赋值为null

  先按照类型找,找到装配,找不到就按变量名做id找,找到装配,找不到抛异常。

  可以用@Qualifier指定id去匹配;此注解可修饰参数

五、在形参位置使用 @Qualifier 注解

   观看源码,可以看到 Autowired 注解可以使用在构造器,字段,方法和注解上面。

   Qualifier 注解可以使用在字段,方法,参数,类和注解上面。

  案例:

    /**
     * 方法上有 @Autowired 注解
     *  1. 这个方法也会在 bean 创建的时候自动运行
     *  2. 这个方法的每一个参数都会自动注入值
     *  3. 可以使用Qualifier注解给参数添加注解
     *  4.  使用 required 属性表示是否必须要装配
     * @param bookDao
     */
    @Autowired(required = false)
    public void saveBook(BookDao bookDao, @Qualifier("bookService") BookService bookService) {
        System.out.println("Spring运行了这个方法" + bookDao + "===" + bookService);
    }

五、@Autowired 注解

    ① 该注解根据类型实现自动装配;
            ② 构造器、普通字段(即使是非 public)、一些具有参数的方法都可以应用 @Autowired 注解;
            ③ 默认情况下,所有使用 @Autowired 注解的属性都需要被设置,当 Spring 找不到匹配的 bean 装配属性时,会抛出异常;
            ④ 若某一属性允许不被设置,可以设置@Autowired 注解的required 属性为 false。 
            ⑤ 默认情况下,当 IOC 容器里存在多个类型兼容的 bean 时,Spring 会尝试匹配 bean 的 id是否与变量名相同,如果相同则进行装配,如果 bean 的 id 值不相同,通过类型的自动装配将无法工作。
                此时可以在 @Qualifier 注解里提供 bean 的名称,Spring 甚至允许在方法的形参上标注 @Qualifier 注解以指定注入 bean 的名称;
            ⑥ @Autowired 注解也可以应用在数组类型的属性上,此时 Spring 将会把所有匹配 bean 进行自动装配;
            ⑦ @Autowired 注解也可以应用在集合属性上,此时 Spring 读取集合的类型信息,然后自动装配所有与之兼容的 bean;
            ⑧ @Autowired 注解用在 java.util.Map 上时,若该 Map 的键值为 String,那么 Spring 将自动装配与值类型兼容的 bean 作为值,并以 bean 的 id 值作为键。

六、@Autowired和@Resource、@Inject的区别

  @Autowired、@Resource、@Inject都可以作为注入注解

@Autowired:最强大;Spring的注解
	①扩展性差,依赖Spring容器框架
	
@Resource:j2ee;java的标准【jdk标准】
	①扩展性强:切换成另一个容器框架,其还会可以使用
	
	
【@Inject:在EJB环境下使用()】

  @Resource 注解要求提供一个 bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 bean 的名称

   @Inject 和 @Autowired 注解一样也是按类型注入匹配的 bean,但没有 required 属性。

   

七、代码示例

  表示控制器层:

//@Controller(value="userCtrl")        //使用 value 属性来给自动生成的 bean 指定 id,默认是类名的首字母表小写
@Controller("userCtrl")                // 如果只设置 value 属性,value可以省略;但如果有多个属性,各个属性间用逗号分隔  
public class UserController {
     
     @Autowired(required=true)           //自动装配,required 该属性表示是否必须自动装配成功
     private IUserService userService;   // true 表示 IOC 容器中必须有一个 bean 与该属性相匹配,然后完成自动装配,不然 Spring 报错
                                         // false 表示 IOC 容器中可以没有与该属性匹配的 bean,即不必自动装配成功,Spring 也不会报错
     public void addUser() {
         userService.addUser();
     }
     public UserController() {
         super();
     
         System.out.println("UserController构造器");
     }
     
}

  业务逻辑层:

@Service
public class UserServiceImpl implements IUserService {
     
     @Autowired
     private IUserDao userDao;
     
     /*
     @Autowired                                     //  这两个注解必须同时使用
     @Qualifier(value="userDaoMybatisImpl")         // 当有多个同类型的bean时,使用该注解指定 bean 的id来给形参赋值,与方法名无关
     public void setUserDao(IUserDao userDao) {
         this.userDao = userDao;
     }
     */
     
     @Override
     public void addUser() {
         userDao.addUser();
     }
     public UserServiceImpl() {
         super();
         System.out.println("UserServiceImpl构造器");
     }
     
}

  持久层:

类一:
@Repository
public class UserDaoImpl implements IUserDao {
     @Override
     public void addUser() {
         System.out.println("UserDaoImpl:添加成功");
     }
     public UserDaoImpl() {
         super();
         System.out.println("UserDaoImpl构造器");
     }
     
}

类二:
@Repository
public class UserDaoMybatisImpl implements IUserDao {
     @Override
     public void addUser() {
         System.out.println("添加成功");
     }
     public UserDaoMybatisImpl() {
         super();
         System.out.println("UserDaoMybatisImpl构造器");
     }
}

八、注解配置 bean 总结

基于注解的组件化管理:
        @Component,@Controller(控制层),@Service(业务逻辑层),@Repository(持久层)
        这四个注解的功能完全相同,都用来标识组件,只是在实际开发中,要在实现不同功能的类上加上相应的注解,以便区分;
    
    完成组件化管理的过程:
        1、在需要被 Spring 管理的类上加上对应的注解
        2、在配置文件中通过 <context:component-scan> 标签对所设置的包结构进行扫描,就会将加上注解的类,作为spring的组件进行加载,
              组件(即为 Spring 中管理的 bean)
              作为 Spring 的组件进行加载:会自动在 Spring 的配置文件中生成相对应的bean,这些 bean的 id 默认会以类的首字母小写为值,也可以通过注解的 value 属性来给自动生成的 bean 指定 id。(如:@Controller(value="userCtrl") 或 @Controller("userCtrl"),只有 value 属性时,value 可以省略)
        
     使用注解自动装配:
            自动装配:在需要赋值的非字面量属性上,加上 @Autowired 注解,就可以在spring容器中,通过不同的方式匹配到相对应的bean;
            @Autowired 装配时,默认使用 byType 的方式进行匹配,此时要求spring容器中只有一个能够为其赋值,否则会报错
                                                当byType 实现不了装配时,会自动切换到 byName 的方式匹配,此时要求spring容器中,有一个bean的id和属性名一致
             使用@Autowired 自动装配时,如果匹配到多个能够赋值的 bean,可以使用 @Qualifier(value="beanId")  来指定一个 bean 给属性赋值;
            @Autowired 和 @Qualifier(value="beanId")  可以一起作用于带形参方法上,此时,@Qualifier(value="beanId") 将用 value 所指定 bean 作用于 方法的形参
 
原文地址:https://www.cnblogs.com/niujifei/p/15418453.html