Spring【五】基于注解和代码的配置

目录

一、Java注解

1.Java基本注解

2.Java注解标准

2.1 Java平台的公共注解标准(JSR-250)

2.2 依赖注入的注解标准(JSR-330)

2.3 软件缺陷检测注解标准(JSR-305)

二、Spring支持的注解类型与开启方式

1.Spring支持的注解类型

2.Spring注解功能的开启方式

2.1 配置实现注解功能的BeanPostProcessor的Bean

2.2 使用标签

2.3 配置标签

3.Spring支持的Java标准注解

3.1 @PostConstruct和PreDestroy

3.2 @Named---组件注解

3.3 @Resource---依赖注入的注解(重要)

3.4 @Inject---依赖注入

4.Spring容器配置注解

4.1 @Component(组件注解)

4.2 @Bean(方法层级的组件注解)

4.3 @Autowired(依赖对象的自动装配)

4.4 @Primary(依赖的主候选)

4.5 @Qualifier(精确查找依赖)

4.6 @Order(同类型的集合注入顺序)

5.基于Java代码的配置

5.1 @Configuration(配置类注解)

5.2 Java代码配置的容器初始化

5.3 @ComponentScan(组件扫描注解)

5.4 @Import(配置类导入注解)

5.5 @Conditional(条件判断注解)

5.6.@ImportResource(导入配置文件)

6.Spring容器注解汇总与说明


使用XML文件配置是Spring最早的配置方式,大中型项目依据功能或不同的命名空间拆分成多个配置文件,每个配置文件的配置内容都可能很多,导致配置文件的维护工作量大,也容易出错。从Spring2.5开始,在以XML文件作为主要配置的同时,可以将某些配置以注解的形式在代码中直接配置,极大地减少了配置地繁琐度,提高了配置地效率,Java开发人员也更容易熟悉和适应。从Spring3.0开始可以完全脱离XML文件,使用Java代码地方式进行容器和框架地配置。

一、Java注解

注解(Annotation)是Java SE 5.0开始引入地概念,与类(Class)和接口(Interface)一样,也属于一种类型。

1.Java基本注解

注解是一种可以对应于类、方法、参数、变量、构造器及包地特殊修饰符,是源代码地元数据。区别于注释对源码地描述给人以提示,注解是Java编译器或代码用来读取和理解的。Java从JDK5开始引入注解,位于java.lang.annotation中,最早引入的基本注解如下:

  1. @Override:注解在方法上,说明当前方法是覆盖超类的方法;
  2. @Deprecated:弃用或者不建议使用的代码;
  3. @SuppressWarnings:忽略编译器的警告。

以上注解主要用于编译检查,也可以自定义注解。在Java中,注解与类、接口和枚举是在同一个层次,所以可以像定义接口一样来定义注解。Java提供了4种用于创建注解的注解,称之为元注解,分别如下:

  1. @Documented:注解是否包含在JavaDoc中;
  2. @Retention:什么时候使用该注解,取值如下:
    1. RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    2. RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
    3. RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,那么在运行时就可以通过反射访问
  3. @Target:注解可以标注在什么地方,常用取值如下:
    1. ElementType.TYPE:可以标注"类、接口(包括注释类型)或枚举声明";
    2. ElementType.FIELD:它可以标注"字段声明";
    3. ElementType.METHOD:它可以标注"方法";
    4. ElementType.PARAMETER:它可以标注"参数";
    5. ElementType.CONSTRUCTOR:它可以标注"构造方法";
    6. ElementType.LOCAL_VARIABLE:它可以标注"局部变量"。
  4. @Inherited:是否允许子类继承该注解。

注解通过@interface关键字进行定义。注解可以像类属性和方法一样,通过反射获取,结合反射就可以对标注注解的类和方法进行功能扩展。使用示例:

@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD })
public @interface MyAnnotation {
        //这里的value是参数名称,String[]是参数类型, unknown是参数的默认值。
	String[] value() default "unknown";
}

当注解只有一个参数,且参数名称为value,那么在使用注解时可以省略参数名称

@MyAnnotation(value = { "类上的注解value" })
public class AnnotationDemo {
    @MyAnnotation    //这里不指定参数,则使用默认参数值
    private String field;

    //参数类型为String[],当有多个值的时候,需要使用{},当只有一个值的时候,可以省略{}
    @MyAnnotation(value = "This is a method")    
    private void method() {}

    public static void showMyAnnotationValue(AnnotatedElement element) {
        if (element == null) {
            return;
        }
        if (element.isAnnotationPresent(MyAnnotation.class)) {
	    MyAnnotation myAnnotation = element.getAnnotation(MyAnnotation.class);
	    String[] valueArr = myAnnotation.value();
	    System.out.println(Arrays.toString(valueArr));    
        }
    }

