Spring框架之基于注解的容器配置

开发 架构
在我们使用基于注解的配置之前,我们需要在Spring配置文件中启用它。因此,如果你想在你的Spring应用程序中使用任何注解,请考虑以下配置文件。

默认情况下,Spring容器中的注解配置没有被打开。因此,在我们使用基于注解的配置之前,我们需要在Spring配置文件中启用它。因此,如果你想在你的Spring应用程序中使用任何注解,请考虑以下配置文件。

<context:annotation-config/>

@Required注解是方法级注解,适用于Bean的setter方法。这个注解简单地表明setter方法必须在配置时被配置为具有依赖注入的值。

@Component

@Component
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}

属性movieFinder必须存在,若注入时为null,会抛NullPointerException异常。

@Autowired

我们可以使用@Autowired来标记Spring将要解析和注入的依赖关系。我们可以在构造函数、Setter或字段注入中使用这个注解。

构造函数注入

class Car {
private Engine engine;

@Autowired
Car(Engine engine) {
this.engine = engine;
}
}

Setter 注入

class Car {
private Engine engine;
@Autowired
void setEngine(Engine engine) {
this.engine = engine;
}
}

字段注入

class Car {
@Autowired
private Engine engine;
}

@Primary

当有多个相同类型的Bean时,使用@Primary来给一个Bean更高的优先权。

为什么需要@Primary?在某些情况下,我们需要注册超过一个相同类型的Bean。在这个例子中,我们有mySQLConnection()和oracleConnection()的Connection类型的bean。

@Configuration
public class Config {
@Bean
public Connection mySQLConnection() {
return new MySQLConnection();
}
@Bean
public Connection oracleConnection() {
return new OracleConnection();
}
}

如果我们运行该应用程序,Spring会抛出NoUniqueBeanDefinitionException。为了访问具有相同类型的Bean,我们通常使用@Qualifier("beanName")注解,和@Autowired一起应用。在我们的案例中,我们在配置阶段配置Bean,所以@Qualifier不能在这里应用。关于@Qualifier会在本章后面讲到。

为了解决这个问题,Spring提供了@Primary注解。下面的例子展示了如何在一个Spring应用程序中使用@Primary注解。
@Primary注解可用于任何直接或间接用@Component注解的类或用@Bean注解的工厂方法。在这个例子中,我们将使用@Primary注解和@Component注解。

package com.demo.spring.primary;
public interface MessageService {
public void sendMsg();
}
package com.demo.spring.primary;
import org.springframework.stereotype.Component;
@Component
public class FacebookMessageService implements MessageService {
@Override
public void sendMsg() {
System.out.println("FacebookMessageService implementation here");
}
}
package com.demo.spring.primary;
import org.springframework.stereotype.Component;
@Component
public class EmailMessageService implements MessageService {
@Override
public void sendMsg() {
System.out.println("EmailMessageService Implementation here");
}
}
package com.demo.spring.primary;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Primary
@Component
public class TwitterMessageService implements MessageService {
@Override
public void sendMsg() {
System.out.println("TwitterMessageService Implementation here");
}
}

请注意,在上述TwitterMessageService类中,我们添加了@Primary与@Component注解。

package com.demo.spring.primary;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.demo.spring.primary")
public class AppConfig {

}
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MessageService messageService = context.getBean(MessageService.class);
messageService.sendMsg();
context.close();
}
}

Output:

TwitterMessageService Implementation here

@Qualifier

当我们创建了多个相同类型的Bean,但只想将其中一个与某个属性关联。可以使用@Qualifier注解和@Autowired注解来控制。
@Qualifier用于解决模糊的依赖关系,也就是说,它帮助@Autowired注解选择其中一个依赖关系。如果一个接口有多种实现,那么我们可以在运行时使用@Qualifier来选择所需的实现。

public interface MessageService {
public void sendMsg(String message);
}
public class EmailService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
public class TwitterService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
public class SMSService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
public interface MessageProcessor {
public void processMsg(String message);
}
public class MessageProcessorImpl implements MessageProcessor {
private MessageService messageService;
// setter based DI
@Autowired
@Qualifier("TwitterService")
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
// constructor based DI
@Autowired
public MessageProcessorImpl(@Qualifier("TwitterService") MessageService messageService) {
this.messageService = messageService;
}
public void processMsg(String message) {
messageService.sendMsg(message);
}
}
@Configuration
@ComponentScan("com.demo.springframework.di")
public class AppConfiguration {
@Bean(name="emailService")
public MessageService emailService(){
return new EmailService();
}
@Bean(name="twitterService")
public MessageService twitterService(){
return new TwitterService();
}
@Bean(name="smsService")
public MessageService smsService(){
return new SMSService();
}
@Bean
public MessageProcessor messageProcessor(){
return new MessageProcessorImpl(twitterService());
}
}
public class TestApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class);
MessageProcessor userService = applicationContext.getBean(MessageProcessor.class);
userService.processMsg("twitter message sending ");
}
}

Output:

twitter message sending 

@Resource

Spring支持通过@Resource(javax.annotation.Resource)方式注入,可以在字段或setter方法上加该注解,@Resource有一个name属性,如果不指定name,默认就是字段或setter方法名称。

public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}

