本文分享自华为云社区《Spring高手之路2——深入了解注解驱动装备与XML装备的融合与差异》,作者:砖业洋__ 。

本文旨在深入探讨Spring结构的注解驱动装备与XML装备,提醒两者之间的相似性与差异。咱们首要介绍了装备类的编写与Bean的注册,然后比较了注解驱动的IOC依靠注入与XML依靠注入。文章进一步解析了Spring的组件注册与组件扫描,包括运用@ComponentScan和XML启用component-scan的状况,以及不运用@ComponentScan的场景。接下来,咱们深入探讨了其他相关的组件

1. 装备类的编写与Bean的注册

XML装备中,咱们一般采用ClassPathXmlApplicationContext,它能够加载类途径下的XML装备文件来初始化Spring运用上下文。可是,在注解驱动的装备中,咱们则运用以Annotation开头和ApplicationContext结尾的类,如AnnotationConfigApplicationContextAnnotationConfigApplicationContextSpring容器的一种,它完成了ApplicationContext接口。

比照于 XML 文件作为驱动,注解驱动需求的是装备类。一个装备类就能够相似的了解为一个 XML 。装备类没有特别的约束,只需求在类上标示一个 @Configuration 注解即可。

咱们创立一个 Book 类:

public class Book {
    private String title;
    private String author;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}

xml 中声明 Bean 是经过 <bean> 标签

<bean id="book" class="com.example.Book">
    <property name="title" value="Java Programming"/>
    <property name="author" value="Unknown"/>
</bean>

假如要在装备类中替换掉 <bean> 标签,需求运用 @Bean 注解

咱们创立一个装备类来注册这个 Book bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LibraryConfiguration {
    @Bean
    public Book book() {
        Book book = new Book();
        book.setTitle("Java Programming");
        book.setAuthor("Unknown");
        return book;
    }
}

在这个装备中,咱们运用了 @Configuration 注解来标明这是一个装备类,相似于一个 XML 文件。咱们在 book() 办法上运用了 @Bean 注解,这意味着这个办法将返回一个由 Spring 容器办理的目标。这个目标的类型便是 Bookbean 的称号id便是办法的称号,也便是 “book”。

相似于 XML 装备的 <bean> 标签,@Bean 注解担任注册一个 bean。你能够把 @Bean 注解看作是 <bean> 标签的替代品。

假如你想要更改这个 bean 的称号,你能够在 @Bean 注解中运用 name 特点:

   @Bean(name="mybook")
    public Book book() {
        Book book = new Book();
        book.setTitle("Java Programming");
        book.setAuthor("Unknown");
        return book;
    }

这样,这个 Book bean 的称号就变成了 “mybook”。

发动并初始化注解驱动的IOC容器

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class);
        // 从容器中获取 Book bean
        LibraryConfiguration libraryConfiguration = context.getBean(LibraryConfiguration.class);
        System.out.println(libraryConfiguration.book().getTitle());
        System.out.println(libraryConfiguration.book().getAuthor());
    }
}

ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class)这个句子创立了一个Spring的运用上下文,它是以装备类LibraryConfiguration.class作为输入的,这儿明晰指定装备类的Spring运用上下文,适用于更一般的Spring环境。

比照一下ApplicationContext context = SpringApplication.run(DemoApplication.class, args);这个句子则是Spring Boot运用的进口,发动一个Spring Boot运用。SpringApplication.run()办法会创立一个Spring Boot运用上下文(也便是一个SpringApplication目标),这个上下文包括了Spring Boot运用一切的Bean和装备类,还有大量的默许装备。这个办法之后,Spring Boot的主动装备就会起作用。你能够把SpringApplication.run()创立的Spring Boot上下文看作是愈加功用丰厚的Spring上下文。

打印成果:

深入理解注解驱动配置与XML配置的融合与区别

Java ProgrammingUnknown被打印,执行成功。

留意:@SpringBootApplication是一个复合注解,它等效于同时运用了@Configuration@EnableAutoConfiguration@ComponentScan。这三个注解的作用是:

  • @Configuration:指明该类是一个装备类,它或许会有零个或多个@Bean注解,办法产生的实例由Spring容器办理。

  • @EnableAutoConfiguration:告知Spring Boot依据增加的jar依靠主动装备你的Spring运用。

  • @ComponentScanSpring Boot会主动扫描该类地点的包以及子包,查找一切的Spring组件,包括@Configuration类。

