SSM系列(二)---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. 提供方
@Autowired
是 Spring
提供的注解;
@Resource
是J2EE
提供的注解,javax.annotation 包下的注解,来自于JSR-250,需要JDK1.6及以上。
2. 注入方式
@Autowired
只按照Type
注入;
@Resource
默认按Name自动注入,也提供按照Type 注入;
3. 属性
@Autowired
注解可用于为类的属性、构造器、方法进行注值。
默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其 required 属性为false。
@Autowired
注解默认
按照类型
装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合 @Qualifier 注解进行限定,指定注入的bean名称。
@Resource
有两个中重要的属性:name
和type
。
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 的项目中,可能会看到配置项中包含这个配置节点
<context:annotation-config>
这条配置会向 Spring 容器中注册以下4个 BeanPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
注册这4个 BeanPostProcessor 的作用,就是为了你的系统能够识别相应的注解。
如果想使用 @Resource
、@PostConstruct
、@PreDestroy
等注解就必须声明CommonAnnotationBeanPostProcessor
。
如果想使用 @PersistenceContext
注解,就必须声明PersistenceAnnotationBeanPostProcessor
的Bean。
如果想使用 @Autowired注解,那么就必须声明AutowiredAnnotationBeanPostProcessor
的 Bean。
如果想使用 @Required
的注解,就必须声明RequiredAnnotationBeanPostProcesso
r的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 举例 实体类这样写,使用注解进行属性注入
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 属性注入了。
<property name="name" value="illusory"></property>
使用@Autowired
后也不用配置引用对象了。
<property name="book" ref="book"></property>
但是还是需要在 xml配置 bean 的基本信息
<bean id="student" class="spring.Student"></bean>
<bean id="book" class="spring.Book"></bean>
<context:annotation-config />
如果在实体类加上@Component
注解
@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中配置
<context:component-scan base-package="spring"/>
系统可以识别到前面的注解,同时还会自动扫描包下的 bean。
这样xml中只要要一行就搞定了。
4. 自定义初始化与销毁方法
init-method destroy-method属性对应的注解
- @PostConstruct注解,在对象创建后调用
- @PreDestroy注解,在对象销毁前调用
@PostConstruct
public void init() {
System.out.println("init");
}
@PreDestroy
public void destory() {
System.out.println("destory");
}
5. @Component和@Configuration 作为配置类的区别
5.1 概述
@Component
和@Configuration
都可以作为配置类,但还是有一定差别的。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //看这里!!!
public @interface Configuration {
String value() default "";
Spring 中新的 Java 配置支持的核心就是 @Configuration 注解的类。
这些类主要包括 @Bean 注解的方法来为 Spring 的 IoC 容器管理的对象定义实例,配置和初始化逻辑。
使用 @Configuration 来注解类表示类可以被 Spring 的 IoC 容器所使用,作为 bean 定义的资源。
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
这和 Spring 的 XML 文件中的非常类似
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
5.2 实例演示
@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。
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
代码如下:
//是否为配置类
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