不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2021-02-27 14:18:18  修改时间:2024-04-16 13:16:16  分类:Java框架/系统  编辑

Spring可以通过编写XML来配置Bean,也可以通过使用Spring的注解来装配Bean。

自动装配(autowiring) 与 自动检测(autodiscovery)

  • 自动装配:让Spring自动识别如何装配bean的依赖关系,减少对<property>元素的使用。
  • 自动检测:让Spring自动识别哪些类需要配置成Spring Bean,减少对<bean>元素的使用。
     

一、自动装配Bean

自动装配的4种类型:

  1. byName:把与bean名字(ID)相同的其他Bean自动装配到Bean的对应属性中。
  2. byType:把与bean类型相同的其他Bean自动装配到Bean的对应属性中。
  3. constructor:把与Bean的构造器入参具有相同类型的其他Bean装配到对应属性中。
  4. autodetect:首先尝试constructor进行自动装配,然后再尝试byType。

假设Spring配有一个Bean:

<bean id="userService" class="com.test.UserServiceImpl">
</bean>

对应于类 com.test.UserService:

public class UserServiceImpl {
    private UserDao userDao; // 属性
    ...
}

若配置的为byName,则Spring会将Bean的id为userDao的Bean自动装配到userService bean的userDao的属性中。

若配置的为byType,则Spring会将Bean的class类型为UserDao的Bean自动装配到userService bean的userDao的属性中。

若配置的为constructor,则类com.test.UserService中要有构造函数,如:

public class UserServiceImpl {
    private UserDao userDao; // 属性
    
    // 构造函数
    pulic UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
}

那么Spring会将类型为UserDao的bean装配到属性userDao中。

写个基于注解自动装配,maven工程如下:

UserServiceImpl类:

import org.springframework.beans.factory.annotation.Autowired;

import com.test.dao.UserDao;
import com.test.domain.User;

public class UserServiceImpl implements UserService {

    @Autowired  // 自动装配Bean,使用注解
    private UserDao userDao;
    
    public void save(User user) {
        userDao.Save(user);
        System.out.println("saved!");
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

自动装配Bean是通过设置自动装配的类型来为Bean的属性装配依赖。所以在beans.xml中只指定Bean而不需要显式指定属性<property>

使用注解装配

从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大差别。但是使用注解方式允许更细粒度的自动装配,我们可以选择性地标注某个属性来对其应用自动装配。

Spring容器默认禁用注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置中启用它。最简单的启用方式是使用Spring的Context命令空间配置中的<context:annotation-config>元素,如下所示:

beans.xml中:

<?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.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd"
        default-autowire="byName">

    
    <context:annotation-config />  <!--开启Spring注解自动装配的功能 -->
    <bean id="userDao" class="com.test.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.test.service.UserServiceImpl"></bean>
    <!-- <context:component-scan base-package="com.test"></context:component-scan> -->
</beans>

<context:annotation-config>元素告诉Spring,我们打算使用基于注解的自动装配。一旦配置成功,我们可以对代码添加注解,标识Spring应该为属性、方法和构造器进行自动装配。

Spring 3 支持几种不同的用于自动装配的注解:

  1. Spring 自带的 @Atutowired注解;
  2. JSR-330 的 @Inject 注解;
  3. JSR-250 的 @Resource 注解;

二、自动检测Bean

当在Spring配置中增加<context:annotation-config>时,我们希望Spring特殊对待我们所定义的Bean里的某一组注解,并使用这些注解指导Bean装配。即使<context:annotation-config>有助于完成消除Spring配置中的<property><constructor-arg>元素,我们仍需要使用<bean>元素显示定义Bean。

但是Spring还有另一个技巧。<context:component-scan>元素除了完成与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean。这意味着不使用<bean>元素,Spring应用中的大多数(或者所有)Bean都能实现定义和装配。

为了配置Spring自动检测,需要使用<context:component-scan>元素来代替<context:annotation-config>元素。

<context:component-scan>元素会扫描指定的包及其所有子包,并查找能够自动注册为Spring Bean的类。base-package的属性标识了<context:component-scan>元素所扫描的包。

默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下:

  1. @Component——通用的构造型注解,标识该类为Spring组件。
  2. @Controller——标识将该类定义为Spring MVC controller。
  3. @Repository——标识将该类定义为数据仓库。
  4. @Service——标识将该类定义为服务。
  5. 使用@Component标注的任意自定义注解。

修改beans.xml,使用<context:component-scan>替代<bean>的定义,如下所示:

<context:annotation-config />

<!-- <bean id="userDao" class="com.test.dao.UserDaoImpl"></bean> -->
<!-- <bean id="userService" class="com.test.service.UserServiceImpl"></bean> -->
<context:component-scan base-package="com.test"></context:component-scan>

修改UserDaoImpl,同样修改UserServiceImpl。

@Component   //spring会自动检测
//@Qualifier("userDao")
public class UserDaoImpl implements UserDao {

    public void Save(User user) {
        System.out.println("userName:" + user.getUserName());
        System.out.println("password:" + user.getPassword());
    }
}

三、限定依赖

当有足够多的Bean满足装配条件,@Autowired注解没办法选择哪一个Bean才是它真正需要的,因此可以配合使用@Qualifier注解

如,除了UserDaoImpl实现接口UserDao,现在还有UserDaoImpl2实现,为UserDaoImpl2使用@Qualifier注解限定。

import com.test.domain.User;

@Qualifier("userDaoOther") // 限定
public class UserDaoImpl1 implements UserDao {

    public void Save(User user) {
        System.out.println("userName1:" + user.getUserName());
        System.out.println("password1:" + user.getPassword());
    }
}

在UserServiceImpl中,若需要装配UserDaoImpl2,除了使用@Autowired自动装配,还需要使用@Qualifier("userDaoOther")限定依赖

@Component("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    @Qualifier("userDaoOther")  // 限定依赖,最后依赖的Bean为UserDaoImpl2
    private UserDao userDao;
    
    public void save(User user) {
        userDao.Save(user);
        System.out.println("saved!");
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}