[火眼速查] Spring 速查指南(一)- 依赖注入

简介

Spring 是一款开源的 J2EE 框架,它有许多项目,为 Java 应用开发提供了一整套的工具,其中最核心的就是 Spring Framework 和 Spring Boot 项目。

文本是一个系列文章的第一篇,下面就这两个项目的核心内容做一些速查整理,同时辅以生产源码,便于理解。

相关文章

依赖注入

注册 Bean

XML 文件配置的方式注册 Bean 是最初的方式,但在现代的应用程序中已不推荐使用,建议使用注解或配置类的方式。

注解方式

Spring 提供了 @Component、@Service、@Controller、@Repository、@Configuration 等注解来标记类,然后通过 @ComponentScan 来配置要扫描的包,自动扫描并导入相应的 Bean。

@Service
public class UserService {
}

这几个注解都有一个参数,用于指定 Bean 的名字,如果不提供,则默认是类名(首字母小写)。

这几个注解都可以注册 Bean,但场景不同。@Component 一般表示通用的组件,@Service 一般表示服务层,@Controller 一般表示控制层,@Repository 一般表示数据层,@Configuration 一般表示配置。其中 @Configuration 有点特殊,比其他注解多了 proxyBeanMethods 参数(默认 true),一般用于配置注册其他 Bean(下面会讲)。

而要上述注解标记的类能被扫描到,需要在运行的主类上添加 @ComponentScan 注解。

@SpringBootApplication
// 指定要扫描的组件的包的路径,** 表示包含其下的子包(子包的子包等)
@ComponentScan("com.example.**")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
  }
}

直达源码

配置类方式

使用配置类(@Configuration 注解标记的类)和 @Bean 注解标记的方法可以编程方式实例化并注册类。这种方式可以实例化第三方的类,并执行一些初始化代码。

@Configuration
public class ServerConfig {

    /** 注册 Bean,方法名即是 Bean 的名字 */
    @Bean
    public AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new AuthenticationManager();
        // 执行一些操作
        return authenticationManager;
    }
}

这种方式可以做一些逻辑判断,例如返回 null 则不注册,或者添加 @ConditionalOnXXX 注解,来根据条件注册。

常用的 @ConditionalOnXXX 注解有以下几种:

  • @ConditionalOnClass:存在指定类时,才算符合条件
  • @ConditionalOnMissingClass:不存在指定类时,才算符合条件
  • @ConditionalOnBean:存在指定名称的 Bean 时,才算符合条件
  • @ConditionalOnMissingBean:不存在指定名称的 Bean 时,才算符合条件
  • @ConditionalOnProperty:检查配置是否满足特定的值

@Configuration 有个特殊的参数 proxyBeanMethods(默认 true)。为 true 时开启代理模式,这个类会被代理类所替换,在类中的方法中调用 @Bean 注解的方法时,不会重复调用,也就是一个 @Bean 的方法可以获取另一个 @Bean 方法的返回值,不会重复生成 Bean。为 false 则不会使用代理模式,类似于普通的 @Component。对于一般的配置类(主要用于注册 Bean,注册的 Bean 又是其他注册方法的参数),建议开启,如果 Bean 之间没有互相调用,可以关闭,以减少开销。

直达源码

配置属性类注入

这种方式主要是注入配置属性,例如添加自定义配置的时候,自动注入配置属性类。

在配置属性类中用 @ConfigurationProperties 注解标记,这个类是简单 Java 对象,主要用于映射配置。

@ConfigurationProperties(prefix = CorsProperties.PREFIX, ignoreInvalidFields = true)
public class CorsProperties {
    public static final String PREFIX = "cors";

    /** 是否启用 */
    private boolean enabled = false;


    /** 缓存时间 */
    private long maxAge = 86400L;

    // 省略 getter 和 setter
}

上面定义的配置属性类会从配置文件中关联如下的配置:

cors:
  enabled: false
  maxAge: 86400

在配置类中,使用 @EnableConfigurationProperties 注解引入并注册。

@Configuration
// 引入配置属性类
@EnableConfigurationProperties({CorsProperties.class})
public class WebConfig {
// 其他配置
}

直达源码

Import 方式

@Import 注解可以动态的注册 Bean,@Import 注解可以搭配 @Configuration 注解或 @EnableXXX 注解使用,实现自动配置。

@Configuration
// 可导入多个类
@Import({ConfigA.class, ConfigB.class})
public class WebConfig {
    // 其他配置
}

导入的类可以有以下几种方式:

  • 普通类:普通类会直接注册 Bean。
  • 实现了 ImportSelector 接口(及其子接口):ImportSelector 接口有个 selectImports 方法,返回类全限定名数组,然后会注册这些 Bean。其中一个子接口 DeferredImportSelector,实现了延迟注册,会在所有配置类注册完成之后再注册。
  • 实现了 ImportBeanDefinitionRegistrar 接口:ImportBeanDefinitionRegistrar 接口有个 registerBeanDefinitions 方法,可在该方法中直接注册 Bean。