    public static void main(String[] args) {
        Class<?> klass = AnnotationDemo.class;
        showMyAnnotationValue(klass);

        Field[] fields = klass.getDeclaredFields();
        for (Field field : fields) {
	    showMyAnnotationValue(field);
	}

	Method[] methods = klass.getDeclaredMethods();
	for (Method method : methods) {
	    showMyAnnotationValue(method);
	}
    }
}

上面示例中获取注解的信息是在showMyAnnotationValue方法中,该方法用了一个AnnotatedElement接口类型,该接口在java.lang.reflect下,用于注解信息的获取。

2.Java注解标准

JCP官方除了在JDK中提供并实现了@Override等基本注解和功能之外,还规范了一系列的注解标准,可以由其他容器或框架去实现。

2.1 Java平台的公共注解标准(JSR-250)

JSR-250定义的标准注解主要如下:

  • @Resource:声明对资源的引用(类似于数据库资源);
  • @PostConstruct:使用在Servlet上,在init()方法之前执行;
  • @PreDestroy:使用在Servlet上,在destroy()方法之后执行。

以上注解定义的注解类文件位于JRE的rt.jar中,对应目录是javax.annotation。JSR-250定义的注解规范基本是关于“资源”的构建、销毁和使用。

2.2 依赖注入的注解标准(JSR-330)

JSR-330是JCP官方定义的依赖注入的注解标准。相关注解定义文件位于javax.inject.jar中,这个jar包没有包含在JDK中,需要额外下载或使用Maven导入。其主要定义了5个注解和1个接口,如下:

  • @Inject:标识需要由注入器注入的类成员,用于类的构造器、方法和属性上,可以于Spring依赖配置结合使用;
  • @Qualifier和@Named:限制器,用于限制注入依赖的类型;
  • @Scope和@Singleton:定义作用域;
  • Provider接口用于提供类型T的实例。

2.3 软件缺陷检测注解标准(JSR-305)

JSR-305是软件缺陷检测注解标准,该注解不是来自Java官方标准,而是来自Java代码的静态分析工具的使用需求。静态分析工具开发人员可以通过注解定义代码的健壮性,例如那些值不能为空,哪些值不能为负。该标准包括@Nonnull和@Nullable等注解。

  • @Nonnull:注解的元素不能为null。用在属性上,表示在对象构造完成之后,属性的值不能为空;用在方法上,表示方法的返回值不能为空。
  • @Nullable:注解的元素可以为空。

二、Spring支持的注解类型与开启方式

1.Spring支持的注解类型

下面的注解类型基本是限定在容器配置方面的注解。

  1. 组件注解有Spring提供的@Component、@Controller和@Service等,也有Java标准注解@Resource、@Name等。
  2. 依赖注解有Spring提供的@Autowired、@Lookup和@Value,也有Java标准注解@Inject等。

从实现方式上来看,有的注解是在容器初始化的时候完成的,比如@Component等组件注解;有的注解是通过BeanPostProcessor的容器扩展方式来实现的。通过BeanPostProcessor扩展实现的注解,根据其对应的注解处理类,包含以下4种:

  • Java公共注解:注解的处理类是CommonAnnotationBeanPostProcessor,提供对Java平台公共注解标准JSR-250的实现,包括@PostConstruct、@PreDestroy和@Resource等。
  • 自动装配注解:注解的处理类是AutowiredAnnotationBeanPostProcessor,用来进行依赖注入的注解,有对官方注解标准JSR-330的支持(比如@Inject、@Named等,从Spring3.0开始支持),也有Spring自行定义的注解(@Autowired、@Value和Lookup)。
  • 非空检查注解:注解的处理类是RequiredAnnotationBeanPostProcessor,对依赖进行检查的注解@Required的功能实现。
  • 持久化注解:注解的处理类是PersistenceAnnotationBeanPostProcessor,用来处理对象关系映射及注解JPA资源EntityManagerFactory和EntityManager,支持@PersistenceUnit和@PersistenceContext等JPA注解。

2.Spring注解功能的开启方式

Spring提供或实现的容器配置注解,在默认情况下使用并不会立即生效,需要配置相关的处理类进行处理。Spring提供了三种方式开启此类型注解的功能,分别是配置实现注解功能的BeanPostProcessor的Bean、使用<context:annotation-config>标签和配置<context:component-scan>标签。

2.1 配置实现注解功能的BeanPostProcessor的Bean

对于使用容器扩展点BeanPostProcessor实现的注解来说,注解功能的开启,只需要在配置文件中加上注解处理类的Bean配置。以Java公共注解为例,在配置文件中加入以下配置:

<bean class="org.springframework.beans.factory.annotation.CommomAnnotationBeanPostProcessor"></bean>

AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor和PersistenceAnnotationBeanPostProcessor的配置方式也与上述相同,但需注意使用正确包名。

2.2 使用<context:annotation-config>标签

上述方式配置比较麻烦,为了简化配置,Spring提供了一种更便捷的方式开启注解功能,在配置文件中加入一行即可开启4中注解的支持:

<context:annotation-config />

