本文打算介绍几个不太容易说出其区别,或者用途的 Spring 注解,比如 @Component 与 @Bean 的比较,@ControllerAdvice 是如何处理自定义异常的等等。
1.Configuration注解
@Configuration这个注解可以加在类上,让这个类的功能等同于一个bean xml配置文件,如下:
@Configuration
public class ConfigBean {
}
上面代码类似于下面的xml:
<?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-4.3.xsd">
</beans>
通过AnnotationConfigApplicationContext来加载@Configuration修饰的类,如下:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);
此时ConfigBean类中没有任何内容,相当于一个空的xml配置文件,此时我们要在ConfigBean类中注册bean,那么我们就要用到@Bean注解了。
总结
@Configuration使用步骤:
在类上使用@Configuration注解
通过AnnotationConfigApplicationContext容器来加@Configuration注解修饰的类
2. @Bean注解
在Spring框架中,一旦你通过@Bean
注解方法注册了一个Bean,你就可以在应用程序的其他部分通过依赖注入(Dependency Injection, DI)来使用它。下面是一个详细的例子,展示了如何创建、配置和使用Bean。
首先,创建一个简单的Java类,我们称之为MyBean
:
public class MyBean {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
然后,创建一个配置类来注册MyBean
的Bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setMessage("Hello, World!");
return myBean;
}
}
在上述配置类AppConfig
中,我们定义了一个名为myBean
的方法,并使用@Bean
注解标记它。这个方法创建一个MyBean
实例,并设置其message
属性,然后返回它。
接下来,创建另一个类来使用MyBean
:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyService {
private final MyBean myBean;
@Autowired
public MyService(MyBean myBean) {
this.myBean = myBean;
}
//或者直接@Autowired
// private final CacheClient cacheClient;
public void printMessage() {
System.out.println(myBean.getMessage());
}
}
注意,必须是有@Component的类才能使用依赖注入,如果是自己new的类,则不能使用@Autowired。
2.1 同类型但不同不同实现方式的bean
在一个大型的Spring应用中,可能会有多个配置类,每个配置类都可能定义了同类型但不同名称的bean。例如,你可能有两个不同的数据源配置,每个数据源配置都创建一个DataSource
bean,但每个bean有不同的数据库连接设置。在这种情况下,你会有两个DataSource
bean在Spring容器中,需要使用@Qualifier
注解来指定你想要注入哪个DataSource
bean。
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource1() {
// ... 创建并返回第一个DataSource
}
@Bean
public DataSource dataSource2() {
// ... 创建并返回第二个DataSource
}
}
@Service
public class SomeService {
@Autowired
@Qualifier("dataSource1")
private DataSource dataSource;
// ...
}
2.2 bean的单例和多例
Bean的单例(Singleton)指的是在Spring容器中,一个类型的Bean只会被创建和初始化一次,所有对该类型Bean的请求都会返回同一个实例。这样可以节省资源和提高效率,因为Bean只被创建一次,并在需要时重用。单例是Spring容器的默认作用域。一个spring容器有一个实例。如果有多个spring容器,则即使是单例也会有多个实例。
prototype(多例):对这个bean的每次请求都会创建一个新的bean实例,类似于new。
在Spring配置中,可以通过scope
属性来指定Bean的作用域,例如,将scope
设置为singleton
(单例)或prototype
(原型,每次请求都创建新实例)。
@Configuration
public class AppConfig {
@Bean
@Scope("singleton") // 此行是可选的,因为默认就是单例
public User user() {
return new User("John Doe", 25);
}
}
@Service
public class SomeService {
@Autowired
private User user1; // user1 和 user2 会是同一个实例
@Autowired
private User user2;
// ...
}
在上述例子中,通过@Bean
注解定义了一个User
类型的bean,在SomeService
类中通过@Autowired
注解注入了两个User
实例user1
和user2
。由于User
bean的作用域是单例,user1
和user2
将指向相同的User
实例。
注:
@Autowired
注解通常根据类型来自动装配bean。Spring容器会查找与目标字段或参数类型匹配的bean。如果找到多个匹配的bean,它将根据bean的名称或使用@Qualifier
注解来确定要使用哪个bean。如果没有找到匹配的bean,或者找到多个匹配的bean但没有指定@Qualifier
,Spring会抛出一个异常。
通俗解释看文章:Spring中Bean的单例和多例简单总结_如何查看spring 是单例还是多例-优快云博客
2.3 为什么一定要使用@Configuration来加载bean
详细见文章:Spring系列第十九讲 @Configuration和@Bean注解详解-腾讯云开发者社区-腾讯云
2.4 在Bean实例中注入其他实例
如果你想在BookService
中注入BookRepository
,你可以将BookService
的构造函数修改为接受BookRepository
参数,并在@Bean
方法中传递这个参数:
@Configuration
public class AppConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService(BookRepository bookRepository) {
return new BookService(bookRepository);
}
}
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
// ...
}
注意,BookService
类没有任何Spring注解,例如@Component
或@Service
,所以它不会被Spring容器管理,也就无法接收到依赖注入。另外,@Autowired
注解通常不能应用于非Spring管理的类。
如果一个类没有加注解如@component, @controller, @service等扫描这个类到容器中
在类中的变量加@Autowired注解无法生效。
因为如果一个类new对象生成的,那么这个类就不归spring容器管理,IOC等spring的功能也就无法使用了。
所以想要在没有注解的类中注入,就需要用构造器的方法定义这个注入的类。然后再new这个类的地方进行注入。(详情请参考自定义的stringRedisTemplate)。
3. @Component 和 @Bean 的区别是什么?
@Bean
和@Component
都是Spring框架中用于注册bean的注解,但它们之间有一些重要的区别:
-
用途和应用场景:
@Bean
注解通常用于方法,指示Spring框架该方法将返回一个应该注册为bean的对象。它通常用在配置类中,用于创建和配置bean。@Component
注解用于类,指示Spring框架该类是一个组件,其实例应该被自动检测和注册为bean。它是一个通用注解,还有一些更具体的变体,如@Service
、@Repository
和@Controller
。
-
Bean的创建:
- 使用
@Bean
注解的方法允许你编程式地创建和配置bean。你可以在方法体内包含创建对象和配置属性的逻辑。 - 使用
@Component
注解时,Spring会自动为你创建bean的实例,通常是通过调用类的默认构造函数。
- 使用
-
自定义逻辑:
@Bean
注解允许你有更多的控制权和自定义逻辑,因为你可以在方法体中包含任何需要的代码来配置bean。@Component
注解主要用于标记类,不提供在bean创建和配置过程中包含自定义逻辑的直接方式。
-
命名:
@Bean
注解的方法名通常用作bean的名称,但你也可以通过name
属性为bean提供一个自定义名称。@Component
注解没有name
属性,但你可以使用@Component("customName")
为bean提供一个自定义名称。
-
依赖注入:
- 在
@Bean
注解的方法中,你可以通过方法参数来注入其他的beans。 - 在
@Component
注解的类中,你可以通过构造函数、setter方法或字段来注入其他的beans。
- 在
-
生命周期回调:
@Bean
注解可以与initMethod
和destroyMethod
属性一起使用,以指定bean的初始化和销毁方法。@Component
注解可以与@PostConstruct
和@PreDestroy
注解一起使用,以指定bean的初始化和销毁方法。
在实践中,你可能会根据具体的需求和上下文来选择使用@Bean
或@Component
。例如,如果你需要更多的控制权和自定义逻辑,可能会选择使用@Bean
。如果你想让Spring自动管理bean的创建和注册,可能会选择使用@Component
。
下面进行详解:
3.1 作用对象不同:@Component 注解作用于类,而 @Bean 注解作用于方法、
@Component 通常是通过路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean 告诉了 Spring 这是某个类的实例,当我们需要用它的时候还给我。
@Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring 容器时,只能通过 @Bean 来实现。
3.2 如果想将一个引入的外部类进行控制反转,只能用@Bean来操作。因为不能去外部类上面加@Component
3.3 @Bean有更强的自定义属性
比如说我有一个类User,需要构造参数name和age。这个User类用@Component修饰。当我需要注入时,使用@Autowired自动装配,我该如何同时传入这两个构造参数
在这种情况下,你可以创建一个配置类并使用@Bean
注解方法来提供User
实例,同时传递所需的构造参数:
@Configuration
public class AppConfig {
@Bean
public User user() {
return new User("John Doe", 25);
}
}
@Service
public class SomeService {
@Autowired
private User user;
// ...
}
在上述代码中,AppConfig
类定义了一个@Bean
注解的user
方法,该方法创建一个新的User
实例并传递所需的name
和age
参数。然后,在SomeService
类中,通过@Autowired
注解自动装配User
实例。
这种情况不使用@Component进行装配。
3.4 @Component简化开发
@Component
注解主要简化了Bean的注册过程,使开发者不再需要在配置类中手动注册Bean。通过@Component
及其派生注解(如@Service
, @Repository
, @Controller
等),Spring可以自动扫描并注册Bean,使配置更为简洁,代码结构更为清晰。同时,它还允许通过构造器、字段或setter方法注入依赖,这样开发者可以更专注于核心业务逻辑,而不是配置代码。
详情如何使用构造器,字段方法注入,见:滑动验证页面
4. @Resource和@Autowired的区别
@Autowired会先按byType去找,如果没找到,则会按照byName去找
@Resource会先按byName去找,如果没找到则会byType去找。如果设置了name属性,则只会按byName去找,找不到就报错。
@Resource(name = "studentServiceImpl")
private PersonService personService;
什么时候用Autowired、Resource?
Autowired:效率低下,先按类型查找,再按名字查找
Resource:按名字查找,后面要跟参数name,好处:当有多个Impl实现类时,可以通name快速找到
当只有一个Impl实类的时候,随便用哪个都差不多,>=2的时候,最好用@Resource,比@Autowired@Qualifier()效率高