public class ConfigA implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 返回类的全限定名数组
        return new String[]{"com.bean.User"};
    }
}

Spring 中 @EnableCaching 就使用了 ImportSelector 方式。

直达源码

Spring 中 @EnableAspectJAutoProxy 就使用了 ImportBeanDefinitionRegistrar 方式。

直达源码

ConfigurableBeanFactory 接口

通过获取 ConfigurableBeanFactory 实例,可以调用它的 registerSingleton 来注册单例 Bean。Hutool 的 SpringUtil 工具类就是用了这种方式。

直达源码

注入 Bean

注入 Bean 是指在业务类中,从 IoC 容器获取已注册的 Bean,而不需要自己实例化类。

注解方式

Spring 提供了 @Autowired 和 @Qualifier 两个常用的注解来实现 Bean 注入。

@RestController
public class UserController {

    @Autowired
    private UserService userService;
}

这两个注解可互为补充,它们的区别是:

  • @Autowired 通过类型注入,查找特定的类型或实现接口的实例,但如果注入的接口有多个实现或类型有多个实例,则会报错。
  • @Qualifier 通过 Bean 名称注入,可以通过参数指定,默认取变量名,查找名称不会重复,但可能类型不匹配。

它们可以结合起来一起使用,通过类型和名称确定 Bean。

J2EE 另外提供了一个 @Resource 注解,也是被 Spring 所支持的。@Resource 注解有多个参数配置,可根据名称或类型(或都匹配)注入。Spring 也支持 JSR 标准的几个注解(@Inject、@Named、@ManagedBean 等),和 Spring 的注解类似,但不推荐使用,这里可以看到它们的差异。

构造函数方式

注解的方式需要在属性上添加额外的注解,对于 Spring 环境之外不太友好,不利于非 Spring 环境下的使用。另一种方式是使用构造函数注入,在非 Spring 环境中可以直接通过构造函数传参创建,而在 Spring 环境中,Sping 会根据类型自动注入。这也是 Spring 官方更推荐的方式。

这种方式搭配 Lombok 的 @RequiredArgsConstructor 注解使用起来非常方便,对需要自动注入的属性声明为 private final 即可。

@Component
// 使用 Lombok 的注解自动生成构造函数
@RequiredArgsConstructor
public class PrepareDatasource implements IBaseCommand {

  private final TplDatasourceService datasourceService;

  private final DatasourceProcessor datasourceProcessor;
}

直达源码

这种方式开发第三方库可以避免 Spring 的依赖。

实现 Aware 接口

Spring 提供了几个 Aware 接口,用于获取对应的实例,如 BeanFactoryAware、ApplicationContextAware 接口就可以分别用来获取 BeanFactory、ApplicationContext(官方推荐应优先使用 ApplicationContext)。另外继承 ApplicationObjectSupport 类(包括其他子类)也是一样的,它就是实现了 ApplicationContextAware 接口的抽象类。

它们都有一个 setXXX 方法,Spring 会在实例化它们之后,调用 setXXX 方法,把对应的实例注入,我们只需要在这个类中用一个属性接收即可。获取到 BeanFactory 或 ApplicationContext 之后,即可通过它们的 getBean 方法获取 Bean。

@Component
public class Component1 implements ApplicationContextAware {

  private ApplicationContext context;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
  }

  public void test1() {
    this.context.getBean("...")
  }
}

这种方式一般主要用于获取动态的 Bean。

工具类方式

这种方式就是通过工具类的方式包装,获取 ApplicationContext 之后来获取 Bean,和上面的方法类似。主要的工具类有 Spring Web 的 WebApplicationContextUtils,Hutool 的 SpringUtil 等,都是静态方法,可以直接使用。另外 Spring Web 的 ContextLoader 也有个静态方法 getCurrentWebApplicationContext 可以获取 ApplicationContext。

这种方式也可以用于第三方库,兼容 Spring 环境或非 Spring 环境。

获取配置文件值

注入属性类

参考上面 配置属性类注入 定义配置属性类并注册后,获取 Bean 可直接获取属性。

直达源码

@Value 注解获取

使用 @Value 注解可直接获取配置。

在属性上获取

@RestController
public class TestController {

    // 获取配置 server.port
    @Value("${server.port}")
    private String port;

    // ...
}

也可以在参数上获取

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

支持 SpEL 表达式(使用 #{}

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
        this.catalog = catalog;
    }
}

Bean 注入冲突

如果使用 @Autowired 注解注入 Bean,存在多个实例时会产生错误。这时就可以使用 @Primary 注解标记主要的实例,可以用在任何注册 Bean 的地方。

@Configuration
public class MovieConfiguration {

	@Bean
	@Primary
	public MovieCatalog firstMovieCatalog() { ... }

	@Bean
	public MovieCatalog secondMovieCatalog() { ... }

	// ...
}

这样获取的就是 firstMovieCatalog 实例了。