注意,在使用context标签时,需要在<beans>根元素中添加context的命名空间等,如下:

//指定beans根元素属性:
xmlns:context="http://www.springframework.org/schema/context"
//在xsi:schemaLocation属性中添加:
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd

这个标签实质上是一次性在容器中注册以上4种PostProcessor的Bean

2.3 配置<context:component-scan>标签

前面两种方式并不会开启@Component等组件的注解功能。要开启此类型注解,需要进行包扫描配置,示例配置如下:

<context:component-scan base-package="com.mec.spring.annotation" />

以上配置的base-package属性用于设置扫描的路径,多个路径间使用逗号分隔。使用该配置,容器对该包以及子包下标注@Component注解的类进行实例化。除了扫描和处理@Component注解外,context:component-scan默认还会注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor,也就是开启@Autowired、@Required和@Resource等注解功能,这和配置context:annotation-config的作用时类似的,如果配置了component-scan,就不需要增加annotation-config的配置了。此外,component-scan也可以通过设置属性annotation-config的值为false禁用@Autowired、@Required和@Resource等注解功能,而仅处理@Component等组件注解

3.Spring支持的Java标准注解

Spring提供了对Java标准注解的支持和实现,包含生命周期回调注解@PostConstruct和@PreDestroy,以及组件注解@Named和依赖注入注解@Inject及@Resource。

3.1 @PostConstruct和PreDestroy

从Java 5开始,Servlet增加了两个生命周期的注解:@PostConstruct和@PreDestroy这两个注解可以用来修饰一个非静态的void方法,注解相关的类位于JRE和rt.jar中。Spring提供了对这两个注解的实现:

  1. 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,@PostConstruct注解的方法会在Bean的构造函数之后,init()方法执行之前执行;
  2. 被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,@PreDestroy注解的方法在destroy()方法执行之后执行。

3.2 @Named---组件注解

@Inject和@Named是JSR-330定义的依赖注入的标准注解。与@PostConstruct、@PreDestroy和@Resource不同,这两个注解没有在JRE中,需要额外导入,Maven配置如下:

<dependency>    
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

@Named使用在属性和参数上,作用是根据名字查找容器中对应的对象;也可以使用在类上,用于对该类进行组件的标注,功能类似于在XML文件中配置Bean。使用方式如下:

@Named("namedBeanAnno") // 定义bean名称
public class NamedBeanAnno {
}

3.3 @Resource---依赖注入的注解(重要)

@Resource是JSR-250的注解,用来标注系统或容器中资源类型的对象引用,包括持久层访问对象资源(DAO)、文件或容器等资源。@Resource注解定在rt.jar中。Spring支持这个注解来引用被Spring容器管理的对象,包括自定义的Bean实例和容器对象@Resource可以使用在属性属性的setter方法,使用示例如下:

public class ResourceAnno {
	@Resource // 使用在属性上,注入自定义的Bean
	private NamedBeanAnno namedBeanAnno;
	@Resource // 使用在属性上,注入容器对象
	private ApplicationContext applicationContext;
	private User user;

	@Resource // 使用在方法上,注入参数定义的对象
	public void setUser(User user) {
		this.user = user;
	}
}

@Resource注解的属性或者setter方法,默认会以属性名或setter方法的参数名去查找容器中的对象,如果没找到,则使用类来进行查找和注入。也可以显式地使用属性name来查找指定名称地Bean实例,如下:

@Resource(name = "namedBeanAnno")
private NamedBeanAnno namedBeanAnno;

3.4 @Inject---依赖注入

@Inject可以使用在构造函数属性属性的setter方法上,用来注入依赖对象,使用示例如下:

public class InjectAnno {
	private NamedBeanAnno namedBeanAnno;
	@Inject // 属性注入依赖对象
	private User user;
	private Person person;

	@Inject // 构造函数注入依赖对象
	public InjectAnno(NamedBeanAnno namedBeanAnno) {
		this.namedBeanAnno = namedBeanAnno;
	}

	@Inject // setter方法注入依赖对象
	public void setPerson(Person person) {
		this.person = person;
	}
}

4.Spring容器配置注解

