Spring系列(二)---Spring 常用注解分析

本文主要对 Spring 框架中经常用到的注解与配置进行了说明。

更多文章欢迎访问我的个人博客–>幻境云图

1. Bean相关的注解

与SpringBean相关的注解有以下四大类:

  • @Controller :标注一个控制器组件类 Controller层
  • @Service:标注一个业务逻辑组件类 Service层
  • @Repository :标注一个 DAO 组件类 DAO层
  • @Component :标注一个普通的 Spring Bean 类 前面三个都不是但又想交给Spring管理就用这个

2. @Autowired与@Resource区别

2.1 相同点

@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。

2.2 不同点

1. 提供方

@AutowiredSpring 提供的注解;

@ResourceJ2EE提供的注解,javax.annotation 包下的注解,来自于JSR-250,需要JDK1.6及以上。

2. 注入方式

@Autowired只按照Type 注入;

@Resource 默认按Name自动注入,也提供按照Type 注入;

3. 属性

@Autowired注解可用于为类的属性、构造器、方法进行注值。

默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其 required 属性为false。
@Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合 @Qualifier 注解进行限定,指定注入的bean名称。

@Resource有两个中重要的属性:nametype

name 属性指定 byName,如果没有指定 name 属性:

当注解标注在字段上,即默认取字段的名称作为 bean 名称寻找依赖对象,
当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

4. 其他

@Autowired注解进行装配容易抛出异常,特别是装配的 bean 类型有多个的时候,解决的办法是增加 @Qualifier 注解进行限定。

@Resource注解的使用性更为灵活,可指定名称,也可以指定类型;

3. context:annotation-config与context:component-scan

3.1 context:annotation-config

我们一般在含有 Spring 的项目中,可能会看到配置项中包含这个配置节点

1
<context:annotation-config>

这条配置会向 Spring 容器中注册以下4个 BeanPostProcessor

  • AutowiredAnnotationBeanPostProcessor

  • CommonAnnotationBeanPostProcessor

  • PersistenceAnnotationBeanPostProcessor

  • RequiredAnnotationBeanPostProcessor

注册这4个 BeanPostProcessor 的作用,就是为了你的系统能够识别相应的注解

如果想使用 @Resource@PostConstruct@PreDestroy等注解就必须声明CommonAnnotationBeanPostProcessor
如果想使用@PersistenceContext注解,就必须声明PersistenceAnnotationBeanPostProcessor的Bean。
如果想使用 @Autowired注解,那么就必须声明AutowiredAnnotationBeanPostProcessor的 Bean。
如果想使用 @Required的注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean。

所以如果不加一句context:annotation-config那么上面的这些注解就无法识别

3.2 context:component-scan

context:component-scan包括了context:annotation-config的功能,即注册 BeanPostProcessor 使系统能够识别上面的注解。

同时还会自动扫描所配置的包下的 bean。即 扫描包下面有@Controller@Service@Repository@Component这四个注解的类,自动放入 Spring 容器。

所以一般写context:component-scan就行了。

3. 实例演示

就拿前面的 student 和 book 举例
实体类这样写,使用注解进行属性注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Student {
@Value(value = "illusory")
private String name;
@Value(value = "23")
private int age;
@Autowired
private Book book;
}
public class Book {
@Value(value = "defaultType")
private String type;
@Value(value = "defaultName")
private String name;
}

配置文件就不用写各种 property 属性注入了。

1
<property name="name" value="illusory"></property>

使用@Autowired后也不用配置引用对象了。

1
<property name="book" ref="book"></property>

但是还是需要在 xml配置 bean 的基本信息

1
2
3
<bean id="student" class="spring.Student"></bean>
<bean id="book" class="spring.Book"></bean>
<context:annotation-config />

如果在实体类加上@Component注解

1
2
3
4
5
6
7
8
9
@Component(value = "student")
public class Student {
@Value(value = "illusory")
private String name;
@Value(value = "23")
private int age;
@Autowired
private Book book;
}

就不用在xml中配置bean了,只需要在xml中配置

1
<context:component-scan base-package="spring"/>

系统可以识别到前面的注解,同时还会自动扫描包下的 bean。

这样xml中只要要一行就搞定了。

4. 自定义初始化与销毁方法

init-method destroy-method属性对应的注解

  • @PostConstruct注解,在对象创建后调用
  • @PreDestroy注解,在对象销毁前调用
1
2
3
4
5
6
7
8
9
@PostConstruct
public void init() {
System.out.println("init");
}

@PreDestroy
public void destory() {
System.out.println("destory");
}

5. @Component和@Configuration 作为配置类的区别

5.1 概述

@Component@Configuration都可以作为配置类,但还是有一定差别的。

1
2
3
4
5
6
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //看这里!!!
public @interface Configuration {
String value() default "";

Spring 中新的 Java 配置支持的核心就是 @Configuration 注解的类

这些类主要包括 @Bean 注解的方法来为 Spring 的 IoC 容器管理的对象定义实例,配置和初始化逻辑。

使用 @Configuration 来注解类表示类可以被 Spring 的 IoC 容器所使用,作为 bean 定义的资源。

1
2
3
4
5
6
7
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}

这和 Spring 的 XML 文件中的非常类似

1
2
3
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

5.2 实例演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Configuration
public static class Config {

@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}

@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}

@Component
public static class Config {

@Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}

@Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
}

第一个代码正常工作,正如预期的那样,SimpleBeanConsumer 将会得到一个单例 SimpleBean 的链接。
第二个配置是完全错误的,虽然 Spring 会创建一个 SimpleBean 的单例bean,但是 SimpleBeanConsumer 将获得另一个SimpleBean实例(也就是相当于直接调用new SimpleBean() ,
这个bean是不归Spring管理的)。

5.3 原因

使用 @Configuration 所有标记为@Bean的方法将被包装成一个 CGLIB包装器,它的工作方式就好像是这个方法的第一个调用,那么原始方法的主体将被执行,最终的对象将在 Spring上下文中注册。所有进一步的调用只返回从上下文检索的 bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//判断是否被@Configuration标注
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
logger.warn("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
//生成代理的class
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
//替换class,将原来的替换为CGLIB代理的class
beanDef.setBeanClass(enhancedClass);
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}

isFullConfigurationClass代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//是否为配置类
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}

//是否为完整配置类
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
//是否为精简配置类
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}

// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}

// Finally, let's look for @Bean methods...
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
//精简配置类包含的注解
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}

6. 参考

https://blog.csdn.net/long476964/article/details/80626930

https://www.jianshu.com/p/89f55286cf21

------------------本文到此结束感谢您的阅读------------------
0%