在非Spring Boot的传统Spring运用中,咱们一般运用AnnotationConfigApplicationContext或许ClassPathXmlApplicationContext等来手动创立和初始化SpringIOC容器。

“非Spring Boot的传统Spring运用”是指在Spring Boot项目呈现之前的Spring项目,这些项目一般需求手动装备许多东西,例如数据库连接、事务办理、MVC操控器等。这种类型的Spring运用一般需求开发者对Spring结构有深入的了解,才干做出正确的装备。

Spring BootSpring项目的一个子项目,它旨在简化Spring运用的创立和装备进程。Spring Boot供给了一系列的”起步依靠”,使得开发者只需求增加少数的依靠就能够快速开始项目的开发。此外,Spring Boot还供给了主动装备的特性,这使得开发者无需手动装备数据库连接、事务办理、MVC操控器等,Spring Boot会依据项目的依靠主动进行装备。

因而,”非Spring Boot的传统Spring运用”一般需求手动创立和初始化SpringIOC容器,比如运用AnnotationConfigApplicationContextClassPathXmlApplicationContext等。在Spring Boot运用中,这个进程被主动化了,开发者只需求在main办法中调用SpringApplication.run办法,Spring Boot就会主动创立和初始化SpringIOC容器。SpringApplication.run(Application.class, args);句子便是发动Spring Boot运用的要害。它会发动一个运用上下文,这个上下文会加载一切的Spring组件,并且也会发动SpringIOC容器。在这个进程中,一切经过@Bean注解界说的bean都会被创立,并注册到IOC容器中。

有人说,那学习Spring Boot就好了,学什么Spring和Spring MVC啊,这不是落后了吗

Spring Boot并不是Spring结构的替代品,而是建立在Spring结构之上的一种东西,它内部仍然运用Spring结构的许多核心技能,包括Spring MVC。所以,当咱们在运用Spring Boot时,咱们实际上仍然在运用Spring MVC来处理Web层的事务。

简而言之,Spring MVC是一个用于构建Web运用程序的结构,而Spring Boot是一个用于简化Spring运用程序开发的东西,它内部仍然运用了Spring MVC。你在Spring Boot运用程序中运用的@Controller@Service@Autowired等注解,其实都是Spring结构供给的,所以,原理性的东西仍是需求知道。

2. 注解驱动IOC的依靠注入与XML依靠注入比照

咱们就以上面的比如来说,假定装备类注册了两个bean,并设置相关的特点:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LibraryConfiguration {
    @Bean
    public Book book() {
        Book book = new Book();
        book.setTitle("Java Programming");
        book.setAuthor("Unknown");
        return book;
    }
    @Bean
    public Library library() {
        Library library = new Library();
        library.setBook(book());
        return library;
    }
}

这儿的办法有@Bean注解,这个注解告知Spring,这个办法返回的目标需求被注册到SpringIOC容器中。

假如不用注解,要完成相同功用的话,对应的XML装备如下:

<bean id="book" class="com.example.Book">
    <property name="title" value="Java Programming"/>
    <property name="author" value="Unknown"/>
</bean>
<bean id="library" class="com.example.Library">
    <property name="book" ref="book"/>
</bean>

在这个XML装备中,咱们界说了两个<bean>元素,分别用来创立Book目标和Library目标。在创立Book目标时,咱们运用了<property>元素来设置titleauthor特点。在创立Library目标时,咱们也运用了<property>元素,可是这次咱们运用了ref特点来引用现已创立的Book目标,这就相当于将Book目标示入到Library目标中。

3. Spring中组件的概念

Spring结构中,当咱们说 “组件” 的时分,咱们一般指的是被Spring办理的各种Java目标,这些目标在Spring的运用上下文中作为Bean存在。这些组件或许是服务层的类、数据拜访层的类、操控器类、装备类等等。

@ComponentScan注解会扫描指定的包(及其子包)中的类,假如这些类上标示了@Component@Controller@Service@Repository@Configuration等注解,那么Spring就会为这些类创立Bean界说,并将这些Bean界说注册到Spring的运用上下文中。因而,咱们一般说@ComponentScan进行了”组件扫描”,由于它扫描的是标示了上述注解的类,这些类在Spring中都被视为组件。