除了对Java标准注解的支持和实现外,Spring自身也提供了诸多的容器注解,包括组件注册的@Component以及其子注解@Bean注解、依赖自动注入的@Autowired注解、依赖项检查的注解@Required(该注解用在属性的setter方法上,用来判断该属性是否被注入,如果没有则会异常提示,该注解在Spring5中被弃用

4.1 @Component(组件注解)

虽然Spring支持Java标准注解@Named@ManagedBean来注解组件,但使用Spring本身的@Component注解组件更加常见一些。在类中使用@Component注解(该注解有一个value属性,用于指定bean名称,默认情况下bean名称为首字母小写的类名),容器可以在启动时进行该类的实例化。@Component是通用的组件注解,在Java Web开发中对应不同的组件层级,Spring在@Component元注解的基础上定义了不同类型的子注解,常用的包括@Controller、@Service、@Repository,分别对应控制层、业务层数据访问层的组件。相比@Component,这三个注解提供了对应层级的一些附加功能,比如@Repository提供了持久层处理异常的自动转换。

通过配置<context:component-scan>即可开启@Component的注解功能,默认情况下会扫描base-package包下所有的@Component和子注解(@Controller、@Service和@Repository)标注的类进行实例化并注册。如果需要对扫描和注册的类及注解做一些过滤,有两种方式可以做到,分别是使用<context:exclude-filter>子标签,以及使用<context:include-filter>子标签use-default-filters属性。

1.<context:exclude-filter>组件扫描的排除过滤

<context:exclude-filter>用于排除组件扫描的条件。比如,如果不需要扫描某个包下的@Controller主解的类,配置如下:

<context:component-scan base-package="com.mec.spring.annotation,com.mec.spring.event">                                
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

2.<context:include-filter>子标签结合use-default-filters实现组件扫描的包含过滤

use-default-filters属性是<context:component-scan>标签可以配置的属性,默认值为true。如果只想扫描某种类型的注解,可以先将use-default-filters属性值设置为false(这个必须设置),之后再进行包含的过滤条件<context:include-filter>的配置。比如,若只想扫描@Repository注解的类,可以使用如下方式:

<context:component-scan base-package="com.mec.spring.annotation,com.mec.spring.event" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>

<context:include-filter><context:exclude-filter>标签有两个主要的属性:type和expression。type用于指定过滤类型,除了注解类型(annotation)之外,还支持assignable、regex、aspectj和custom这4种过滤类型。expression的值对应的是不同类型过滤器的匹配表达式,包含annotation类型在内的5种过滤器类型和表达式的描述如下:

  • annotation:对应expression的值设置为注解类的全路径名。
  • assignable:expression的值设置为类和接口的全路径名,如下:
<context:exclude-filter type="assignable" expression="com.mec.spring.annotation.InjectAnno"/>

 

  • regex:表达式可以使用正则表达式匹配包的路径或类。例如,对某个包下所有类都不扫描:
<context:exclude-filter type="regex" expression="com.mec.spring.annotation.*"/>
  • aspectj:表达式使用AspectJ类型表达式匹配包或类。
  • custom:表达式使用自定义的过滤器类,这个类需要继承org.springframework.core.type.filter.TypeFilter接口,如下:
public class MyTypeFilter implements TypeFilter {

	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// 获取类元数据
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		String className = classMetadata.getClassName();
		if (className.contains("InjectAnno")) {
			return true;
		}
		return false;
	}
}

定义好之后进行如下配置:

<!--只扫描MyTypeFilter的match方法返回true的-->
<context:component-scan base-package="com.mec.spring.annotation,com.mec.spring.event" use-default-filters="false">
    <context:include-filter type="custom" expression="com.mec.spring.annotation.MyTypeFilter"/>
</context:component-scan>
<!--排除match方法返回true的-->
<context:exclude-filter type="custom" expression="com.mec.spring.annotation.MyTypeFilter"/>

4.2 @Bean(方法层级的组件注解)

@Component及其子注解是使用在类层级的组件注解,也可以在类方法上使用@Bean注解来注册Bean,例如:

@Component
public class ServiceMedol {
    @Bean
    public User method() {
        User user = new User();
        user.setMailbox("123");
        return user;
    }
}

@Bean注解定义如下:

