基于配置的注解
从Spring 2.5开始可以使用注解来配置依赖注入,所以你可以在组件类、方法以及域的声明中使用注解来替代XML的配置说明。
如果注解和XML都配置了依赖注入,那么注解会覆盖XML中的属性配置的依赖注入。
在Spring容器中没有默认打开注解注入,所以在我们使用基于注解注入之前,我们需要在我们Spring配置文件中打开它。如果你想使用注解注入需要进行相关的配置,比如下面这样:
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
一旦<context:annotation-config/>
被配置,你就可以在组件类、方法以及构造中使用注解注入。下面看下几个重要的注解注入:
No | Annotation | Description |
---|---|---|
1 | @Required | 该注解应用到bean属性的setter方法中 |
2 | @Autowired | 该注解可以在bean属性的setter方法、非setter方法、构造以及其属性中 |
3 | @Qualifier | 该注解和@Autowired一起使用,为了确定哪一个bean是要被注入的 |
4 | JSP-250Annotations | Spring支持JSR-250的注解,其中包含@Resource, @PostConstruct和 @PreDestroy |
Spring @Required Annotation
@Required 注解适用于bean属性的setter方法,预示着受影响的bean必须在XML配置文件中配置。否则,容器会抛出BeanInitializationException
异常,下面的例子将说明该问题
Example
Student.java
package com.soygrow.RequireAnnotation;
import org.springframework.beans.factory.annotation.Required;
public class Student {
private Integer age;
private String name;
@Required
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
MainApp.java
package com.soygrow.RequireAnnotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("RequiredBeans.xml");
Student student = (Student) context.getBean("student");
System.out.println("Name : " + student.getName());
System.out.println("Age : " + student.getAge());
}
}
RequiredBeans.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="student" class="com.soygrow.RequireAnnotation.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
</beans>
如果一切正常,运行结果如下所示:
Name : Zara
Age : 11
如果在XML中不加一下代码,会抛出BeanInitializationException
异常
<property name="age" value="11"/>
BeanInitializationException: Property 'age' is required for bean 'student'
小结
- @Required是作用于setter方法
- 一旦使用@Required,那么容器在初始化bean的时候必须要进行set
- @Required注解表示受影响的bean属性必须在XML配置文件中进行配置填充,不然会抛异常
- Spring使用@Required注解依赖检查
Spring @Autowired Annotation
@Autowired 注解提供了更细微的控制,关于在哪里以及如何完成自动装配。@Autowired 可以在setter方法上自动装配bean,就像@Required注解、构造函数、具有任意名称或者多个参数的属性或方法一样。
@Autowired on Setter Methods
你可以在setter方法中使用@Autowired注解来消除XML配置文件中的属性。当Spring发现一个@Autowired注解在setter方法中使用,那么它会尝试autowired = byType的方式进行依赖注入。
Example
SpellChecker.java
package com.soygrow.AutowiredAnnotation;
public class SpellChecker {
public SpellChecker() {
System.out.println("Inside SpellChecker constructor ...");
}
public void checkSpelling() {
System.out.println("Inside checkSpelling ...");
}
}
TextEditor.java
package com.soygrow.AutowiredAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public void setSpellChecker(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
MainApp.java
package com.soygrow.AutowiredAnnotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutowiredBeans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
AutowiredBeans.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="textEditor" class="com.soygrow.AutowiredAnnotation.TextEditor">
</bean>
<bean id="spellChecker" class="com.soygrow.AutowiredAnnotation.SpellChecker">
</bean>
</beans>
如果一切正常,运行的结果如下:
Inside SpellChecker constructor ...
Inside checkSpelling ...
小结:
- @Autowired 作用在setter方法上,可以免去XML配置中的<property>
,Spring容器会自动根据type进行依赖注入
@Autowired on Properties
@Autowired 在setter方法上使用,可以消除XML配置中的<property>
属性,进一步的在成员变量上使用,可以消除setter方法。当你使用<property>
属性去自动装配属性值的时候,Spring容器会自动分配传递的值或者引用的值给autowired的属性。
因此使用@Autowired的TextEditor.java的文件将会像下面这样:
package com.soygrow.AutowiredAnnotation2;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
@Autowired
private SpellChecker spellChecker;
public TextEditor() {
System.out.println("Inside TextEditor constructor ..." );
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
SpellChecker.java的代码是差不多的,这里就不贴代码了,可以参照以前的代码。
MainApp.java
package com.soygrow.AutowiredAnnotation2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutowiredBeans2.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
AutowiredBeans2.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="textEditor" class="com.soygrow.AutowiredAnnotation2.TextEditor">
</bean>
<bean id="spellChecker" class="com.soygrow.AutowiredAnnotation2.SpellChecker">
</bean>
</beans>
如果一切正常,运行的结果是:
Inside TextEditor constructor ...
Inside SpellChecker constructor ...
Inside checkSpelling ...
小结
- 进一步@Autowired在类的成员变量上使用,可以进一步消除类中setter方法,不仅仅是XML配置文件中的
<property>
@Autowired on Constructors
同样你可以在构造方法中应用@Autowired,构造方法的@Autowired注解表明党创建bean的时候就自动装配构造方法,甚至在XML配置文件中如果没有<constructor-arg>
元素也是可以行的。下面是相应的示例:
Example
TextEditor.java
package com.soygrow.AutowiredAnnotation3;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public TextEditor(SpellChecker spellChecker) {
System.out.println("Inside TextEditor constructor ..." );
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
MainApp.java
package com.soygrow.AutowiredAnnotation3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutowiredBeans3.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
AutowiredBeans3.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="textEditor" class="com.soygrow.AutowiredAnnotation3.TextEditor">
</bean>
<bean id="spellChecker" class="com.soygrow.AutowiredAnnotation3.SpellChecker">
</bean>
</beans>
如果一切正常,运行结果:
Inside TextEditor constructor ...
Inside SpellChecker constructor ...
Inside checkSpelling ...
小结
- @Autowired 注解构造函数,Spring 容器会自动将bean作为参数注入到对应的地方
@Autowired with (required=false) option
By default, the @Autowired annotation implies the dependency is required similar to @Required annotation, however, you can turn off the default behavior by using the (required=false) option with @Autowired.
默认情况下,@Autowired 注解实现依赖注入和 @Required 相类似,然而,你可以是用使用 required=false
取消@Autowired的默认设置。
The following example will work even if you do not pass any value for age property but still it will demand for the name property. You can try this example yourself because this is similar to the @Required annotation example except that only Student.java file has been changed.
下面的例子在你不传age值的情况下,仍然可以正常工作,但是它仍然需要name属性。你可以尝试下面的例子,和上面例子对比,只有下面的文件发生了变化:
Student.java
package com.soygrow.AutowiredAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
public class Student {
private Integer age;
private String name;
@Autowired(required=false)
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Autowired
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
总结
- @Autowired 注解setter方法,免去在XML中配置
<property>
属性,如果在Autowired中添加(required=false),那么该参数在没有配置的情况也是可以正确执行的,表示该参数不是必须参数。这个和@Required正好相反,@Required表示参数是必须参数 - @Autowired 注解对象,不仅仅免去XML中的
<property>
属性配置,也省去了setter方法 - @Autowired 注解构造方法,Spring 容器会自动找到对应的对象进行注入。
Spring @Qualifier Annotation
存在一种场景,你可以创建同种类型的bean多个,但是你只想绑定其中的一个。再这种场景下,你可以使用@Qualifier注解去解除歧义,使用确切的bean进行装配。下面的例子将说明这个问题:
Example
Student.java
package com.soygrow.QualifierAnnotation;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Profile.java
package com.soygrow.QualifierAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Profile {
@Autowired
@Qualifier("student1")
private Student student;
public Profile() {
System.out.println("Inside Profile constructor ...");
}
public void printAge() {
System.out.println("Age : " + student.getAge());
}
public void printName() {
System.out.println("Name : + " + student.getName());
}
}
MainApp.java
package com.soygrow.QualifierAnnotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("QualifierBeans.xml");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
}
}
QualifierBeans.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<bean id="profile" class="com.soygrow.QualifierAnnotation.Profile"></bean>
<bean id="student1" class="com.soygrow.QualifierAnnotation.Student">
<property name="age" value="11"/>
<property name="name" value="Zara"/>
</bean>
<bean id="student2" class="com.soygrow.QualifierAnnotation.Student">
<property name="name" value="Nuha"/>
<property name="age" value="2"/>
</bean>
</beans>
如果一切正常,运行的结果:
Inside Profile constructor ...
Age : 11
Name : + Zara
总结
- @Qualifier 可以指定确切的bean对象进行注入,通过名称来区分
- 和@Autowired 一起使用
Spring JSR-250 Annotations
Spring 也支持JSR-250注解,其中包含@PostConstruct
、@PreDestroy
和@Resource
注解。虽然这些注解你不是真的需要,因为你又其他替代选择,可以作为了解的知识。
@PostConstruct and @PreDestroy Annotations
定义一个bean的setup和teardown,我们可以使用<bean>
的init-method
和destroy-method
参数来指定。init-method
指定了一个方法,该方法会在bean初始化的时候被调用,同样destroy-method
指定了bean在销毁时回调的方法。
你可以使用@PostConstruct
注解作为初始化回调方法的替代,使用@PreDestroy
注解作为销毁回调方法的替代。
Example
HelloWorld.java
package com.soygrow.JSR250Annotation;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
@PostConstruct
public void init() {
System.out.println("Bean is going through init ...");
}
@PreDestroy
public void destroy() {
System.out.println("Bean will destroy now ...");
}
}
MainApp.java
package com.soygrow.JSR250Annotation;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Jsr250Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
context.registerShutdownHook();
}
}
Jsr250Beans.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<bean id="helloWorld" class="com.soygrow.JSR250Annotation.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
如果一切正常,运行的结果是:
Bean is going through init ...
Bean will destroy now ...
小结
- @PostConstruct 可以作为XML中 init-method 的替代
- @ProDestroy 可以作为XML中 destroy-method 的替代
@Resource Annotation
你可以使用@Resource注解在某个域或者setter方法上,并且它和在Java EE 5 中作用一样(之前不是搞java的,没有明白)。@Resource 带一个‘name’属性,这个name指明一个注入的bean。下面的例子可以说明该问题:
Example
SpellChecker.java
package com.soygrow.ResourceAnnotation;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling(){
System.out.println("Inside checkSpelling." );
}
}
TextEditor.java
package com.soygrow.ResourceAnnotation;
import javax.annotation.Resource;
public class TextEditor {
private SpellChecker spellChecker;
@Resource(name = "spellChecker")
public void setSpellChecker(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
MainApp.java
package com.soygrow.ResourceAnnotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ResourceBeans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
ResourceBeans.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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<bean id="textEditor" class="com.soygrow.ResourceAnnotation.TextEditor">
</bean>
<bean id="spellChecker" class="com.soygrow.ResourceAnnotation.SpellChecker"></bean>
</beans>
如果一切正常,运行结果:
Inside SpellChecker constructor.
Inside checkSpelling.
小结
- @Resource 通过名称实现依赖注入,可以指定具体的bean