而这些注解符号的类,终究在Spring的运用上下文中都会被创立为Bean,因而,你也能够了解@ComponentScan为”Bean扫描”。可是需求留意的是,@ComponentScan只担任扫描和注册Bean界说,Bean界说便是元数据描绘,包括了怎样创立Bean实例的信息。

总结一下,@ComponentScan注解会扫描并注册的”组件”包括:

  • 标示了@Component注解的类
  • 标示了@Controller注解的类(Spring MVC中的操控器组件)
  • 标示了@Service注解的类(服务层组件)
  • 标示了@Repository注解的类(数据拜访层组件)
  • 标示了@Configuration注解的类(装备类)

这些组件终究都会在Spring的运用上下文中以Bean的形式存在。

4. 组件注册

这儿Library 标示 @Configuration 注解,即代表该类会被注册到 IOC 容器中作为一个 Bean

@Component
public class Library {
}

相当于 xml 中的:

<bean id="library" class="com.example.demo.configuration.Library">

假如想指定 Bean 的称号,能够直接在 @Configuration 中声明 value 特点即可

@Component("libra")
public class Library {
}

@Component("libra")就将这个bean的称号改为了libra,假如不指定 Bean 的称号,它的默许规则是 “类名的首字母小写”(例如Library默许称号是 library )

5. 组件扫描

假如咱们只写了@Component@Configuration 这样的注解,IOC容器是找不到这些组件的。

5.1 运用@ComponentScan的组件扫描

忽略掉之前的比如,在这儿咱们需求运转的代码如下:

@Component
public class Book {
    private String title = "Java Programming";
    private String author = "Unknown";
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
@Component
public class Library {
    @Resource
    private Book book;
    public Book getBook() {
        return book;
    }
    public void setBook(Book book) {
        this.book = book;
    }
}

假如不写@ComponentScan,并且@Component注解标识的类不在当时包或许子包,那么就会报错。

深入理解注解驱动配置与XML配置的融合与区别

莫非@Component注解标识的类在当时包或许当时包的子包,主程序上就能够不写@ComponentScan了吗?

是的!前面说了,@SpringBootApplication 包括了 @ComponentScan,其完成已帮咱们写了!只有组件和主程序不在一个共同的根包下,才需求显式地运用 @ComponentScan 注解。由于 Spring Boot 的规划原则是“约好优于装备”,所以推荐将主运用类放在根包下。

在运用中,咱们的组件(带有 @Component@Service@Repository@Controller 等注解的类)和主装备类坐落不同的包中,并且主装备类或许发动类没有运用 @ComponentScan 指定扫描这些包,那么在运转时就会报错,由于Spring找不到这些组件。

主程序:

@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        Library library = context.getBean(Library.class);
        System.out.println(library.getBook().getTitle());
        System.out.println(library.getBook().getAuthor());
    }
}

深入理解注解驱动配置与XML配置的融合与区别

@ComponentScan 不一定非要写在主程序(一般是指 Spring Boot 的发动类)上,它能够写在任何装备类(符号有 @Configuration 注解的类)上。@ComponentScan 注解会告知 Spring 从哪些包开始进行组件扫描。

为了简化装备,咱们一般会将 @ComponentScan 放在主程序上,由于主程序一般会坐落根包下,这样能够扫描到一切的子包。这儿为了演示,并没有把主程序放在根目录。

咱们上面说过,@ComponentScan只担任扫描和注册Bean界说,只有需求某个Bean时,这个Bean才会实例化

那怎样才干知道是不是需求这个Bean呢?

我来给大家举比如,并且还会阐明Bean的创立次序问题,”需求某个Bean“一般体现在以下几个方面:

  • 依靠注入(Dependency Injection): 假如一个BeanA的字段或许构造办法被标示为@Autowired或许@Resource,那么Spring就会尝试去寻觅类型匹配的BeanB并注入到BeanA中。在这个进程中,假如BeanB还没有被创立,那么Spring就会先创立BeanB的实例。

    @Component public class BeanA { @Autowired private BeanB beanB; }

    @Component public class BeanB { }