public @interface Bean {
    @AliasFor("name")
    String[] value() default {};
    @AliasFor("value")
    String[] name() default {};
    @Deprecated
    Autowire autowire() default Autowire.NO;
    boolean autowireCandidate() default true;    //用于指定是否作为其他对象注入时候的候选bean
    String initMethod() default "";
    String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

@Bean注解的方法需要有非空的返回类型,返回的对象就是注册Bean的对象,@Bean注解的方法不能是private或final的。该注解只有在其方法对应的类被注册为Bean的状况下才有效(可以通过XML配置或@Component注解配置)。默认情况下,@Bean注解方法注册的Bean的id是方法名。可以使用name属性指定名称,value属性指定别名。name和value可以单独使用,也可以混合使用,共同使用需要保持name和value的值一致,否则会出错

@Bean(name="userBean", value="userBean")

容器在执行@Bean注解方法实例化Bean时,如果该方法有输入参数,则容器会根据参数名查找Bean并作为依赖项进行注入,若没找到,则容器启动失败。

XML配置的Bean使用init-method和destroy-method属性设置初始化和销毁的回调方法,与此类似,@Bean注解使用initMethod和destroyMethod属性定义回调方法@Bean注解默认注册的是singleton作用域的Bean实例,结合@Scope注解,可以定义其他的作用域范围。使用@Description注解可以对该Bean做一些详细的描述,注解的描述内容通过beanDefinition.getDescription()方式获取。

@Component("serviceMedol")
public class ServiceMedol {
	// 配置User类中的初始化方法和销毁方法
	@Bean(name = "userBean", initMethod = "initUser", destroyMethod = "destroyUser")
	@Scope("singleton")
	@Description("这是一个通过@Bean产生的userBean")
	public User method() {
		User user = new User();
		user.setMailbox("123");
		return user;
	}
}

@Bean较常使用在Java代码配置类中(使用@Configuration注解的类)

4.3 @Autowired(依赖对象的自动装配)

在XML中通过<constructor-arg>和<property>标签进行构造器注入和属性注入依赖对象是传统的依赖注入方式,但这种方式较为繁琐,特别是对于依赖项特别多的状况,Bean的配置就显得冗杂,容易出错,整个XML文件的维护也比较麻烦。Spring提供了@Autowired注解,注解在代码中进行依赖项的自动装载,大大简化了配置,提高了开发效率。@Autowired注解可以使用在类构造器属性属性setter方法甚至一般方法上,也可以混合使用

1.在构造器中使用@Autowired

构造器中的参数名和容器中存在的该类的实例名可以不一样,此时,容器会通过类型查找,如果该类的实例存在多个的话会出错。一般而言,建议保持参数的名称于需要注入的依赖的Bean名称一致。另外,从Spring4.3开始,如果该Bean类只有一个构造器,在该构造器包含参数的状况下,不加@Autowired注解,容器也会自动查找对象并进行注入。如果该类有多个构造器,则至少需要在某一个构造器上添加该注解。

2.在方法上使用@Autowired

可以使用在属性的setter方法上,也可以使用在一般方法上。对应的方法会被调用。如果两个方法同时有此配置,则都会被调用,如下:

public class AutowiredAnno {
	private User user;
	@Autowired
	public void setUser(User user) {
		System.out.println("setUser方法被调用");
	}

	@Autowired
	public void method(User user) {
		this.user = user;
		System.out.println("method方法被调用");
	}
}

3.在属性上使用@Autowired

可以在任何作用域的属性上使用@Autowired注解,包括private、public和protected。

使用@Autowired注解后,Bean在XML中的配置就不需要处理依赖项的注入了。@Autowired默认根据类来查找和注入容器中管理的对象,对于注解的属性和参数依赖要确保相应的Bean被容器托管(对于XML配置方式,要在配置文件中有定义相应的Bean),否则容器在初始化的时候就会找不到依赖对象而无法正常启动。如果多个Bean对应同一个类的话,则使用该类集合类型装载就可以得到该类的所有Bean的集合,集合类型包括该类的Array[]、List、Set和Map。集合类型也可以应用在构造器和方法的自动装载的注解方式中,以属性注解的方式来看:

@Autowired // 自动装载数组类型的依赖
private User[] userArray;
@Autowired // 自动装载集合类型的依赖
private Set<User> userSet;
@Autowired // 自动装载键值类型的依赖,键和值分别是Bean的id和根据Bean配置产生的实例
private Map<String, User> userMap;

4.自动装配的required配置

@Autowired默认是required的,也就是被注解的依赖对象必须已经在容器中注册。如果没有,则抛出UnsatisfiedDependencyException异常,容器初始化失败。这和@Required注解的效果是一致的,区别是@Required是对XML文件中的配置依赖项进行检查;@Autowired会自动在容器中查找依赖项并注入。使用了@Autowired注解的构造器和setter方法一般不再需要@Required注解。如果需要取消在容器初始化时对依赖项额检查,则可以设置required属性值为false。如果这样设置,那么容器启动时尽管被依赖的对象没有在容器中注册,也不会抛出异常,导致容器启动失败。

除了使用@Autowired的required属性方式检查,还可以在方法参数上使用@Nullable注解可以达到同样的效果。

5.自动装配的顺序和选择

在同一个类被注入多个Bean实例的状况下,使用@Autowired可以注入该类的依赖对象和该类对应的Bean的集合。

  • 注入该类的对象时,需要在某一个Bean的配置上使用@Primary注解标注该Bean实例优先被使用。也可以结合@Autowired和@Qualifier,通过名称等限定标识符查找某一个Bean实例。
  • 注入该类的对象时,在Bean配置中使用@Order注解可以设定各Bean实例在集合中的顺序

@Autowired可以用来自动装配自定义的Bean,也可以用来装配容器的上下文和容器对象,包括BeanFactory、ApplicationContext、Environment等、自动装配可以简化依赖注入的配置,加快开发的速度,但是也存在一些限制和缺点,比如:简单类型不能自动装配。自动装配和显式依赖注入可以共存,显示依赖注入配置会覆盖自动装配

4.4 @Primary(依赖的主候选)

@Autowired默认根据类来查找和注入容器中的对象,如果存在同一个类的多个Bean实例被容器管理的状况,在使用@Autowired装配该类的依赖对象时也会报UnsatisfiedDependencyException异常,容器初始化失败。可以在该类的某个Bean的配置中设置该Bean作为依赖注入的主候选解决此问题。与XML中<bean>上的primary属性一样。@Primary注解可以使用在类和方法上。可以使用在@Component注解的类上,也可以使用在@Bean注解的方法上。

1.@Primary使用在@Component注解的类上的场景

A是一个接口,BA和CA是该接口的实现类,这两个实现类都用Component注解为组件,如果通过@Autowired注入A接口类型的依赖,会找到两个Bean,此时可以在BA或CA之一中使用@Primary注解。

2.使用在@Bean注解的方法上

方法中使用@Bean注解,可以达成对同一个类的多个Bean实例的注册,这也是@Primary更为常见的使用方式。

@Bean
@Primary
public User firstUser() {
    return new User();
}

@Bean
public User secondUser() {
    return new User();
}

4.5 @Qualifier(精确查找依赖)

除了使用@Primary指定主候选外,可以用@Autowired和@Qualifier结合使用,根据Bean名字查找依赖对象。@Qualifier可以使用在属性方法参数中。例如:

@Autowired
@Qualifier("firstUser")
private User user;

4.6 @Order(同类型的集合注入顺序)

如果同一个类注册了多个Bean实例,则通过@Autowired注解可以自动装配该类的集合类型的依赖,集合中元素的顺序默认是以定义和注入的顺序,也就是在XML文件中的配置顺序或以@Bean注解的顺序进行排序。使用@Order注解可以指定Bean注入的顺序。

@Bean
@Order(1)
public User firstUser() {
    return new User();
}

@Bean
@Order(2)
public User secondUser() {
    return new User();
}

@Order后面的数值越小,则优先级越高,在集合中的位置越靠前。

@Autowired
private List<User> userList;

5.基于Java代码的配置

配置<context:component-scan>标签后,我们可以在Java类和方法中使用注解实现Bean的配置,自定义类的Bean配置也就可以从XML移到Java代码的注解中实现了。但是还有一些配置留在XML中,比如容器类的Bean配置,类似BeanPostProcessor等,以及<context:component-scan>等功能标签的配置。Spring也提供了完全脱离XML的容器配置方式,也就是所有配置都通过Java代码完成。

5.1 @Configuration(配置类注解)

@Configuration也是一种组件类型的注解,它是使用@Component元注解定义的注解。使用@Configuration注解的类相当于XML配置中的<beans>元素(即相当于一个配置文件),Spring首先会将该类注册为Bean然后可以在该类中的方法使用@Bean注解注册组件,对应的方法相当于XML配置中的<bean>元素。在非@Configuration注解类的@Bean注解方法中不能定义Bean间的依赖关系,如果定义在非@Configuration注解类的依赖关系中,则有可能被当作一般的方法被调用,而不是用来作为Bean定义的方法。对于至这一点,通过如下示例验证:

@Component
public class AppConfig {
	@Bean
	public User getUser() {
		System.out.println("执行getUser方法");
		return new User("123");
	}

	@Bean
	public UserService getUserService1() {
		System.out.println("执行getUserService1方法");
		User user = this.getUser();
		return new UserService(user);
	}

	@Bean
	public UserService getUserService2() {
		System.out.println("执行getUserService2方法");
		User user = this.getUser();
		return new UserService(user);
	}
}

上面AppConfig类没有被@Configuration注解,UserService依赖User对象,我们将User也注册为Bean,通过getUser获取User这个Bean,并注入到两个UserService对应的Bean中。输出如下内容:

System.out.println(applicationContext.getBean("appConfig"));    
System.out.println(applicationContext.getBean("getUser"));    //默认方法名为Bean名称
System.out.println(applicationContext.getBean("getUserService1"));
System.out.println(applicationContext.getBean("getUserService2"));

从输出结果可以看出来没有被@Configuration注解的情况下容器中的User对象并没有注入到两个UserService中,而且getUser()方法总共被调用了三次,这就是没有将this.getUser()当成Bean定义方法,而是一般方法调用了,对AppConfig加上@Configuration注解,执行结果如下:

可以看到getUser方法只执行了一次,getUserService1和getUserService2方法中调用的getUser方法被当成从容器中获取Bean(User)的方法,而且这个配置类AppConfig也是通过cglib代理生成的,他会代理配置类中的@Bean注解方法(这个就是为什么User对象都是同一个的原因,因为代理类会先判断容器中是否存在getUser()这个方法返回的Bean,如果存在则直接从容器中获取)。当然还可以指定其中proxyBeanMethods属性为false;这样的话就又会变成像调用一般方法那样了。

@Component
public @interface Configuration {
    @AliasFor(annotation = Component.class)
    String value() default "";
    //Spring 5.2开始引入    //默认为true
    boolean proxyBeanMethods() default true;
}

所以如果某些配置类中的Bean没有依赖关系时最好将其设置为false,因为true的话每次还要判断容器中是否存在。

5.2 Java代码配置的容器初始化

XML的容器配置方式使用ClassPathXmlApplicationContext、FileSystemXmlApplicationContext或GenericXmlApplicationContext来读取配置文件并初始化容器,代码配置的方式使用AnnotationConfigApplicationContext根据配置类初始化容器。从类名上可以看出,这个容器初始化类就是通过读取@Configuration注解类来初始化容器的。AnnotationConfigApplicationContext的构造器参数是一个配置类(该类最好用@Configuration注解;该配置类上也可以没有任何注解,即使这样容器也会管理该配置类,个人认为@Configuration注解的作用就是语义性和proxyBeanMethods属性)。该参数是可变长的Class类型,可以添加多个配置类或者组件类。如下:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(new Class[] { AppConfig.class });

虽然Spring支持@Component注解类作为容器初始化参数和使用register()进行组件注册,但更好的方式是使用类似于XML配置中路径扫描的方式来及进行组件注册。(register()方法是AnnotationConfigApplicationContext的应用上下文的类才有的方法,在添加完注解类之后,需要调用refresh()方法更新容器)

5.3 @ComponentScan(组件扫描注解)

XML配置方式中使用<context:component-scan>标签配置扫描组件,与此类似,在代码配置方式中可以使用@ComponentScan实现此功能,使用属性basePackages设置扫描的路径。如下:

@Configuration    //该注解可以去掉,不影响结果。此处是为了语义性。
//该注解可以重复使用
@ComponentScan(basePackages = { "com.mec.spring.annotation" })    
public class AppConfig {
}

如果@ComponentScan没有任何参数指定,则他会扫描AppConfig类所在包及其子包下的组件

该注解定义如下:

public @interface ComponentScan {
        //同basePackages属性
	@AliasFor("basePackages")
	String[] value() default {};

        //指定需要扫描的包
	@AliasFor("value")
	String[] basePackages() default {};
    
        //指定一些类,Spring容器会扫描这些类所在的包以及其子包中的类
	Class<?>[] basePackageClasses() default {};
        
        //自定义bean名称生成器
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
	Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
        ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
        
        //需要扫描包中的哪些资源,默认是扫描.class文件
	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
        
        //类似XML配置中的useDefaultFilters属性。是否启用默认过滤器,默认为true
        //默认的过滤器会将标注有@Component、@Repository、@Service、@Controller这几个注解的类注册到容器中
	boolean useDefaultFilters() default true;
        
        //类似于XML配置中的<context:include-filter>子标签(白名单)
        //注意这里的Filter[]是该注解类中内部定义的@Filter注解
	Filter[] includeFilters() default {};
	
        //类似与XML配置中的<context:exclude-filter>子标签(黑名单)
        Filter[] excludeFilters() default {};
        
        //全局作用,是否延迟实例化bean
	boolean lazyInit() default false;
        
        //includeFilters和excludeFilters就是通过@Filter注解数组来控制过滤的
	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {
                /*
                    通过哪种类型过滤,默认为ANNOTATION,表示通过注解进行过滤。还有如下4种取值
                    ASSIGNABLE_TYPE:通过指定的类型来过滤,即判断候选者是否是指定的类型
                    ASPECTJ:ASPECTJ表达式方式,即判断候选者是否匹配ASPECTJ表达式
                    REGEX:正则表达式方式,即判断候选者的完整名称是否和正则表达式匹配
                    CUSTOM:用户自定义过滤器来筛选候选者,对候选者的筛选交给用户自己来判断
                */
		FilterType type() default FilterType.ANNOTATION;
                
                /*
                    1.type=FilterType.ANNOTATION,该参数可以指定一些注解类,用来判断被扫描的类上是否有classes参数指定的注解
                    2.type=FilterType.ASSIGNABLE_TYPE,该参数可以指定一些类型,用来判断被扫描的类是否是classes参数指定的类型
                    3.type=FilterType.CUSTOM,表示这个过滤器是用户自定义的,classes参数就是用来指定用户自定义的过滤器,自定义的过滤器需要实现org.springframework.core.type.filter.TypeFilter接口
                */
		@AliasFor("classes")
		Class<?>[] value() default {};

		@AliasFor("value")
		Class<?>[] classes() default {};
                
                /*
                    1.type=FilterType.ASPECTJ,该参数指定需要匹配的ASPECTJ表达式的值
                    2.type=FilterType.REGEX,该参数指定正则表达式的值,通过正则筛选过滤

                */
		String[] pattern() default {};

	}

}

AnnotationConfigApplicationContext还提供了scan()方法在容器初始化之后对包中的组件进行扫描注解,例如:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.scan(new String[] { "com.mec.spring.annotation" });
applicationContext.refresh();

因为@Configuration是@Component的子注解,所以@Configuration注解的类也会被扫描并处理。在实际项目中,一般是将@Configuration和@Component注解的类放在不同的包中。

5.4 @Import(配置类导入注解)

@Import使用在@Configuration注解的类上,用于导入其他的类,它只有一个value属性,用于指定需要导入的类。使用@Import注解有一个好处就是初始化参数只需要指定一个类就可以,@Import类似于XML中的<import>标签。常用的用法为如下几种情况:

  • value指定的类被@Configuration注解:就像在当前的XML配置文件中导入另一个XML配置文件。
  • value指定的类被@ComponentScan注解:则被导入的类上面指定的扫描路径也被扫描。
  • value指定的类为普通类这两个普通类作为Bean被注册到容器中

此外Spring还提供了另外一个标签@ImportResource,可以导入XML的配置文件。

5.5 @Conditional(条件判断注解)

@Conditional注解是Spring4.0引入的,它可以用在任何类型或者方法上,利用该注解可以配置一些条件判断,当所有条件满足时,被@Conditional注解的目标才会注册到容器中。该注解定义如下:

public @interface Conditional {
    Class<? extends Condition>[] value();
}

该注解只有一个value属性,需要传入的参数类型必须是Condition接口的实现类,该接口定义如下:

public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

matches方法用于条件判断,返回true则容器注册该Bean,否则不注册。其中ConditionContext用于获取容器的一些信息,AnnotatedTypeMetadata用于获取被@Conditional注解的对象上的注解信息。

1.使用在类上

@Conditional注解可以使用在配置类上,也可以使用在普通类上。如果使用在配置类上不能满足条件判断的话,那么配置类就不能被注册为Bean,那么里面的配置信息将不能被处理,作用在普通类上,如果不能满足条件的话则不能被注册为Bean。

自定义Condition接口的实现类如下:

public class MyCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		return false;
	}
}

将@Conditional注解到配置类上,如下:

@Conditional(MyCondition.class)    //利用MyCondition中的逻辑判断是否可以处理该配置类
@Configuration
public class AppConfig {
	@Bean(name = "userBean")
	public User getUser() {
		return new User("123");
	}
}

MyCondition中默认返回false,所以此时该配置类不能被注册,那么userBean就不能被注册。利用getBean方法获取userBean会报异常。

2.使用在方法上

使用在方法上也是一样,比如在上面的getUser方法上加上该注解,利用MyCondition中的逻辑判断该userBean是否可以被注册。

5.6.@ImportResource(导入配置文件)

@Import用于导入配置类,而该注解用于导入XML配置文件。该注解定义如下:

public @interface ImportResource {
    /*
        用于指定配置文件路径,Spring中资源文件路径中最常用classpath和classpath*指定路径
        在路径指定中可以使用*和**,*为通配符,**表示递归任意子目录。
        如:classpath:com/mec/**/beans-*.xml(递归com/mec下的所有子目录,找到其中以“bean-”开头的xml文件)
    */
    @AliasFor("locations")
    String[] value() default {};

    @AliasFor("value")
    String[] locations() default {};
        
    /*
        指定bean定义的读取器,bean的配置方式有xml文件的方式,注解的方式,其实还有其他的方式,
        比如properties文件的方式,如果用其他的方式,需要告诉框架具体要用那种解析器去解析这个bean配置文件,
        这个解析器就是BeanDefinitionReader。
    */    
    Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;

}

一般我们的项目是Maven项目,classpath就是编译后的.class文件存放的位置。Maven项目classpath即为classes目录,resources目录的文件也会被放到classes目录下。

6.Spring容器注解汇总与说明

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring中,我们可以使用注解配置和装配Bean,这可以使我们的代码更加简洁和易于维护。下面是关于如何基于注解配置和装配Bean的一些简要介绍: 1. 基于注解配置Bean 在Spring中,我们可以使用以下注解配置Bean: - @Component:表示该类是一个Spring Bean,需要被Spring容器管理。 - @Service:表示该类是一个服务层的Bean。 - @Controller:表示该类是一个控制层的Bean。 - @Repository:表示该类是一个数据访问层的Bean。 这些注解都是基于@Component注解的衍生注解,它们的作用是更加明确地表示Bean的角色。我们可以在Bean类上添加这些注解,告诉Spring容器该类需要被管理。例如: ``` @Service public class UserService { // ... } ``` 2. 基于注解装配Bean 在Spring中,我们可以使用以下注解来装配Bean: - @Autowired:自动装配Bean。 - @Qualifier:指定具体的Bean名称进行装配。 - @Resource:指定具体的Bean名称进行装配,与@Qualifier类似。 - @Value:注入一个具体的值。 使用@Autowired注解进行自动装配时,Spring会自动在容器中寻找与该类型匹配的Bean,并将其注入到类的属性中。例如: ``` @Service public class UserService { @Autowired private UserDao userDao; // ... } ``` 使用@Qualifier或@Resource注解可以指定具体的Bean名称进行装配。例如: ``` @Service public class UserService { @Autowired @Qualifier("userDaoImpl") private UserDao userDao; // ... } ``` 使用@Value注解可以注入一个具体的值。例如: ``` @Service public class UserService { @Value("10") private int maxCount; // ... } ``` 以上就是关于Spring中基于注解配置和装配Bean的简要介绍,希望能对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值