@Value

该注解可用于向Spring管理的Bean中的字段注入值,它可以应用于字段或构造函数/方法参数级别。

我们需要一个属性文件来定义我们想用@Value注解注入的值。因此,我们首先需要在我们的配置类中定义一个@PropertySource--带有属性文件名。

value.from.file=Value got from the file
priority=high
listOfValues=A,B,C

注意:Spring Boot默认配置了一个PropertySourcesPlaceholderConfigurer的Bean,它将从application.properties和application.yml文件中获取属性。

@Value("${value.from.file}")
private String valueFromFile;

有时,我们需要注入一堆值。将它们定义为属性文件中单个属性用逗号分隔的值,或定义为系统属性并注入一个数组。例如listOfValues中定义了逗号分隔的值,所以数组的值将是["A","B", "C"]。

@Value("${listOfValues}")
private String[] valuesArray;

使用SpEL表达式

使用SpEL表达式获取配置文件中priority的值。

@Value("#{systemProperties['priority']}")
private String spelValue;

如果在property文件中没有定义属性,就会报nullvalue异常。为了防止这种情况,我们可以在SpEL表达式中提供一个默认值。如果属性没有定义,我们就为该字段设置默认值。

@Value("#{systemProperties['unknown'] ?: 'some default'}")
private String spelSomeDefault;

我们也可以用其它bean的属性值:

@Value("#{someBean.someValue}")
private Integer someBeanValue;

从属性配置中获取List列表:

@Value("#{'${listOfValues}'.split(',')}")
private List<String> valuesList;

把@Value注入到Map加入propeties文件中有如下key:

valuesMap={key1: '1', key2: '2', key3: '3'}

注意,Map中的值必须是单引号。

@Value("#{${valuesMap}}")
private Map<String, Integer> valuesMap;

如果要获取map中指定的key:

@Value("#{${valuesMap}.key1}")
private Integer valuesMapKey1;

如果不确定Map中是否包含key,我们可以使用一个安全的表达式,使其不会抛出异常,并且设置值为null:

@Value("#{${valuesMap}['unknownKey']}")
private Integer unknownMapKey;

为属性设置默认值:

@Value("#{${unknownMap : {key1: '1', key2: '2'}}}")
private Map<String, Integer> unknownMap;

@Value("#{${valuesMap}['unknownKey'] ?: 5}")
private Integer unknownMapKeyWithDefaultValue;

注入前过滤不需要的值:

@Value("#{${valuesMap}.?[value>'1']}")
private Map<String, Integer> valuesMapFiltered;

与构造函数一起注入

@Component
@PropertySource("classpath:application.properties")
public class PriorityProvider {

private String priority;

@Autowired
public PriorityProvider(@Value("${priority:normal}") String priority) {
this.priority = priority;
}

// standard getter
}

注意:默认配置文件是application.properties或application.yaml则不需要@PropertySource注解。

与Setter一起注入

@Component
@PropertySource("classpath:values.properties")
public class CollectionProvider {
private List<String> values = new ArrayList<>();
@Autowired
public void setValues(@Value("#{'${listOfValues}'.split(',')}") List<String> values) {
this.values.addAll(values);
}
// standard getter
}

@PostConstruct and @PreDestroy

我们可以自定义Bean的创建和销毁附动作。我们可以通过实现InitializingBean和DisposableBean接口来实现它。我们也可以用@PostConstruct和@PreDestroy注解来实现。

@PostConstruct

Spring只调用一次用@PostConstruct注释的方法,就在Bean属性初始化之后。请记住,即使没有任何东西需要初始化,这些方法也会运行。带有@PostConstruct注释的方法不能是静态的。

@Component
public class DbInit {
@Autowired
private UserRepository userRepository;
@PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}

@PreDestroy

带有@PreDestroy注解的方法只运行一次,就在Spring将我们的Bean从容器中移除之前。带有@PreDestroy注解的方法不能是静态的。

@Component
public class UserRepository {
private DbConnection dbConnection;
@PreDestroy
public void preDestroy() {
dbConnection.close();
}
}
责任编辑:姜华 来源: 今日头条
相关推荐

2022-06-09 07:27:14

JavaSpring容器

2022-05-30 09:32:07

Spring容器

2009-05-11 10:27:32

Spring Batc配置工作划分

2022-06-07 07:58:45

SpringSpring AOP

2019-10-18 16:05:32

框架开发Java

2009-06-19 09:52:46

Acegi安全框架Spring框架

2009-06-19 11:43:59

Spring MVC框

2009-06-19 11:28:45

2022-06-08 08:04:28

Springservicerepository

2022-05-27 08:25:55

容器Spring

2022-02-20 07:28:13

Spring注解用法

2022-02-19 07:41:36

Bean注解项目

2021-07-09 07:52:34

SpringContextEventListen

2019-11-22 09:40:40

SpringJava编程语言

2024-04-02 09:32:08

Spring@Retry开发者

2021-03-08 00:11:02

Spring注解开发

2024-03-14 10:47:12

Spring生命周期阿里

2019-07-02 11:01:35

SpringBean配置

2023-05-10 08:29:28

Spring配置原理

2024-01-02 08:18:41

点赞
收藏

51CTO技术栈公众号