编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

Spring学习Bean配置的三种方式(XML、注解、Java类)介绍与对比

gowuye 2024-04-04 11:54 6 浏览 0 评论

本文将详细介绍Spring Bean配置的三种不同方式的特点与使用条件。

主要包括:

  • 基于XML的配置方式
  • 基于注解的配置方式
  • 基于Java类的配置方式

一.基于XML的配置

请看Spring学习(十二)Spring 的配置文件概述

二.基于注解的配置

Spring2.0开始引入基于注解的配置方式,即Bean的定义信息可以通过在Bean的实现类上标注注解实现。

具体关于注解的知识请看Java注解(Annotation)介绍

@Component是Spring容器中的基本注解,表示容器中的一个组件(bean),可以作用在任何层次,下面的示例介绍该注解的使用方法。

注解配置示例:


  1. @Component("userDao")
  2. public class userDao{......}

他等效于XML配置

<bean id="userDao" class="cn.lovepi.***.userDao"/>

此外,还有一些其他的可以被用来注解bean的注解,这些可以让注解类本身的用途更加清晰,此外,特定的注解也具备特定的功能。

Spring在2.5后提供了一个context的命名空间,它提供了通过扫描类包来加载利用注解定义的Bean的方式。

在context中可以使用resource-pattern来过滤出特定的类。

<context:component-scan base-package="cn.lovepi.spring" resource-pattern="anno/*.class"/>

默认情况下加载的是package下的*.class即扫描全部类,在使用了resource-pattern之后,则只扫描package下的anno子包下的所有类。

不过使用resource-pattern并不能提供给我们完善的功能,所有我们得使用过滤子元素的方法。


  1. <context:component-scan base-package="cn.lovepi.spring">
  2. <context:include-filter type="regex" expression="cn.lovepi.spring.*"/>
  3. <context:exclude-filter type="aspectj" expression="cn.lovepi..*Controller+"/>
  4. </context:component-scan>

其中:

include-filter表示要包含的目标类,

exclude-filter表示要排除在外的目标类

一个component-scan标签下可以有多个include-filter和exclude-filter,

过滤表达式所支持的类型如下表所示:

在这些类型当中,除了Custom外,aspectj的过滤功能最强大,他能轻易的实现其他类别的过滤规则。

Spring3.0提供了一系列的针对依赖注入的注解,这使得Spring IoC在XML文件之外多了一种可行的选择,主要包含如下注解类型:

  • Bean的定义注解
  • Bean的生命周期注解
  • Bean的依赖检查注解
  • Bean的自动装配注解

1.Bean的定义注解

Spring自2.0开始,陆续引入了一些注解用于简化Spring的开发。

@Repository注解便属于最先引入的一批,用于将数据访问层(DAO层)的类标识为Spring Bean。具体使用如下:

①首先使用@Repository将DAO类声明为Bean


  1. @Repository
  2. public class UserDaoImpl implements UserDao{......}

②在XML配置文件中启动Spring的自动扫描功能


  1. <beans ...>
  2. <context:component-scan base-package="cn.lovepi.dao"/>
  3. ......
  4. <beans/>

如此的话,我们便不在需要在XML当中显式使用bean来进行bean的配置。Spring容器在初始化的时候便会自动扫描base-package所指定的包以及子包下面的所有class文件。所有标注为Repository的类将被自动注册为bean。

为什么Repository只能标注在DAO类上面呢?

因为该注解的作用不只是将类识别为bean,同时他还能将所标注的类中所抛出的数据访问异常封装为Spring的数据访问异常类型。Spring本身提供了一个丰富的,并且是与具体的访问技术无关的数据访问异常结构,用于封装不同的持久层框架所抛出的异常,使得异常独立与底层的框架。

Spring2.5在@Repository的基础上增加了功能类似的额外三个注解,总共有如下四种注解:

  • @Component:一个泛化的概念,表示一个组件(Bean),可作用在任何层次
  • @Controller:用于对Controller实现类进行标注,目前该功能与Component相同
  • @Repository:用于对DAO实现类进行标注
  • @Service:用于对Service实现类进行标注,目前该功能与Component相同

