Spring注解驱动开发系列(一)Spring容器组件的注册

简介: Spring注解驱动开发系列(一)Spring容器组件的注册

引言

用过SpringBoot的同学都知道,SpringBoot框架使用注解来代替繁琐的XML配置文件用以管理对象的生命周期,相信大家都被大量的XML配置文件折磨过,但在SpringBoot中,开发人员只需要进行极少量的配置就可以构建出一个优秀的应用。
当然,这一切都建立在大量的注解上,虽然注解的使用相对于XML配置文件来说非常方便,但也因为如此,使得SpringBoot入门简单精通难,因为想要精通就需要熟悉注解的功能和底层的实现。
从本篇文章开始将连载Spring注解驱动开发系列,带大家理解SpringBoot底层的注解并灵活使用,这对后续SpringBoot的学习将会大有裨益。

@Configuration和@Bean注解

先来讲讲@Configuration和@Bean这两个注解,现在有如下一个Bean类:

public class Dog {
   

    private String name;
    private int age;

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public int getAge() {
   
        return age;
    }

    public void setAge(int age) {
   
        this.age = age;
    }

    @Override
    public String toString() {
   
        return "Dog [name=" + name + ", age=" + age + "]";
    }
}

在之前的Spring框架中,我们需要在配置文件中作如下配置:

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

    <bean id="dog" class="com.wwj.bean.Dog">
        <property name="name" value="Tom"></property>
        <property name="age" value="5"></property>
    </bean>

</beans>

然后通过ApplicationContext获得容器中的对象:

    public static void main(String[] args) {
   
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationcontext.xml");
        Dog dog = ctx.getBean(Dog.class);
        System.out.println(dog);
    }

以上是通过XML配置文件实现的,那么这些操作如何通过注解实现呢?我们需要通过一个配置类来实现:

@Configuration // 声明一个配置类
public class MyConfig {
   

    @Bean // 将该类放入到容器中,类型为返回值类型,默认以方法名为id
    public Dog dog() {
   
        Dog dog = new Dog();
        dog.setName("Tom");
        dog.setAge(5);
        return dog;
    }
}

其中@Configuration注解用于声明该类是一个配置类,配置类的作用等价于配置文件。
@Bean注解用于将方法返回值的对象放入到容器中,默认以方法名为对象id。如果想修改id,可以修改方法名,如果不想通过修改方法名对id进行修改,还可以通过@Bean注解的value属性指定id。

@Bean("tomDog")

在从容器中获取对象的过程也和之前不太一样,不再是使用ClassPathXmlApplicationContext类了,而是使用AnnotationConfigApplicationContext类获得容器对象:

    public static void main(String[] args) {
   
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
        Dog dog = (Dog) ctx.getBean("tomDog");
        System.out.println(dog);
    }

@ComponentScan注解

在实际开发中,通过bean标签配置Bean类显然很繁琐,所以通常会使用包扫描将要扫描的包下的组件自动放入容器中,我们同样回顾一下在之前的Spring中是如何进行包扫描的:

<context:component-scan base-package="com.wwj"></context:component-scan>

在配置文件中进行这样的配置,Spring将会在com.wwj包及其子包下自动扫描所有的类,将@Controller、@Service、@Repository、@Component注解的类自动放入容器中。那么如何通过注解实现同样的功能呢?这就需要用到@ComponentScan注解:

@Configuration // 声明一个配置类
@ComponentScan("com.wwj")
public class MyConfig {
   

}

在配置类上使用@ComponentScan注解,并通过value属性指定需要扫描的包。
我们还知道,在配置文件中,可以通过<context:exclude-filter><context:include-filter>子节点对扫描的类进行控制。那么同样地,在@ComponentScan注解中,我们通过excludeFilters和includeFilters属性进行控制。但需要注意的是,这两个属性的值均为Filter类型的数组,所以它们需要借助@Filter注解进行控制,而@Filter需要指定type属性值作为控制方式:例如通过注解控制、通过给定类型控制、通过正则控制、自定义控制等等,这里以通过注解控制举例:

@Configuration // 声明一个配置类
@ComponentScan(value = "com.wwj", excludeFilters = {
   
        @Filter(type = FilterType.ANNOTATION, classes = {
    Controller.class, Service.class }) })