public class MovieRecommender {

	@Autowired
	private MovieCatalog movieCatalog;

	// ...
}

Bean 生命周期钩子

实现接口

如果想在 Bean 初始化之后或者销毁之前执行一些代码,可以实现 InitializingBean、DisposableBean 或 BeanPostProcessor 等接口,它们提供了一些方法来介入 Bean 的生命周期。

  • InitializingBean 的 afterPropertiesSet 方法,在 Bean 初始化之后执行相关代码。
  • DisposableBean 的 destroy 方法,在 Bean 销毁前执行相关代码。
  • BeanPostProcessor 的 postProcessBeforeInitialization 方法,在 Bean 初始化之前执行相关代码。
  • BeanPostProcessor 的 postProcessAfterInitialization 方法,在 Bean 初始化之后执行相关代码。

直达源码:InitializingBean

直达源码:DisposableBean

直达源码:BeanPostProcessor

方法注解

另外也可以使用 JSR-250 标准提供的注解:@PostConstruct、@PreDestroy。这两个注解用在类的方法上,用于在对应的时机调用方法。使用这种方式的话就可以与 Spring 解绑,兼容非 Spring 环境了。

public class CachingMovieLister {

	@PostConstruct
	public void populateMovieCache() {
		// 实例化之后初始化,例如关联实例,引入资源等
	}

	@PreDestroy
	public void clearMovieCache() {
		// 销毁实例前清理,例如释放资源
	}
}

@Bean 注解

@Bean 注解也提供了参数,用来指定初始化和销毁的方法。

public class BeanOne {

	public void init() {
		// 初始化逻辑
	}
}

public class BeanTwo {

	public void cleanup() {
		// 销毁逻辑
	}
}

@Configuration
public class AppConfig {

	@Bean(initMethod = "init")
	public BeanOne beanOne() {
		return new BeanOne();
	}

	@Bean(destroyMethod = "cleanup")
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}

注:销毁方法默认值是推断,如果类中含有公开的 closeshutdown 方法,那么它们就会被注册成为销毁方法。如果不希望这样,需要手动指定 destroyMethod 或者换个方法名。

当然如果一个 Bean 结合了上述的多种方式(最好不要这么做),那么它的执行顺序是怎么样的呢?简单来说,@PostConstruct/@PreDestroy > 接口 > 自定义方法。

Aware 接口

Spring 提供了很多 Aware 接口,可以被理解为感知接口,这些接口提供了一个 setXXX 方法,用于在 Bean 初始化之后注入 Spring 的一些重要对象。

主要的一些 Aware 接口有:

  • ApplicationContextAware:获取 ApplicationContext
  • ApplicationEventPublisherAware:获取 ApplicationEventPublisher,用于发布事件
  • BeanClassLoaderAware:获取 Bean 的类载入器
  • BeanFactoryAware:获取 BeanFactory
  • BeanNameAware:获取 Bean 名称
  • LoadTimeWeaverAware:获取 LoadTimeWeaver,可实现类的动态加载
  • MessageSourceAware:获取 MessageSource,可实现多语言国际化
  • NotificationPublisherAware:获取 NotificationPublisher,可发送 JMX 通知
  • ResourceLoaderAware:获取 ResourceLoader,可用来加载 resources 中的资源
  • ServletConfigAware:在 Web 应用中获取 ServletConfig
  • ServletContextAware:在 Web 应用中获取 ServletContext

(未完待续)

如果觉得有用,请多多支持,点赞收藏吧!

火眼证据分析是一款专业的电子取证软件,主要用于数字取证和取证分析。它提供了强大的取证功能和多种分析工具,帮助用户高效地从各种数字设备和电子媒体上提取、分析和还原目标数据。 要下载火眼证据分析,可以在CSDN(中国软件开发网)上进行操作。CSDN是一个为软件开发者提供学习、交流和资源下载的平台,拥有广泛的软件和开发相关资源。以下是在CSDN上下载火眼证据分析的步骤: 1. 打开浏览器,进入CSDN官网(www.csdn.net)。 2. 在网站首页的搜索栏中输入"火眼证据分析",点击搜索按钮。 3. 在搜索结果页面中,可以看到与"火眼证据分析"相关的内容和资源。找到适合自己的下载资源,并点击进入对应页面。 4. 在资源页面,可以阅读该软件的详细介绍和用户评价,确保其真实可靠。 5. 根据页面提供的下载链接,点击下载按钮来下载软件安装包。通常,CSDN提供多个下载通道供选择,用户可以根据自己的需求选择合适的下载方式。 6. 下载完成后,找到下载的安装包文件,并运行安装程序。 7. 根据安装向导的指示,逐步完成火眼证据分析软件的安装。 8. 安装完成后,可以双击桌面图标或从开始菜单中找到火眼证据分析,并打开软件开始使用。 总之,通过在CSDN上搜索并下载火眼证据分析软件,用户可以方便地获取该软件并进行电子取证和分析工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值