这三个注解除了作用于不同软件层次的类,其使用方式与Repository是完全相同的。

2.Bean的生命周期注解

在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,例如资源的获取和是否操作,Spring1.x为此提供了两种方式供用户指定执行生命周期回调的方法:

  1. 实现Spring提供的两个接口:initializingBeanDisposableBean,这种方法是要求bean类实现Spring的接口,但增加了bean和Spring容器的耦合度,因此不推荐使用。
  2. 在XML文件中使用<bean>的init-methoddestory-method 属性,指定初始化之后和回调之前的回调方法。这两个属性的取值是bean中相应的初始化和销毁方法的名称。方法名称任意,但是方法不能有参数。

示例如下:


  1. <bean id="userService" class="cn.lovepi.***.UserService"
  2. init-method="init" destory-method="destory">
  3. </bean>

在这里,我们指定了userService 这个bean的初始化方法为:init 销毁方法为:destory

Spring2.5在保留以上两种方式的基础上,提供了对JSR-250的支持。

JSR-250规范定义了两个用于指定声明周期方法的注解:

  • @PostConstruct:初始化之后的执行的回调方法
  • @PreDestroy:销毁之前的回调方法

注解示例说明:


  1. public class PersonService{
  2. @PostConstruct
  3. public void init(){......}
  4. @PreDestory
  5. public void destory(){......}
  6. }

在这里init方法是初始化之后执行的方法,而destory方法为销毁之前执行的方法

由于使用了注解,所以得激活Bean的后处理器,所以得在XML配置文件当中增加

<context:annotation-config/>

3.Bean的依赖检查注解

Spring2.0之前使用dependency-check在配置文件中设置属性用于依赖检查(只会检查Setter方法是否被调用),缺点是粒度较粗,该属性的取值包括以下几种:

  • none: 默认不执行依赖检查
  • simple :对原始基本类型和集合类型进行检查
  • objects :对复杂类型进行检查
  • all :对所有类型进行检查

使用Spring2.0提供的@Required注解,提供了更细粒度的控制,@Required注解只能标注在Setter方法之上,(标注在其他方法之上会被忽略 )用于检查其是否被调用,当Setter方法未被调用的话会抛出异常

由于使用了注解,所以得激活Bean的后处理器,所以得在XML配置文件当中增加

<context:annotation-config/>

4.Bean的自动装配注解

@Autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,他根据类型进行自动装配,如果需要按名称进行装配,则需要配合@Qualifier使用。

当标注了@Autowired的方法所需的类型在Spring容器中不存在的话会抛出异常


  1. @Service
  2. public class LoginService{
  3. @Autowired
  4. private LogDao logDao;
  5. }

如上面的例子所示,假如Spring容器中没有LogDao这个bean的话便会抛出异常。

解决的办法便是使用required=false属性来标注


  1. public class LoginService{
  2. @Autowired(required=false)
  3. private LogDao LogDao;
  4. }

但是假如Spring当中存在多个所需类型的bean,那么便要使用@Qualifier注解来指定名称。


  1. public class LoginService{
  2. @Autowired
  3. @Qualifier("userDao")
  4. private UserDao userDao;
  5. }

@Autowired 可以对类中集合类的变量或方法入参进行标注,此时会将容器中类型匹配的所有Bean都注入进来,如下所示:


  1. public class loginService{
  2. @Autowired(required=false)
  3. public List<Plugin> pligins;
  4. public List<Plugin> getPlugins(){
  5. return plugins;
  6. }
  7. }

Spring会将容器中所有类型为Plugin的bean都注入到集合中去。

三.基于Java类的配置

基于Java类定义Bean配置元数据,其实就是通过Java类定义Spring配置元数据,且直接消除XML配置文件。