BeanA依靠于BeanB。在这种状况下,当你尝试获取BeanA的实例时,Spring会首要创立BeanB的实例,然后把这个实例注入到BeanA中,最后创立BeanA的实例。在这个比如中,BeanB会先于BeanA被创立。

这种办法的一个主要长处是,咱们不需求关心Bean的创立次序,Spring会主动解决这个问题。这是Spring IoC容器的一个重要特性,也是为什么它能够使咱们的代码愈加简洁和易于维护的原因。

  • Spring结构调用: 有些状况下,Spring结构的一些组件或许模块或许需求用到你界说的Bean。比如,假如你界说了一个@Controller,那么在处理HTTP恳求时,Spring MVC就会需求运用到这个@Controller Bean。假如这个时分Bean还没有被创立,那么Spring也会先创立它的实例。

假定咱们有一个名为BookController的类,该类需求一个BookService目标来处理一些事务逻辑。

@Controller
public class BookController {
    @Autowired
    private BookService bookService;
    // 其他的操控器办法
}

BookService

@Service
public class BookService {
    @Autowired
    private BookMapper bookMapper;
    // 一些事务逻辑办法
}

Spring Boot运用程序发动时,以下步骤将会产生:

  1. 首要,Spring结构经过@ComponentScan注解扫描类途径,找到了BookControllerBookServiceBookMapper等类,并为它们创立Bean界说,注册到Spring的运用上下文中。

  2. 当一个恳求抵达并需求运用到BookController时,Spring结构会尝试创立一个BookControllerBean实例。

  3. 在创立BookControllerBean实例的进程中,Spring结构发现BookController类中需求一个BookServiceBean实例(经过@Autowired注解指定),所以Spring结构会先去创立一个BookServiceBean实例。

  4. 同样,在创立BookServiceBean实例的进程中,Spring结构发现BookService类中需求一个BookMapperBean实例(经过@Autowired注解指定),所以Spring结构会先去创立一个BookMapperBean实例。

  5. 在一切依靠的Bean都被创立并注入之后,BookControllerBean实例终究被创立完结,能够处理来自用户的恳求了。

在这个进程中,BookControllerBookServiceBookMapper这三个Bean的创立次序是有严格要求的,有必要依照他们之间的依靠关系来创立。只有当一个Bean的一切依靠都现已被创立并注入后,这个Bean才干被创立。这便是Spring结构的IoC(操控反转)和DI(依靠注入)的机制。

  • 手动获取: 假如你在代码中手动经过ApplicationContext.getBean()办法获取某个Bean,那么Spring也会在这个时分创立对应的Bean实例,假如还没有创立的话。

总的来说,”需求”一个Bean,是指在运转时有其他代码需求运用到这个Bean的实例,这个”需求”或许来源于其他Bean的依靠,也或许来源于结构的调用,或许你手动获取。在这种需求呈现时,假如对应的Bean还没有被创立,那么Spring就会依据之前经过@ComponentScan等办法注册的Bean界说,创立对应的Bean实例。

5.2 xml中启用component-scan组件扫描

对应于 @ComponentScanXML 装备是 <context:component-scan> 标签

<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:component-scan base-package="com.example" />
</beans>

在这段 XML 装备中,<context:component-scan> 标签指定了 Spring 需求扫描 com.example 包及其子包下的一切类,这与 @ComponentScan 注解的功用是相同的。

留意:在运用 <context:component-scan> 标签时,需求在 XML 装备文件的顶部包括 context 命名空间和相应的 schema 方位(xsi:schemaLocation)。

5.3 不运用@ComponentScan的组件扫描

假如咱们不写@ComponentScan注解,那么这儿能够把主程序改为如下:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
        Library library = context.getBean(Library.class);
        System.out.println(library.getBook().getTitle());
        System.out.println(library.getBook().getAuthor());
    }
}

AnnotationConfigApplicationContext 的构造办法中有一个是填写basePackages途径的,能够接受一个或多个包的姓名作为参数,然后扫描这些包及其子包。

深入理解注解驱动配置与XML配置的融合与区别

运转成果如下:

深入理解注解驱动配置与XML配置的融合与区别

在这个比如中,Spring 将会扫描 com.example 包及其一切子包,查找并注册一切的 Bean,达到和@ComponentScan注解相同的作用。

咱们也能够手动创立一个装备类来注册bean,那么想要运转得到相同的作用,需求的代码如下:

@Component
public class Book {
    private String title = "Java Programming";
    private String author = "Unknown";
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
@Component
public class Library {
    private Book book;
    public Book getBook() {
        return book;
    }
    public void setBook(Book book) {
        this.book = book;
    }
}
@Configuration
public class LibraryConfiguration {
    @Bean
    public Book book() {
        Book book = new Book();
        book.setTitle("Java Programming");
        book.setAuthor("Unknown");
        return book;
    }
    @Bean
    public Library library() {
        Library library = new Library();
        library.setBook(book());
        return library;
    }
}

主程序:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfiguration.class);
        Library library = context.getBean(Library.class);
        System.out.println(library.getBook().getTitle());
        System.out.println(library.getBook().getAuthor());
    }
}

咱们创立了一个装备类LibraryConfiguration,用于界说BookLibrary这两个bean。然后以装备类LibraryConfiguration.class作为输入的来创立SpringIOC容器(Spring运用上下文便是Spring IOC容器)。

运转成果和前面相同。

留意,在这个比如里,假如你写@ComponentScan,并且SpringApplication.run(Application.class, args);作为Spring上下文,那么这儿运转装备类需求去掉BookLibrary类的@Component注解,不然会报错A bean with that name has already been defined。这是由于假如同时在 BookLibrary 类上运用了 @Component 注解,并且装备类LibraryConfiguration上运用了@Configuration注解,这都会被 @ComponentScan 扫描到,那么 BookLibrary的实例将会被创立并注册两次。正确的做法是,要么在装备类中经过 @Bean 注解的办法创立BookLibrary的实例,要么在 BookLibrary 类上写 @Component 注解。假如不是第三方库,咱们一般挑选后者。

为什么要有装备类呈现?一切的Bean上面运用@Component,用@ComponentScan注解扫描不就能解决了吗?

咱们在运用一些第三方库时,需求对这些库进行一些特定的装备。这些装备信息,咱们或许无法直接经过注解或许XML来完结,或许经过这些办法完结起来非常麻烦。而装备类能够很好地解决这个问题。经过装备类,咱们能够在Java代码中完结任何杂乱的装备逻辑。

假定你正在运用 MyBatis,在这种状况下或许需求装备一个SqlSessionFactory,在大多数状况下,咱们无法(也不应该)直接修正第三方库的代码,所以无法直接在SqlSessionFactory类或其他类上增加@Configuration@Component等注解。为了能够在Spring中运用和装备这些第三方库,咱们需求创立自己的装备类,并在其间界说@Bean办法来初始化和装备这些类的实例。这样就能够灵敏地操控这些类的实例化进程,并且能够利用Spring的依靠注入功用。

下面是一个运用@Configuration@Bean来装备MyBatis的比如:

@Configuration
@MapperScan("com.example.demo.mapper")
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath*:com/example/demo/mapper/*Mapper.xml")
        );
        return factoryBean.getObject();
    }
}

sqlSessionFactory办法创立一个SqlSessionFactoryBean目标,并运用DataSourceSpring Boot默许为你装备的一个Bean)进行初始化。然后,它指定MyBatis mapper XML文件的方位,最后返回SqlSessionFactory目标。

经过这种办法,你能够灵敏地装备MyBatis,并将其整合到Spring运用中。这是一种比运用XML装备文件或仅仅依靠于主动装备更为灵敏和强大的办法。

6. 组件注册的其他注解

@Controller, @Service, @Repository@Component 相同的作用,它们都会被 Spring IoC 容器识别,并将类实例化为 Bean。让咱们来看这些注解:

  • @Controller:这个注解一般标示在标明表现层(比如 Web 层)的类上,如Spring MVC 中的操控器。它们处理用户的 HTTP 恳求并返回呼应。虽然 @Controller@Component 在功用上是相似的,但 @Controller 注解的运用标明晰一种语义化的分层结构,使得操控层代码愈加明晰。

深入理解注解驱动配置与XML配置的融合与区别

  • @Service:这个注解一般用于标示事务层的类,这些类担任处理事务逻辑。运用 @Service 注解标明该类是事务处理的核心类,使得代码更具有语义化。

深入理解注解驱动配置与XML配置的融合与区别

  • @Repository:这个注解用于符号数据拜访层,也便是数据拜访目标或DAO层的组件。在数据库操作的完成类上运用 @Repository 注解,这样Spring将主动处理与数据库相关的反常并将它们转化为SpringDataAccessExceptions

深入理解注解驱动配置与XML配置的融合与区别

在实际开发中,几乎很少看到@Repository,而是利用 MyBatis@Mapper@MapperScan 完成数据拜访,一般做法是,@MapperScan 注解用于扫描特定包及其子包下的接口,这些接口被称为 Mapper 接口。Mapper 接口办法界说了 SQL 查询句子的签名,而详细的 SQL 查询句子则一般在与接口同名的 XML 文件中界说。

@MapperScan("com.example.**.mapper") 会扫描 com.example 包及其一切子包下的名为 mapper 的包,以及 mapper 包的子包。 ** 是一个通配符,代表任意深度的子包。

举个比如,以下是一个 Mapper 接口的界说:

package com.example.demo.mapper;
public interface BookMapper {
    Book findBookById(int id);
}

对应的 XML 文件(一般坐落 resources 目录下,并且与接口在相同的包途径中)

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.BookMapper">
    <select id="findBookById" parameterType="int" resultType="com.example.demo.Book">
        SELECT title, author FROM book WHERE id = #{id}
    </select>
</mapper>

留意:在 XML 文件中的 namespace 特点值有必要与 Mapper 接口的全限制类名相同,<select> 标签的 id 特点值有必要与接口办法名相同。

然后,在 Spring Boot 的主类上,咱们运用 @MapperScan 注解指定要扫描的包:

@SpringBootApplication
@MapperScan("com.example.**.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这样,MyBatis 就会主动为 UserMapper 接口创立一个完成类(实际上是一个署理目标),并将其注册到 Spring IOC 容器中,你就能够在你的服务类中直接注入 BookMapper 并运用它。

或许有小伙伴留意到了,这几个注解中都有这么一段代码

    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

@AliasForSpring 结构的注解,它答应你在一个注解特点上声明别号。在 Spring 的许多核心注解中,@AliasFor 用于声明一个或多个别号特点。

举个比如,在 @Controller, @Service, @Repository注解中,value() 办法上的 @AliasFor 声明晰一个别号特点,它的目标示解是 @Component,详细的别号特点是 value。也便是说,当咱们在 @Controller, @Service, @Repository 注解上运用 value() 办法设置值时,实际上也就相当于在 @Component 注解上设置了 name 特点的值。同时,这也标明晰 @Controller, @Service, @Repository注解本身便是一个特别的 @Component

7. 将注解驱动的装备与XML驱动的装备结合运用

有没有这么一种或许,一个旧的Spring项目,里边有许多旧的XML装备,现在你接手了,想要全部用注解驱动,不想再写XML装备了,那应该怎样兼容呢?

假定咱们有一个旧的Spring XML装备文件 old-config.xml

<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">
    <bean id="oldBean" class="com.example.OldBean" />
</beans>

这个文件界说了一个名为 “oldBean” 的bean

然后,咱们编写一个新的注解驱动的装备类:

package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource("classpath:old-config.xml")
public class NewConfig {
    @Bean
    public NewBean newBean() {
        return new NewBean();
    }
}

在这个新的装备类中,咱们运用 @ImportResource 注解来引进旧的XML装备文件,并界说了一个新的beannewBean”。@ImportResource("classpath:old-config.xml")告知Spring在初始化AppConfig装备类时,去类途径下寻觅old-config.xml文件,并加载其间的装备。

当咱们发动运用程序时,Spring会创立一个 ApplicationContext,这个 ApplicationContext 会包括 old-config.xml 文件中界说的一切beans(例如 “oldBean”),以及 NewConfig 类中界说的一切beans(例如 “newBean”)。

package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(NewConfig.class);
        OldBean oldBean = context.getBean("oldBean", OldBean.class);
        NewBean newBean = context.getBean("newBean", NewBean.class);
        System.out.println(oldBean);
        System.out.println(newBean);
    }
}

在以上的main办法中,咱们经过运用AnnotationConfigApplicationContext并传入NewConfig.class作为参数,初始化了一个Spring上下文。在这个上下文中,既包括了从old-config.xml导入的bean,也包括了在NewConfig装备类中运用@Bean注解界说的bean

所以,经过运用 @ImportResource,能够在新的注解装备中引进旧的XML装备,这样就能够在不打断旧的XML装备的基础上逐渐迁移至新的注解装备。

上面咱们提到类途径,什么是类途径?

resources目录便是类途径(classpath)的一部分。所以当咱们说”类途径下”的时分,实际上也包括了”resources“目录。JVM在运转时,会把”src/main/resources“目录下的一切文件和文件夹都增加到类途径中。

例如有一个XML文件坐落”src/main/resources/config/some-context.xml“,那么能够用以下办法来引用它:

@Configuration
@ImportResource("classpath:config/some-context.xml")
public class AppConfig {
    //...
}

这儿能够描绘为在类途径下的’config‘目录中查找’some-context.xml‘文件。

为什么说JVM在运转时,会把”src/main/resources“目录下的一切文件和文件夹都增加到类途径中?

当你编译并运转一个Java项目时,JVM需求知道去哪里查找.class文件以及其他资源文件。这个查找的方位便是所谓的类途径(Classpath)。类途径能够包括文件体系上的目录,也能够包括jar文件。简略的说,类途径便是JVM查找类和资源的当地。

在一个标准的Maven项目结构中,Java源代码一般在src/main/java目录下,而像是装备文件、图片、静态网页等资源文件则放在src/main/resources目录下。

  当你构建项目时,Maven(或许其他的构建东西,如Gradle)会把src/main/java目录下的.java文件编译成.class文件,并把它们和src/main/resources目录下的资源文件一起复制到项目的输出目录(一般是target/classes目录)。

深入理解注解驱动配置与XML配置的融合与区别

深入理解注解驱动配置与XML配置的融合与区别

然后当你运转程序时,JVM会把target/classes目录(即编译后的src/main/javasrc/main/resources)增加到类途径中,这样JVM就能够找到程序运转所需的类和资源了。

假如有一个名为application.properties的文件在src/main/resources目录下,就能够运用类途径来拜访它,就像这样:classpath:application.properties。在这儿classpath:前缀告知JVM这个途径是相关于类途径的,所以它会在类途径中查找application.properties文件。由于src/main/resources在运转时被增加到了类途径,所以JVM能找到这个文件。

8. 考虑总结

8.1 为什么咱们需求注册组件,这与Bean注册有什么差异?

Spring结构中,Bean目标是由Spring IoC容器创立和办理的。一般Bean目标是运用程序中的事务逻辑组件,如数据拜访目标(DAO)或其他服务类。

组件注册,或许说在Spring中经过@Component或许其派生注解(@Service, @Controller, @Repository等)符号的类,是告知Spring结构这个类是一个组件,Spring需求创立它的实例并办理它的生命周期。这样当运用到这个类的时分,Spring就能够主动地创立这个类的实例并注入到需求的当地。

Bean注册和组件注册其实是非常相似的,都是为了让Spring知道它需求办理哪些类的实例。差异在于Bean注册一般产生在装备类中,运用@Bean注解来明晰地界说每一个Bean,而组件注册则是经过在类上运用@Component或许其派生注解来告知Spring,这个类是一个组件,Spring应该主动地为其创立实例。

8.2 什么是组件扫描,为什么咱们需求它,它是怎样作业的?

组件扫描是Spring的一种机制,用于主动发现运用程序中的Spring组件,并主动地为这些组件创立Bean界说,然后将它们注册到Spring的运用上下文中,咱们能够经过运用@ComponentScan注解来发动组件扫描。

咱们需求组件扫描是由于它能够大大简化装备进程,咱们不再需求为运用程序中的每个类都显式地创立Bean。而是经过简略地在类上增加@Component或许其派生注解,并发动组件扫描,就能够让Spring主动地为咱们的类创立Bean并办理它们。

组件扫描的作业进程如下:运用@ComponentScan注解并指定一个或多个包途径时,Spring会扫描这些包途径及其子包中的一切类。关于符号了@Component或许其派生注解的类,Spring会在运用上下文发动时为它们创立Bean,并将这些Bean界说注册到Spring的运用上下文中。当需求运用这些类的实例时,Spring就能够主动注入这些实例。

点击重视,第一时间了解华为云新鲜技能~

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。