文章目录
注解配置
spring
与基于
xml
配置
spring
那种方式好,其实是要看情况的,各有各的好
-
注解配置:使配置更简短,简洁
-
XML
配置:擅长连接组件而不需要接触源代码和重新编译。
注:注解配置
spring
会在基于XML
配置spring
之前执行的,所以你的上下文中即使用了注解配置和XML
配置,如果两个都配置了同一个Bean,那么基于XML
配置的会覆盖前面注解配置的数据。
在使用注解配置spring
前需要先注册相应注解的类,例如,如果需要使用Autowired
注解注入属性值,先得把处理这个注解的类org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
注册到IOC
容器中才能使用Autowired
的功能,注册这些处理类的方式有两种,一种是显示的注入的IOC
容器中,另一种就是在XML
中配置<context:annotation-config/>
隐式的注入容器中。
在XML
中配置<context:annotation-config/>
隐式注入注解的配置类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
在XML
中配置<context:annotation-config/>
显示的注入注解的配置类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:annotation-config/>-->
<bean id="required" class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
</beans>
以上方式其中在XML
中添加<context:annotation-config/>
会隐式的注册一些BeanPostProcessor
如下:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
注:
<context:annotation-config/>
仅仅会检测与它定义的同一个上下文中的bean,不会检测到其他上下文中的注解,如果你在WebApplication
上下文中加入<context:annotation-config/>
配置,那么就只能解析在web
容器中Bean
的注解。
1.9.1 @Required
这个 @Required
注解一般是使用在Bean
属性中的setXXX()
方法上面,用来标注这个属性需要显示注入的或容器依赖注入方式给一个值。
代码使用方式如下:
public class TestBean {
private String name;
@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
此注释指示必须在配置时通过bean定义中的显式属性值或通过自动装配来填充受影响的bean属性。
注:从Spring Framework 5.1开始,@ Required批注已正式弃用,转而使用构造函数注入进行必需的设置(或InitializingBean.afterPropertiesSet()的自定义实现以及bean属性setter方法)
1.9.2 @Autowired
**注 ** : 一般有使用
@Autowired
注释的地方都可以使用JSR 330
的@Inject
注释代替
@Autowired
的几种使用方式如下:
- 在构造函数上添加
@Autowired
注入依赖
可以在一个Bean
中的构造函数中使用@Autowired
来注入依赖,并且从Spring Framework 4.3
开始,如果一个bean中只定义了一个构造函数,则不需要在构造函数上使用
@Autowired`注解也能注入依赖,但是,如果有几个构造函数可用,则至少在一个构造函数上使用@Autowired注解,用来告诉容器使用哪个。
一个构造函数注入情况@Autowired
可加可不加:
public class TestBean {
private TestBeanB testBeanB;
//这里可加可不加都能完成注入
@Autowired
public TestBean(TestBeanB testBeanB) {
this.testBeanB = testBeanB;
}
// get/set...
}
多个构造函数注入情况必须要在一个构造函数上加@Autowired
,不然容器会抛出一个异常找不到构造函数:
public class TestBean {
private TestBeanB testBeanB;
private TestBeanA testBeanA;
//两个构造函数其中一个必须要使用@Autowired,如果不加会抛异常
public TestBean(TestBeanB testBeanB,TestBeanA testBeanA) {
this.testBeanB = testBeanB;
this.testBeanA = testBeanA;
}
@Autowired
public TestBean(TestBeanB testBeanB) {
this.testBeanB = testBeanB;
}
// get/set...
}
-
在
setXXX
方法上添加@Autowired
注入依赖public class TestBean { private TestBeanB testBeanB; public TestBeanB getTestBeanB() { return testBeanB; } @Autowired public void setTestBeanB(TestBeanB testBeanB) { this.testBeanB = testBeanB; } }
-
在普通方法上添加
@Autowired
注入依赖public class TestBean { private TestBeanB testBeanB; @Autowired public void fillAttribute(TestBeanB testBeanB){ this.testBeanB = testBeanB; } public TestBeanB getTestBeanB() { return testBeanB; } public void setTestBeanB(TestBeanB testBeanB) { this.testBeanB = testBeanB; } }
-
在属性上添加
@Autowired
注入依赖public class TestBean { @Autowired private TestBeanB testBeanB; // get/set... }
-
在属性和构造函数上组合添加
@Autowired
注入依赖public class TestBean { @Autowired private TestBeanB testBeanB; private TestBeanA testBeanA; @Autowired public TestBean(TestBeanA testBeanA) { this.testBeanA = testBeanA; } // get/set... }
-
还可以在数组,集合,Map等容器中添加
@Autowired
注入依赖,同样可以支持,属性,构造函数,方法这些方式,跟上述一致。public class TestBean { //这里可以是数组,集合,Map等容器,同样可以支持,属性,构造函数,方法这些方式注入 //这里只展示了属性注入 @Autowired private TestBeanB[] testBeanB; // get/set... }
-
可以通过设置
@Autowired
的属性required
为false,可以指示容器跳过这个注入点操作,不注入对应的值,required
默认值为true
。public class TestBean { @Autowired(required = false) private TestBeanB testBeanB; // get/set... }
-
使用
Java8
中的java.util.Optional
或者是JSR-305
中的@Nullable
注解组合@Autowired
也可以达到跳过莫个注入点操作,不注入对应的值。public class TestBean { @Autowired public void setTestBeanB(Optional<TestBeanB> testBeanB) { //... } @Autowired public void setTestBeanB(@Nullable TestBeanB testBeanB) { //... } }
-
也可以将
@Autowired
注解放在(BeanFactory
,ApplicationContext
,Environment
,ResourceLoader
,ApplicationEventPublisher
,MessageSource
)这些接口以及它们的实现类中(例如ConfigurableApplicationContext或ResourcePatternResolver) 将自动解析而无需进行特殊设置public class TestBean { @Autowired private ApplicationContext applicationContext; public String getName(){ //这里可以直接使用 上面注入的ApplicationContext 容器 return this.applicationContext.getBean("testBeanA", TestBeanA.class).getName(); } }
注 :
@Autowired
,@Inject
,@Value
和@Resource
批注由Spring
BeanPostProcessor
实现处理。
这意味着您不能在自己的BeanPostProcessor
或BeanFactoryPostProcessor
类型(如果有)
中应用这些注释。必须使用XML
或Spring
@Bean
方法显式使用这些类型。
1.9.3 @Primary
让@Autowired
注入不纠结
由于在定义Bean
的时候可以有多个不同名称对应同一个Class
路径类的情况,所以通常系统会出现同中类型有多个Bean
,那么@Autowired
在按类型注入Bean
的时候就不指定到底要注入那个一个对象了,而@Primary
正好可以表示多个Bean
中可以指明容器在注入的时候优先注入添加了@Primary
注解的对象,也就是同类型的多个对象中添加了@Primary
注解的被注入的优先级最高。
使用java
代码定义Bean
@Configuration
public class JavaConfig {
@Primary
@Bean
public TestBeanB firstTestBean() {
TestBeanB testBeanB = new TestBeanB();
testBeanB.setName("张三");
return testBeanB;
}
@Bean
public TestBeanB secondTestBean() {
TestBeanB testBeanB = new TestBeanB();
testBeanB.setName("张三");
return testBeanB;
}
@Bean
public TestBean testBean() {
return new TestBean();
}
}
使用XML
形式定义Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="testBean"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
</bean>
<bean id="testBeanB1"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB"
primary="true">
<property name="name" value="李四"/>
</bean>
<bean id="testBeanB2"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<property name="name" value="李四"/>
</bean>
</beans>
在Bean
中使用@Autowired
注入@primary
选择好的对象
public class TestBean {
@Autowired
private TestBeanB testBeanB;
// get/set ....
}
1.9.4 @Qualifiers
让@Autowired
注入指定名称的Bean
在定义同类型的多个Bean
的时候还可以使用Qualifier
标注别名来区分对象,然后在目标类中使用@Autowired
和@Qualifier
注解组合注入对应别名的对象。
定义Bean
可以使用java
配置定义也可以使用XML
定义,这里就不展示java
配置定义了,直接展示XML
怎么使用<qualifier>
标签顶一个对象的别名。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="testBean"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
</bean>
<bean id="testBeanB1"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier value="main"></qualifier>
<property name="name" value="qualifier"/>
</bean>
<bean id="testBeanB2"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier value="action"></qualifier>
<property name="name" value="qualifier"/>
</bean>
</beans>
上述利用<qualifier>
定义好Bean
就可以在目标类中使用@Autowired
和@Qualifier
注解来注入指定名称的对象了,同样的@Qualifier
注解也可以使用在构造方法,属性,普通方法,方法参数等@Autowired
注解可以使用的地方。
public class TestBean {
@Autowired
@Qualifier(value = "main")
private TestBeanB testBeanB;
// get/set ...
}
还可以使用自定义的注解来定义对象的别名,使用方法基本和Qualifier
一致,只是自定义的注解需要指定Type
类型。
如何定义@Qualifier
类型的自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.PARAMETER})
@Qualifier
public @interface Genre {
String value() default "";
}
使用自定义的Qualifier
类型的注解定义对象,注意自定义的注解需要设置Type
属性为自定义注解的完全限定名称。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="testBean"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
</bean>
<bean id="testBeanB1"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
<property name="name" value="qualifier"/>
</bean>
<bean id="testBeanB2"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>
<property name="name" value="qualifier"/>
</bean>
</beans>
上述使用自定义注解给Bean
名称的时候也可以使用Bean
标签的元数据标签<meta/>
来代替<qualifier>
标签把value
值用<meta/>
表示前提是在没有<qualifier>
标签,因为<qualifier>
标签的优先级远高于<meta/>
标签。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="testBean"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
</bean>
<bean id="testBeanB1"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
<property name="name" value="qualifier"/>
</bean>
<bean id="testBeanB2"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<!--<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>-->
<!-- 使用meta 替换 qualifier -->
<meta key="value" value="action"/>
<property name="name" value="qualifier"/>
</bean>
</beans>
使用自定义的注解结合@Autowired
注解注入对应的依赖
public class TestBean {
private TestBeanB testBeanB;
@Autowired
@Genre(value = "main")
private TestBeanB testBeanB2;
public TestBeanB getTestBeanB() {
return testBeanB;
}
@Autowired
public void setTestBeanB(@Genre(value = "action") TestBeanB testBeanB) {
this.testBeanB = testBeanB;
}
}
1.9.5 可以使用泛型来限定对象
有时候使用泛型<>也可以达到qualifier
一致的作用来区分不同的对象通过容器判断注入到目标对象。
像下面这种情况就只会注入父接口是Store
,且泛型类型是Integer
的对象存到集合中,其他泛型的不会注入。
public class TestBeanA {
private List<Store<Integer>> storeList;
}
1.9.6 可以使用CustomAutowireConfigurer
添加自定义的qualifier
类型的注解
在介绍怎么自定义qualifier
类型的注解的时候,我们的做法是直接在自定义的注解里面引入@Qualifier
注解可以查看上述是怎么自定义的,其实可以不在自定义中引入@Qualifier
注解转而在org.springframework.beans.factory.annotation.CustomAutowireConfigurer
类中添加自定义的Qualifier
类型的注解也可以达到注解@Qualifier
一样的效果,因为这个类最终会把自定义的注解添加到和@qualifier
注解同一个处理类中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="testBean"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
</bean>
<bean id="testBeanB1"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
<property name="name" value="qualifier"/>
</bean>
<bean id="testBeanB2"
class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>
<property name="name" value="qualifier"/>
</bean>
<bean id="customQualifier"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre</value>
</set>
</property>
</bean>
</beans>
1.9.7 使用@Resource
注入
Spring
还通过在字段或bean``setXXX
方法上使用JSR-250 @Resource
批(javax.annotation.Resource
)支持注入@Resource
具有名称属性。默认情况下,Spring
将注入Bean
名称为name
属性值的对象,如果没有指定名称,则默认名称是从字段名称或setter方法派生的。如果是字段,则采用字段名称。在使用setter方法的情况下,它采用参数bean
属性名称。@Resource
注解会先根据属性名称去查找Bean
名称为属性名称的对象,如果没有找到,再根据属性类型去查找对应的Bean
。@Resource
也可以跟@Autowired
一样直接注入下面这些接口以及他们的实现类:BeanFactory
,ApplicationContext
,ResourceLoader
,ApplicationEventPublisher
和MessageSource
等。
public class TestBean {
//1. 使用在属性上
@Resource(name = "testBeanBResource")
private TestBeanB testBeanB;
//也可以跟@Autowired一样直接注入ApplicationContext
@Resource
private ApplicationContext applicationContext;
public TestBeanB getTestBeanB() {
return testBeanB;
}
//2. 使用在set方法上
@Resource
public void setTestBeanB(TestBeanB testBeanB) {
this.testBeanB = testBeanB;
}
}
1.9.8 使用 @Value
注入
@Value
通常用于注入外部属性,一般都是使用application.properties文件中定义的值,也可以使用SPEL表达式,也还有其他外部的值。
public class TestBean {
//如果没有找到值,就会把ceshi.name这个字符串赋值给name
//如果不想没有值的时候注入ceshi.name字符串,
//就需要声明一个PropertySourcesPlaceholderConfigurer ,并且这个方法必须是static的
@Value("${ceshi.name}")
private String name;
//引入SPEL表达式获取系统属性
@Value("#{systemProperties['os.name']}")
private String other;
//....
}
新建一个ioc.properties
文件
ceshi.name=王五
如果没有找到值,就会把ceshi.name
这个字符串赋值给name,如果不想没有值的时候注入ceshi.name
字符串,
就需要声明一个PropertySourcesPlaceholderConfigurer
,并且这个方法必须是static
的。
@Configuration
@PropertySource("classpath:ioc.properties")
public class JavaConfig {
//...
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
1.9.9 PostContruct
和PreDestory
的使用
CommonAnnotationBeanPostProcessor
不仅可以识别@Resource
注解,还可以识别JSR-250
生命周期注解:javax.annotation.PostConstruct
和javax.annotation.PreDestroy
。在Spring 2.5
中引入的对这些注解的支持为初始化回调
和销毁回调
中描述的生命周期回调机制提供了一种替代方法。PostConstruct
的执行时机为构造器执行完成后立马执行,PreDestroy
的执行时机为对象销毁前一步执行。假设CommonAnnotationBeanPostProcessor
在Spring ApplicationContext
中注册,则在生命周期中与相应的Spring
生命周期接口方法或显式声明的回调方法在同一点调用带有这些注解之一的方法。在以下示例中,演示了初始化,销毁用法:
public class TestBean {
public TestBean() {
System.out.println("构造器方法执行");
}
@PostConstruct
public void postConstruct(){
System.out.println("初始话回掉方法执行");
}
@PreDestroy
public void preDestroy(){
System.out.println("销毁回掉方法执行");
}
}
与@Resource
一样,@PostConstruct
和@PreDestroy
批注类型是JDK 6到8
的标准Java
库的一部分。但是,整个javax.annotation
包与JDK 9
中的核心Java
模块分开,并最终在JDK 11
中删除了。如果需要,现在需要通过Maven Central
获取javax.annotation-api
工件,只需像其他任何库一样将其添加到应用程序的类路径中即可。
参考文献
【https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-postconstruct-and-predestroy-annotations】
【1.9. Annotation-based Container Configuration】