首先让我们看一下基于Java类如何定义Bean配置元数据,具体步骤如下:

  1. 使用@Configuration注解需要作为配置的类,表示该类将定义Bean的元数据
  2. 使用@Bean注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。
  3. AnnotationConfigApplicationContext或子类进行加载基于java类的配置

接下来通过示例来演示下如何基于Java类来配置Spring

首先创建一个配置类


  1. @Configuration
  2. public class ApplicationContextConfig {
  3. @Bean
  4. public String message() {
  5. return "hello";
  6. }
  7. }<strong>
  8. </strong>

然后还需要一个测试类,来查看配置是否成功


  1. public class ConfigurationTest {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext ctx =
  4. new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
  5. System.out.println(ctx.getBean("message"));
  6. }
  7. }

通过@Configuration注解的类将被作为配置类使用,表示在该类中将定义Bean配置元数据,且使用@Configuration注解的类本身也是一个Bean,使用方式如下所示:


  1. @Configuration("ctxConfig")
  2. public class ApplicationContextConfig {
  3. ……
  4. }

其中Configuration中的参数值即为该bean的名称

通过@Bean注解配置类中的相应方法,则该方法名默认就是Bean名,该方法返回值就是Bean对象,并定义了Spring IoC容器如何实例化、自动装配、初始化Bean逻辑,具体使用方法如下:


  1. @Bean(name={},
  2. autowire=Autowire.NO,
  3. initMethod="",
  4. destroyMethod="")

其中name为bean的名称,可以有多个,autowire为是否自动装配,默认值为NO,initMethod为bean的初始化方法,destoryMethod为bean的销毁方法。

bean的注解具体使用如下:


  1. @Bean
  2. public String message() {
  3. return new String("hello");
  4. }

如上的代码等价与XML配置:


  1. <bean id="message" class="java.lang.String">
  2. <constructor-arg index="0" value="hello"/>
  3. </bean>

注意使用bean注解的方法不能是private、final、static的。

基于Java方式的配置方式不是为了完全替代基于XML方式的配置,两者可以结合使用,因此可以有两种结合使用方式:

  • 在基于Java方式的配置类中引入基于XML方式的配置文件
  • 在基于XML方式的配置文件中中引入基于Java方式的配置

引入基于XML配置文件:


  1. <bean id="message" class="java.lang.String">
  2. <constructor-arg index="0" value="test"></constructor-arg>
  3. </bean>
  4. @Configuration("ctxConfig")
  5. @ImportResource("classpath:com/jike/***/appCtx.xml")
  6. public class ApplicationContextConfig {
  7. ……
  8. }

可以看到在java程序中使用@ImportResource导入了XML的配置文件

引入基于Java的配置文件:


  1. <context:annotation-config/>
  2. <bean id="ctxConfig" class=“com.jike.***..ApplicationContextConfig"/>
  3. //测试类
  4. public void testXmlConfig() {
  5. String configLocations[] = {" classpath:com/jike/***/appCtx.xml"};
  6. ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);
  7. ……
  8. }

可以看到在XML的配置文件当中将java的配置类当中Bean来声明,第一行的是开启注解驱动支持。

值得注意的是必须得配置<context:annotation-config/>在XML配置文件中。

Spring提供了一个AnnotationConfigApplicanContext类,能够直接通过标注@Configuration的Java类启动Spring容器:

通过构造函数加载配置类:

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);

通过编码方式注册配置类:


  1. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  2. ctx.register(DaoConfig.class);
  3. ctx.register(ServiceConfig.class);
  4. ctx.refresh();

可以看到ctx注册了多个configuration类,然后通过refresh类来刷新容器以应用这些配置文件。

可以通过代码一个个的引入配置类,当然也可以使用@Import注解来引入配置类

引入多个配置类:


  1. @Configuration
  2. @Import(DaoConfig.class)
  3. public class ServiceConfig {……}

总结:不同配置方式比较

我们来看一下不同配置方式在不同方面的使用

