当前位置:   article > 正文

spring揭秘06-Autowired自动绑定依赖及组件自动扫描

spring揭秘06-Autowired自动绑定依赖及组件自动扫描

【README】

本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;

1)spring自动绑定对象间依赖关系: 指的是不用明确指定每一个bean与被依赖bean之间的依赖关系,太繁琐; 而是通过一种约定方式来指定依赖关系,如通过名称,bean类型,构造器等约定方式来指定; 通过名称或类型等约定方式指定bean之间的依赖关系称自动绑定

2)spring容器自动绑定对象间依赖关系的方式有2种,如下:

  • 方式1: 基于xml配置文件版本的自动绑定,自动绑定策略通过设置 beans元素的 default-autowire属性 或 bean元素的autowire属性来配置;
  • 方式2: 基于java注解版本的自动配置;

【1】基于xml配置文件版本的自动绑定

1)基于xml配置文件版本的自动绑定,自动绑定策略通过设置 beans元素的 default-autowire属性 或 bean元素的autowire属性来配置

2)default-autowire属性值或autowire属性值的可选项:

  • no: 不开启自动绑定;(默认不开启)
  • byname: 通过bean名称来自动绑定; 针对 bean property 的自动绑定,要有setter方法设置property属性;
  • byType: 通过bean类型来自动绑定;针对 bean property 的自动绑定, 要有setter方法设置property属性;
  • constructor: 通过构造器来自动绑定;针对构造方法参数的类型进行自动绑定;显然,constructor实际上是byType类型的自动绑定(但不需要setter方法);
  • autodetect: 通过自动检测来自动绑定;是byType与constructor的结合体; 如果对象拥有默认无参构造器,则优先考虑byType的自动绑定模式(因为构造器没有参数,也就没法使用构造器绑定);如果对象有有参构造器,则使用constructor自动绑定; 如果通过构造器注入依赖关系后还有其他属性没有绑定,最后容器还是会使用byType对剩下的属性进行自动绑定;

3)自动绑定补充:

  • 手工明确指定的绑定关系 优先级高于 自动绑定模式;
  • 自动绑定可以应用到除开原生类型 ,string类型及 Classes类型(包括三者的数组)以外的对象类型

【1.1】基于xml配置文件版本的自动绑定代码示例

1)应用场景: 对于DDD领域驱动设计,其有4层,包括表现层,应用层,领域层,基础设施层;

  • 表现层: 本文用main方法表示;

  • 应用层: 本文用 appService 表示;采用构造器constructor绑定; 底层使用setter注入;

  • 领域层: 本文用 DomainService 表示;采用byType绑定; 底层使用setter注入;

  • 基础设置层:本文用dao表示;采用byName绑定, 有setter方法;

  • 防腐层support: 在业务模型与技术模型之间,即应用层或领域层与基础设施层之间(**作用:**技术模型底层做改造,仅防腐层改动代码,上游的应用层与领域层不修改代码);

【AutoWireXmlConfMain】xml配置文件自动绑定main

public class AutoWireXmlConfMain {
    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirebasedxml.xml");
        container.getBean(RoomBookAppService.class).book("基于xml配置文件的自动绑定房间01", "张三");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【beans0601autowirebasedxml.xml】 beans元素的default-autowire属性设置为byName, bean元素的autowire属性设置为 constructor,byType, byName

<?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"
        default-autowire="byName">

    <bean id="roomBookAppService" class="com.tom.springnote.chapter05.chapter0504.RoomBookAppService" autowire="constructor" />
    <bean id="roomBookDomainService" class="com.tom.springnote.chapter05.chapter0504.RoomBookDomainService" autowire="byType" />
    <bean id="roomBookSupportImpl" class="com.tom.springnote.chapter05.chapter0504.RoomBookSupportImpl" autowire="byName" />
    <bean id="roomBookDAO" class="com.tom.springnote.chapter05.chapter0504.RoomBookDAO" />
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【RoomBookAppService】应用层, 通过构造器绑定

public class RoomBookAppService {

    private RoomBookDomainService roomBookDomainService;

    public RoomBookAppService(RoomBookDomainService roomBookDomainService) {
        this.roomBookDomainService = roomBookDomainService;
    }

    public void book(String roomId, String customer) {
        roomBookDomainService.book(roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【RoomBookDomainService】领域层,通过byType绑定,必须要有setter方法;( 属性名称叫roomBookSupport,但xml中配置的bean名称为roomBookSupportImpl,通过byName无法绑定

public class RoomBookDomainService {

    private IRoomBookSupport roomBookSupport;

    public void book(String roomId, String customer) {
        roomBookSupport.saveRoomBookInf(roomId, customer);
    }

    public void setRoomBookSupport(IRoomBookSupport roomBookSupport) {
        this.roomBookSupport = roomBookSupport;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【IRoomBookSupport】防腐层接口

public interface IRoomBookSupport {

    void saveRoomBookInf(String roomId, String customer);
}
  • 1
  • 2
  • 3
  • 4

【RoomBookSupportImpl】防腐层实现类 ; 通过byName绑定 ;必须要有setter方法 ;

public class RoomBookSupportImpl implements IRoomBookSupport {

    private RoomBookDAO roomBookDAO;

    @Override
    public void saveRoomBookInf(String roomId, String customer) {
        roomBookDAO.insertRoomBook(roomId, customer);
    }

    public void setRoomBookDAO(RoomBookDAO roomBookDAO) {
        this.roomBookDAO = roomBookDAO;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

【RoomBookDAO】基础设施层; 无需绑定对象依赖关系(因为没有依赖),仅注册bean到spring容器即可;

public class RoomBookDAO {

    public void insertRoomBook(String roomId, String customer) {
        System.out.printf("RoomBookDAO: 插入订房信息成功:roomId=[%s], customer=[%s]\n", roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【打印日志】

RoomBookDAO: 插入订房信息成功:roomId=[基于xml配置文件的自动绑定房间01], customer=[张三]
  • 1

【2】基于注解版本的自动绑定

【2.1】根据类型匹配的注解版自动绑定@Autowired

1)@Autowired是基于注解版本的自动绑定的核心注解, spring容器根据@Autowired标注的位置为当前类注入依赖;

2)@Autowired注解的标注位置:

  • 标注类属性property;优先使用 byName自动绑定,若绑定失败,则使用 byType 自动绑定;
  • 标注构造方法定义;使用byType绑定;即根据构造器参数类型,来决定把什么样的依赖对象注入给当前对象;
  • 标注普通方法定义; @Autowired不仅标注setter方法,还可以标注任意名称的方法,只要该方法定义了被注入的参数;

3)案例场景: 房间预订服务;

【AutoWireBasedAnnotationMain】基于注解的自动绑定main

public class AutoWireBasedAnnotationMain {
    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirebasedannotation.xml");
        container.getBean(AnnotationBookAppService.class).book("基于注解的自动绑定房间01", "张三");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【beans0601autowirebasedannotation.xml】 若使用 @Autowired,必须注册 AutowiredAnnotationBeanPostProcessor

<?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">

    <!-- 注册AutowiredAnnotationBeanPostProcessor, 用于检查是否有@Autowired标注的位置以便注入依赖 【若使用 @Autowired,必须注册该BeanPostProcessor-->
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

    <!-- 注册bean,依赖关系由 @Autowired 标注,spring容器根据标注位置绑定依赖 -->
    <bean id="roomBookAppService" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookAppService" />
    <bean id="roomBookDomainService" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookDomainService" />
    <bean id="roomBookSupportImpl" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomBookSupportImpl" />
    <bean id="roomDAO" class="com.tom.springnote.chapter06.autowirebaseannotation.RoomDAO" />
    <bean id="hotelDAO" class="com.tom.springnote.chapter06.autowirebaseannotation.HotelDAO" />

</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

【RoomBookAppService】 应用层:@Autowired标注构造器 (byType的自动绑定

public class RoomBookAppService {

    private RoomBookDomainService bookDomainService;

    @Autowired
    public RoomBookAppService(RoomBookDomainService bookDomainService) {
        this.bookDomainService = bookDomainService;
    }

    public void book(String roomId, String customer) {
        bookDomainService.book(roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

【RoomBookDomainService】 领域层: @Autowired标注属性 (byType的自动绑定

public class RoomBookDomainService {

    @Autowired
    private IRoomBookSupport bookSupport;

    public void book(String roomId, String customer) {
        bookSupport.saveRoomBookInf(roomId, customer);
    }

    public void setBookSupport(IRoomBookSupport bookSupport) {
        this.bookSupport = bookSupport;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

【IRoomBookSupport】防腐层接口

public interface IRoomBookSupport {

    void saveRoomBookInf(String roomId, String customer);
}
  • 1
  • 2
  • 3
  • 4

【RoomBookSupportImpl】防腐层:.@Autowired标注setter方法 ,标注普通方法 (byType的自动绑定

public class RoomBookSupportImpl implements IRoomBookSupport {
    private RoomDAO bookDAO;
    private HotelDAO hotelDAO;

    @Override
    public void saveRoomBookInf(String roomId, String customer) {
        if (!hotelDAO.IfAuthLegal(customer)) {
            System.out.println("权限校验失败");
            return;
        }
        System.out.println("权限校验成功");
        bookDAO.insertRoomBook(roomId, customer);
    }

    @Autowired
    public void setBookDAO(RoomDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    @Autowired
    public void injectHotelDAO(HotelDAO hotelDAO) {
        this.hotelDAO = hotelDAO;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

【HotelDAO】

public class HotelDAO {

    public boolean IfAuthLegal(String customer) {
        System.out.printf("HotelDAO#IfAuthLegal(): [%s]权限校验合法", customer);
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

【RoomDAO】

public class RoomDAO {

    public void insertRoomBook(String roomId, String customer) {
        System.out.printf("RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[%s], customer=[%s]\n", roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【打印日志】

HotelDAO#IfAuthLegal(): [张三]权限校验合法权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[基于注解的自动绑定房间01], customer=[张三]
  • 1
  • 2


【2.2】根据名称匹配的注解版自动绑定@Autowired+@Qualifier

1)背景: 若一个接口有2个或多个实现类,如实例A与B ; 而@Autowired 注解是通过byType自动绑定,无法确认注入实例A还是实例B;

2)解决方法: 引入 @Qualifier注解, 解决@Autowired无法注入有多个类型匹配的bean的问题;

  • @Qualifier注解:实际上是 byName自动绑定注解版,指定注入给定name的bean;
  • @Qualifier注解 只能与 @Autowired结合使用,不能单独使用;

【2.2.1】 示例代码

业务场景: 酒店预订助手接口有2个实现类, 包括 万达酒店预定助手实现类, 希尔顿酒店预定助手实现类; 通过 @Qualifier注解指定bean名称并注入name匹配的bean;

【AutowireQualifierAnnotationMain】基于@Autowired与@Qualifier注解自动绑定main

public class AutowireQualifierAnnotationMain {
    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601autowirequlifierannotation.xml");
        container.getBean(AutowireQualifierBookAppService.class).book("@Autowired与@Qualifier注解的自动绑定01", "张三");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【beans0601autowirequlifierannotation.xml】 新增 <context:annotation-config/> 元素, 以激活 @Autowired 和 @Qualifier 注解

<?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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <!-- 注册 AutowiredAnnotationBeanPostProcessor, 用于检查是否有@Autowired标注的位置以便注入依赖
    AutowiredAnnotationBeanPostProcessor 激活 @Autowired 注解 -->
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

    <!-- 注册bean -->
    <bean id="bookAppService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookAppService" />
    <bean id="bookDomainService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
    <bean id="bookDomainService2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
    <bean id="hiltonRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HiltonRoomBookSupportImpl" />
    <bean id="wandaRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.WandaRoomBookSupportImpl" />
    <bean id="roomDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
    <bean id="roomDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
    <bean id="hotelDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
    <bean id="hotelDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />

</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

【AutowireQualifierBookAppService】 ( 构造器新增 @Quailfier注解

public class AutowireQualifierBookAppService {

    private AutowireQualifierBookDomainService annotationBookDomainService;

    @Autowired
    public AutowireQualifierBookAppService(@Qualifier("bookDomainService2") AutowireQualifierBookDomainService bookDomainService) {
        this.annotationBookDomainService = bookDomainService;
    }

    public void book(String roomId, String customer) {
        annotationBookDomainService.book(roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

【AutowireQualifierBookDomainService】( 属性上新增 @Quailfier注解

public class AutowireQualifierBookDomainService {

    @Autowired
    @Qualifier("hiltonRoomBookSupportImpl")
    private IAutowireQualifierBookSupport bookSupport;

    public void book(String roomId, String customer) {
        bookSupport.saveRoomBookInf(roomId, customer);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

【IAutowireQualifierBookSupport】

public interface IAutowireQualifierBookSupport {

    void saveRoomBookInf(String roomId, String customer);
}
  • 1
  • 2
  • 3
  • 4

【HiltonRoomBookSupportImpl】 ( setter方法与普通方法上新增 @Quailfier注解

public class HiltonRoomBookSupportImpl implements IAutowireQualifierBookSupport {
    private RoomDAO bookDAO;
    private HotelDAO hotelDAO;

    @Override
    public void saveRoomBookInf(String roomId, String customer) {
        System.out.println("希尔顿酒店预订助手");
        if (!hotelDAO.IfAuthLegal(customer)) {
            System.out.println("权限校验失败");
            return;
        } 
        System.out.println("权限校验成功");
        bookDAO.insertRoomBook(roomId, customer);
    }

    @Autowired
    public void setBookDAO(@Qualifier("roomDAO") RoomDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    @Autowired
    public void injectHotelDAO(@Qualifier("hotelDAO") HotelDAO hotelDAO) {
        this.hotelDAO = hotelDAO;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

【WandaRoomBookSupportImpl】 ( setter方法与普通方法上新增 @Quailfier注解

public class WandaRoomBookSupportImpl implements IAutowireQualifierBookSupport {
    private RoomDAO bookDAO;
    private HotelDAO hotelDAO;

    @Override
    public void saveRoomBookInf(String roomId, String customer) {
        System.out.println("万达酒店预订助手");
        if (!hotelDAO.IfAuthLegal(customer)) {
            System.out.println("权限校验失败");
            return;
        }
        System.out.println("权限校验成功");
        bookDAO.insertRoomBook(roomId, customer);
    }

    @Autowired
    public void setBookDAO(@Qualifier("roomDAO2") RoomDAO bookDAO) {
        this.bookDAO = bookDAO;
    }

    @Autowired
    public void injectHotelDAO(@Qualifier("hotelDAO2") HotelDAO hotelDAO) {
        this.hotelDAO = hotelDAO;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

【打印日志】

希尔顿酒店预订助手
HotelDAO#IfAuthLegal(): [张三]权限校验合法
权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[基于注解的自动绑定房间01], customer=[张三]
  • 1
  • 2
  • 3
  • 4

【2.3】关于@Primary注解的补充

@Primary注解: 标注的类的bean实例化后, 若该类下有多个实例化的bean,则优先使用 被Primary标注的那个bean;

【PdfFileReader】FileReader子类1

package basic.ioc.wiring.finetune;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Primary
@Component
public class PdfFileReader implements FileReader {

  @Override
  public void print() {
    System.out.println("Inside PdfFileReader");
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

【WordFileReader】FileReader子类2

package basic.ioc.wiring.finetune;
import org.springframework.stereotype.Component;

@Component
public class WordFileReader implements FileReader {

  @Override
  public void print() {
    System.out.println("Inside WordFileReader");
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【FineTuneAutowiring】Primary注解测试案例入口

package basic.ioc.wiring.finetune;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(ConfigWiring.class)
public class FineTuneAutowiring {

  @Autowired
  private FileReader fileReader;

  @Test
  public void testFileReader() {
    Assert.assertNotNull(fileReader);
    Assert.assertEquals(fileReader.getClass(), PdfFileReader.class);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

因为 PdfFileReader 被@Primary注解标注, 所以业务bean#FineTuneAutowiring通过 @Autowired自动绑定 FileReader实例时,优先选择PdfFileReader示例进行绑定;

【2.3】关于context:annotation-config元素

1)<context:annotation-config /> 可以激活多个注解以便在class中被探测到: 激活的注解清单如下:

  • @Required, @Autowired
  • JSR 250定义的注解 @PostConstruct, @PreDestroy and @Resource ;
  • JAX-WS’s @WebServiceRef
  • EJB 3’s @EJB
  • JPA’s @PersistenceContext and @PersistenceUnit

或者你也可以选择注入这些注解对应的BeanPostProcessor ;

2)context:annotation-config 注册的BeanPostProcessor清单如下: 参见: https://docs.spring.io/spring-framework/reference/core/beans/annotation-config.html

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • EventListenerMethodProcessor

3) 我们可以这样理解:因为context:annotation-config 注册了多个BeanPostProcessor到spring容器,这些BeanPostProcessor可以扫描注解标注的位置,以此自动绑定相应依赖注入;即 注册BeanPostProcessor,间接激活了自动绑定的注解

4)所以 beans0601autowirequlifierannotation.xml中可以删除 AutowiredAnnotationBeanPostProcessor 这个BeanPostProcessor的注入配置;因为 context:annotation-config 激活了对应的BeanPostProcessor;

删除AutowiredAnnotationBeanPostProcessor注入配置后xml如下

【beans0601autowirequlifierannotation.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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <!-- 注册bean -->
    <bean id="bookAppService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookAppService" />
    <bean id="bookDomainService" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
    <bean id="bookDomainService2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.AutowireQualifierBookDomainService" />
    <bean id="hiltonRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HiltonRoomBookSupportImpl" />
    <bean id="wandaRoomBookSupportImpl" class="com.tom.springnote.chapter06.autowiredqualifierannotation.WandaRoomBookSupportImpl" />
    <bean id="roomDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
    <bean id="roomDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.RoomDAO" />
    <bean id="hotelDAO" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />
    <bean id="hotelDAO2" class="com.tom.springnote.chapter06.autowiredqualifierannotation.HotelDAO" />

</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

【补充】 如果xml文件中只注入 AutowiredAnnotationBeanPostProcessor , 而不引入 <context:annotation-config />元素, 则无法激活 @Qualifier注解。 这会导致spring自动绑定依赖关系时,若发现同一个类有多个bean,则抛出异常 【No qualifying bean of type ‘XXX’ available: expected single matching bean but found 2: XXX1, XXX2】。


【2.4】JSR250注解

1) jsr250,即第250号java规范请求,其提供的注解包括 @Resource, @PostConstruct , @PreDestory ;

  • @Resource: 根据byName自动绑定依赖注入关系; 可以标注属性和方法,但不能标注构造器;
  • @PostConstruct: bean实例化后被调用(构造器执行后被调用)
  • @PreDestory: bean销毁前被调用

【2.4.1】 @Resource注解标注依赖注入关系代码

【Jsr250AnnotationAutowireMain】JSR250注解自动装配main入口

public class Jsr250AnnotationAutowireMain {
    public static void main(String[] args) {
        String[] locations = {"classpath:chapter06/beans0601autowirequlifierannotation.xml"
                , "classpath:chapter06/beans0601jsr250annotation.xml"};
        ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext(locations);
        // 若有bean销毁前执行回调,则需要注册关闭钩子回调
        container.registerShutdownHook();
        container.getBean(Jsr250AnnotationBookAppService.class).book("JSR250#@Resource注解的自动绑定01", "张三");
        System.out.println("容器关闭");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

【Jsr250AnnotationBookAppService】JSR250注解@Resource标注bean以便自动绑定依赖关系

public class Jsr250AnnotationBookAppService {

    @Resource(name = "bookDomainService2")
    private AutowireQualifierBookDomainService annotationBookDomainService;

    public Jsr250AnnotationBookAppService() {
        System.out.println("Jsr250AnnotationBookAppService 构造器");
    }

    public void book(String roomId, String customer) {
        annotationBookDomainService.book(roomId, customer);
    }

    @PostConstruct
    public void afterInstance() {
        System.out.println("@PostConstruct注解标注的方法,实例化之后执行");
    }

    @PreDestroy
    public void destory() {
        System.out.println("@PreDestroy注解标注的方法,bean销毁前执行");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

【打印日志】

Jsr250AnnotationBookAppService 构造器
@PostConstruct注解标注的方法,实例化之后执行
希尔顿酒店预订助手
HotelDAO#IfAuthLegal(): [张三]权限校验合法
权限校验成功
RoomDAO#insertRoomBook(): 插入订房信息成功:roomId=[JSR250#@Resource注解的自动绑定01], customer=[张三]
容器关闭
@PreDestroy注解标注的方法,bean销毁前执行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

@Resource注解作用总结

@Resource注解可以替代 @Autowired 与 @Qualilfier这2个注解结合的功能,即根据beanName到spring容器中查找匹配的bean,并注入依赖关系到当前bean(如Jsr250AnnotationBookAppService);
在这里插入图片描述

【2.4.2】@PostConstruct 与 @PreDestroy注解

1) @PostConstruct: bean实例化后被调用(构造器执行后被调用);

  • 总结:bean实例化后的生命周期管理方式:
    • @PostConstruct注解;
    • 实现 InitializingBean接口;
    • 通过 init-method 指定初始化方法;

2)@PreDestroy: bean销毁前被调用;

  • 总结:bean销毁前执行回调的生命周期管理方式:
    • @PreDestroy注解;
    • 实现 DisposableBean 接口;
    • 通过 destory-method 指定销毁前的回调方法;

补充: 代码示例参见 Jsr250AnnotationBookAppService ;


【2.5】 <context:component-scan>开启spring组件扫描

1)背景: 2.2 与 2.3的代码示例中, xml配置文件仅注册bean,不管理bean的依赖关系; 其依赖关系通过注解来管理; 即需要编写xml配置与注解;当bean比较多的时候,开发xml配置文件比较繁琐;

2)解决方法: 使用 classpath-scanning 扫描给定路径的package;

  • 当扫描到某个类标注对应注解后,就提取该类的相关信息,构建对应 BeanDefinition,并把BeanDefinition注册到容器。
  • 接着容器遍历BeanDefinition实例化bean,并通过标注注解的对应BeanPostProcessor来注入依赖关系;

3)classpath-scanning组件扫描功能,通过在xml文件配置 <context:component-scan> 来开启;

  • <context:component-scan> 默认扫描 @Component注解标注的类,并实例化bean;
  • 此外, 因为 @Controller注解, @Service注解, @Repository注解 是基于@Component定义的,所以上述3个也可以被 <context:component-scan> 扫描到

4)补充: <context:component-scan> 元素,可以新增属性 include-filter 和 exclude-filter 分别包含和排除某些package ;


【2.5.1】开启组件扫描的自动绑定代码示例

【ComponentScanMain】main入口

public class ComponentScanMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext("classpath:chapter06/beans0601componentscan.xml");
        container.getBean(ComponentScanAppService.class).scan();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【beans0601componentscan.xml】 配置了 <context:annotation-config/> 元素, <context:component-scan> 元素 ;

其中,当需要扫描多个包时,用逗号分割;

<?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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 注册5个BeanPostProcessor,间接激活多个自动绑定注解及JSR250注解,包括 @Autowired, @Qualiifer, @Resource, @PostConstruct, @PreDestory  -->
    <context:annotation-config/>
    <!-- 开启组件扫描,无需手工在xml配置文件注册每个bean,间接激活多个component注解,包括 @Componenet, @Repository ,@Service, @Controller -->
    <context:component-scan base-package="com.tom.springnote.chapter06.componentscan,com.tom.springnote.chapter06.componentscan2" />

</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

【ComponentScanAppService】 ( 使用 @Service注解

@Service
public class ComponentScanAppService {

    @Autowired
    private ComponentScanDomainService scanComponentDomainService;

    public void scan() {
        scanComponentDomainService.scan();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

【ComponentScanDomainService】( 使用 @Service注解

@Service
public class ComponentScanDomainService {
    @Autowired
    @Qualifier("hiltonComponenetScanSupportImpl")
    private IComponentScanSupport componentScanSupport;

    public void scan() {
        componentScanSupport.scan();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

【IComponentScanSupport】

public interface IComponentScanSupport {

    void scan();
}
  • 1
  • 2
  • 3
  • 4

【HiltonComponenetScanSupportImpl】 ( 使用 @Component注解

@Component("hiltonComponenetScanSupportImpl")
public class HiltonComponenetScanSupportImpl implements IComponentScanSupport {

    @Autowired
    private ComponentScanDAO scanComponentDAO;

    @Override
    public void scan() {
        System.out.println("希尔顿组件扫描助手");
        scanComponentDAO.scan();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【WandaComponenetScanSupportImpl】 ( 使用 @Component注解

@Component("wandaComponenetScanSupportImpl")
public class WandaComponenetScanSupportImpl implements IComponentScanSupport {

    @Autowired
    private ComponentScanDAO scanComponentDAO;

    @Override
    public void scan() {
        System.out.println("万达组件扫描助手");
        scanComponentDAO.scan();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【ComponentScanDAO】 ( 使用 @Repository注解

@Repository
public class ComponentScanDAO {
    public void scan() {
        System.out.println("ScanComponentDAO#scan():扫描完成");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

【打印日志】

希尔顿组件扫描助手
ScanComponentDAO#scan():扫描完成
  • 1
  • 2

【3】小结

1)本文描述了2种自动绑定依赖关系的方式, 包括xml配置文件版本的自动绑定,注解版本的自动绑定;

  • 依赖注入自动化: 在介绍注解版本的自动绑定时,本文引入了 <context:annotation-config> xml元素 ,它注册5个BeanPostProcessor,间接激活多个自动绑定注解及JSR250注解,包括 @Autowired, @Qualiifer, @Resource, @PostConstruct, @PreDestory ; 有了这些组件,我们可以实现自动绑定依赖关系,而无需手工在xml文件中配置每个bean的依赖关系, 开发量显著减少;
  • 对象bean注册自动化:本文还引入了 <context:component-scan> xml元素, 开启组件扫描,无需手工在xml配置文件注册每个bean ; 简单理解为:<context:component-scan> 间接激活多个component注解,包括 @Component, @Repository ,@Service, @Controller

2)问题:能否仅使用注解实现自动绑定; 答案是否;

  • 因为无法通过注解来标注第三方提供的类库;即针对第三方类库, 我们可以结合使用基于配置文件的依赖注入方式;因为基于xml的依赖注入方式是spring提供的最基本,最强大的表达方式

【补充】 JSR250

1)JSR,Java Specification Request, 即 java规范请求: 是 Java 平台提议和最终规范的实际描述。任何时候都有大量 JSR 正在通过审查和批准流程。 JSR250表示第250号java规范请求; 参见 https://jcp.org/en/jsr/overview ;JSR清单,参见:https://jcp.org/en/jsr/all

简单理解, 开发者自行开发了api,把api封装到JSR并报送给java社区;审批通过后,以JSR形式发布api;



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/1000721
推荐阅读
相关标签
  

闽ICP备14008679号