public class MyConfig {
   

}

该注解即排除了@Controller和@Service注解标注的类,这些类将不会被放入容器中。
excludeFilters 属性的用法与其类似,但还是有必要讲一下,不知道大家还记不记得,在之前的Spring框架中,若是想要<context:include-filter>节点生效,则需要将Spring默认的控制规则禁用,即:将use-defalut-filters属性置为false才行。那么同样地,在注解中,我们也需要将默认的控制规则禁用:

@Configuration // 声明一个配置类
@ComponentScan(value = "com.wwj", includeFilters = {
    @Filter(type = FilterType.ANNOTATION, classes = {
    Controller.class,
        Service.class }) }, useDefaultFilters = false)
public class MyConfig {
   

}

Java8及以上的版本可以通过配置多个@ComponentScan注解来搭配控制规则,而Java8以下则需要通过@ComponentScans进行配置,该注解只有一个属性value,值类型即为@ComponentScan类型。
其它的几种控制方式也都很简单,例如通过类型控制:

@Configuration // 声明一个配置类
@ComponentScan(value = "com.wwj", includeFilters = {
   
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
    DemoController.class }) }, useDefaultFilters = false)
public class MyConfig {
   

}

其它方式不做过多解释,重点说一下自定义规则:

public class MyTypeFilter implements TypeFilter {
   

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
   
        return false;
    }
}

自定义类实现TypeFilter 接口,通过重写方法match()进行处理即可,然后将其配置到配置类中:

@Configuration // 声明一个配置类
@ComponentScan(value = "com.wwj", includeFilters = {
   
        @Filter(type = FilterType.CUSTOM, classes = {
    MyTypeFilter.class }) })
public class MyConfig {
   

}

@Scope注解

该注解用于指定Bean的作用范围,它有四种取值:

  • prototype:多实例,每次从容器中获取都创建对象
  • singleton:单实例,整个容器中只有一个实例,默认为单实例
  • request:同一个请求创建一个实例
  • session:同一次会话创建一个实例

使用方式如下:

@Configuration
public class MyConfig {
   

    @Scope("prototype")
    @Bean 
    public Dog dog() {
   
        Dog dog = new Dog();
        dog.setName("Tom");
        dog.setAge(5);
        return dog;
    }
}

@Lazy注解

我们知道,单实例的Bean实在Spring容器初始化的时候就被创建了的,之后使用只需从容器中获取即可,那么@Lazy注解的作用就是懒加载。也就是说,容器在初始化的时候并不会去创建Bean对象,而是当使用该Bean对象的时候才去创建,用法如下:

@Configuration
public class MyConfig {
   

    @Lazy
    @Bean 
    public Dog dog() {
   
        Dog dog = new Dog();
        dog.setName("Tom");
        dog.setAge(5);
        return dog;
    }
}

@Conditional注解

该注解可以用于制定一些条件在注册Bean之前,你可以自定义一个类实现Condition接口,并重写matches()方法:

public class MyCondition implements Condition {
   

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
        return false;
    }
}

在该方法中进行一些条件的判断,然后配置即可,用法如下:

@Configuration
public class MyConfig {
   

    @Conditional({
    MyCondition.class })
    @Bean 
    public Dog dog() {
   
        Dog dog = new Dog();
        dog.setName("Tom");
        dog.setAge(5);
        return dog;
    }
}

该注解还可以标注在类上,当标注在类上时,如果满足自定义的条件,类中的所有Bean才能注册,否则将全部无法注册,该注解在SpringBoot底层中的使用十分频繁。

@Import注解

到目前为止,我们将Bean放入容器中的方式有两种:

  • 通过@Controller、@Service、@Repository、@Component注解和包扫描搭配使用
  • 通过@Bean注解

当然,除了这两种,Spring还提供了一种方式:@Import,用法如下:

@Configuration
@Import(Dog.class)
public class MyConfig {
   

}

这种方式的特点就是简单快速,无需方法,无需扫描,一个注解就完成了Bean的注册操作。
@Import支持ImportSelector导入Bean类,具体用法如下:
自定义一个类实现ImportSelector接口,并重写selectImports()方法,该方法的返回值即为需要放入容器中的Bean,返回值需为Bean的全路径。

public class MyImportSelector implements ImportSelector {
   

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
   
        return new String[] {
   };
    }
}

需要注意的是,即便是你不想注册任何一个Bean,你也应该返回一个空数组而不是null,因为返回null会抛出空指针异常。
MyImportSelector定义好后,在配置类中进行配置即可,用法如下:

@Configuration
@Import(MyImportSelector.class)
public class MyConfig {
   

}

@Import还支持ImportBeanDefinitionRegistrar导入Bean类,具体用法如下:
自定义一个类实现ImportBeanDefinitionRegistrar接口,并重写registerBeanDefinitions()方法,该方法有一个BeanDefinitionRegistry类型的参数,只需调用该对象的registerBeanDefinition()方法即可,该方法将传入Bean的名称和Bean的定制信息。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
   

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   
        RootBeanDefinition beanDefinition = new RootBeanDefinition(Dog.class);
        registry.registerBeanDefinition("dog", beanDefinition);
    }
}

MyImportBeanDefinitionRegistrar 定义好后,在配置类中进行配置,配置方式和前面相同:

@Configuration
@Import(MyImportBeanDefinitionRegistrar .class)
public class MyConfig {
   

}

@FactoryBean注解

该注解同样可以将Bean放入容器中,但它有点特殊,直接来看看用法:
自定义一个类实现FactoryBean接口,并重写方法:

public class DogFactoryBean implements FactoryBean<Dog>{
   

    public Dog getObject() throws Exception {
   
        return new Dog();
    }

    public Class<?> getObjectType() {
   
        return Dog.class;
    }

    public boolean isSingleton() {
   
        return true;
    }
}

泛型即为需要注册的Bean类型,第一个方法返回需要注册的对象,第二个方法返回注册的对象类型,第三个方法设置该对象是否为单例。
接下来在配置类中编写方法,通过@Bean注解将DogFactoryBean放入容器中:

@Configuration
public class MyConfig {
   

    @Bean
    public DogFactoryBean dogFactoryBean() {
   
        return new DogFactoryBean();
    }
}

很神奇的事情是,当你从容器中获取这个DogFactoryBean对象的时候,得到的却是Dog对象,所以FactoryBean获取的其实是getObject()返回的对象。

相关文章
|
12月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
925 0
|
12月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
1657 128
|
11月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
12月前
|
Java 测试技术 数据库
使用Spring的@Retryable注解进行自动重试
在现代软件开发中,容错性和弹性至关重要。Spring框架提供的`@Retryable`注解为处理瞬时故障提供了一种声明式、可配置的重试机制,使开发者能够以简洁的方式增强应用的自我恢复能力。本文深入解析了`@Retryable`的使用方法及其参数配置,并结合`@Recover`实现失败回退策略,帮助构建更健壮、可靠的应用程序。
1196 1
使用Spring的@Retryable注解进行自动重试
|
11月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
822 2
|
12月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
729 12
|
12月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
651 0
探索Spring Boot的@Conditional注解的上下文配置
|
12月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
444 0
Spring中最大化@Lazy注解,实现资源高效利用
|
12月前
|
安全 IDE Java
Spring 的@FieldDefaults和@Data:Lombok 注解以实现更简洁的代码
本文介绍了如何在 Spring 应用程序中使用 Project Lombok 的 `@Data` 和 `@FieldDefaults` 注解来减少样板代码,提升代码可读性和可维护性,并探讨了其适用场景与限制。
377 0
Spring 的@FieldDefaults和@Data:Lombok 注解以实现更简洁的代码
|
12月前
|
缓存 监控 安全
Spring Boot 的执行器注解:@Endpoint、@ReadOperation 等
Spring Boot Actuator 提供多种生产就绪功能,帮助开发者监控和管理应用。通过注解如 `@Endpoint`、`@ReadOperation` 等,可轻松创建自定义端点,实现健康检查、指标收集、环境信息查看等功能,提升应用的可观测性与可管理性。
537 0
Spring Boot 的执行器注解:@Endpoint、@ReadOperation 等

热门文章

最新文章