其实Spring支持这么多的配置方式,那么这些配置方式必然有其自己独特的舞台

基于XML的配置主要使用场景:

  • 第三方类库,如DataSource、JdbcTemplate等;
  • 命名空间,如aop、context等;

基于注解的配置主要使用场景:

  • Bean的实现类是当前项目开发的,可直接在Java类中使用注解配置

基于Java类的配置主要使用场景

  • 对于实例化Bean的逻辑比较复杂,则比较适合用基于Java类配置的方式

在日常的开发中我们主要是使用XML配置注解配置方式向结合的开发方式,一般不推荐使用基于Java类的配置方式。

相关推荐

爱上开源之golang入门至实战第四章-切片(Slice)

前言Go数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可...

Go语言入门必知教程-切片

切片是一种灵活的和可扩展的数据结构,用于实现和管理数据集。切片由多个元素组成,所有元素都是相同类型的。切片是动态数组的一部分,可以根据需要进行增长和收缩。与数组一样,切片也可以索引。切片具有容量和长度...

Go语言基础-切片

切片是什么?切片是Go语言的一种数据结构。和数组相似,不过切片可以在它的结尾增加更多的元素。这样可变长度在实际编程中更为有用。声明切片切片的声明和数组也很相似,只是声明切片时不需要指定大小。例:va...

5分钟掌握GO中切片的基本使用方法

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

揭秘 Go 切片(Slice)的秘密

当向切片添加新参数时,底层数组会发生什么变化?它会扩展以容纳更多元素吗?在这篇文章中,我们将深入探讨切片的内部工作原理,以及如何利用这些知识来进行更好的内存管理和性能优化。具体而言,我们将探索Go...

【Go语言slice详解】深入掌握Go语言中的slice类型及常用操作!

Go语言中的slice(切片)是一种非常方便的数据结构,可以动态地增加或减少其元素数量,且可以访问底层数组的任意一个子序列。本文将对Go语言中的slice进行详细的讲解。Slice的定义在Go语言中,...

掌握GO中的Slice,这就够了

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

golang2021面向对象(26)Go语言类型内嵌和结构体内嵌

结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体。?可以粗略地将这个...

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内的指针方法列表?以下代码应该返回{"S1":["M1","M2"],"S...

Go语言文件和目录操作

文件和目录操作概述一、文件和目录操作概述在计算机中,文件和目录是存储数据的重要方式。在Go语言中,我们可以使用os和io/ioutil包提供的函数和结构体来进行文件和目录操作。本文将详细介绍Go语言中...

跟我一起学习go语言(五)golang中结构体的初始化方法

1、自定义一个结构体typeVertexstruct{X,Yfloat64}2、初始化方法-指针:rect1:=new(Vertex)rect2:=&Vertex...

Go复合数据类型:结构体

一种通用的、对实体对象进行聚合抽象的能力,在Go中,提供这种聚合抽象能力的类型是结构体类型,也就是struct。自定义一个新类型在Go中,我们自定义一个新类型一般有两种方法。第一种是类型定义...

Go语言基础:方法

导读在阅读本文章前,假定你具备如下能力:?已掌握结构体1.方法1.1方法的概念在理解程序中方法的概念时,我们先看看现实中的一些情况,这样相对比较好理解一些。在农村的朋友可能会知道,在医疗落后的情况...

为什么 Go 语言 struct 要使用 tags

在Go语言中,struct是一种常见的数据类型,它可以用来表示复杂的数据结构。在struct中,我们可以定义多个字段,每个字段可以有不同的类型和名称。除了这些基本信息之外,Go还提供了s...

一文带你掌握掌握 Golang结构体与方法

1.Golang结构体的概念及定义结构体是Golang中一种复合类型,它是由一组具有相同或不同类型的数据字段组成的数据结构。结构体是一种用户自定义类型,它可以被用来封装多个字段,从而实现数据的...

取消回复欢迎 发表评论: