why(为什么要使用spring注解):
有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让spring自动识别如何装配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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="tiger" class="org.xrq.action.by.Tiger" /> <bean id="zoo" class="org.xrq.action.by.Zoo"> <property name="animal" ref="tiger" /> </bean> </beans>
<property>标签过多会引发两个问题:
- 如果一个Bean中要注入的对象过多,比如十几二十个(这是很正常的),那将导致Spring配置文件非常冗长,可读性与维护性差
- 如果一个Bean中要注入的对象过多,配置麻烦且一不小心就容易出错
因此,为了解决使用<property>标签注入对象过多的问题,Spring引入自动装配机制,简化开发者配置难度,降低xml文件配置大小。
使用注解自动装配与在xml中使用autowire属性自动装配并没有太大差别,但是使用注解方式允许更细粒度的自动装配,可以选择性的标注某一个属性来对其应用自动装配。
spring容器默认禁用注解装配,所以在使用基于注解的自动装配前,我们需要在spring配置中启用它。最简单的启用方式是使用spring的context命名空间配置中的<context:annotation-config>元素
在beans标签中加入<context:annotation-config/>即是告诉spring我们打算使用基于注解的自动装配。一旦配置完成,我们就可以对代码添加注解,标识spring应该为属性、方法、构造器进行自动装配。
spring 3支持几种不同的用于自动装配的注解
1.spring 自带的@Autowired注解,
2.JSR-330的@Inject注解:是为了统一各种依赖注入框架的编程规范,JCP发布了是最新的规范,称之为JSR-330,其中@Inject是其核心部件,该注解几乎可以完全替换spring的@Autowired,
可以用来自动装配方法,属性,构造器,基本上等同于@Autowired
3.JSR-250的@Resource注解
@Value:spring 3.0引入的,它是一个新的装配注解,可以让我们使用注解装配string类型的值和基本类型的值,如int ,boolean
@Value("Eruption")
private String song;
表明spring为string类型的属性装配了一个string 类型的值
<content:annotation-config>有助于完全消除spring配置中的<property>和<constructor-arg>元素,但是我们仍然需要使用<bean>元素显式的定义bean.
但是spring还有另一个技巧,<context:annotation-scan>元素除了完成与<context:annotation-config>一样的工作,还允许spring自动检测bean和定义bean.这就意味着不使用<bean>元素,spring
应用中的大多数bean都能够实现定义和装配。<context:annotation-scan>元素会扫描指定的包及其所有的子包,并查找出能够自动注册为spring bean的类,那么它又是如何知道哪些类要注册为spring的bean呢?
默认情况下,<context:annotation-scan>查找使用构造型注解所标注的类,这些特殊的注解如下:
@Component:通用的构造型注解,标识该类为spring组件
@Controller:标识将该类定义为spring mvc的controller
@Repository:标识将该类定义为数据仓库,一般用于持久化层的 DAO component
@Service:标识将该类定义为服务
使用@Component标注的任意自定义的注解。
在项目中,我们可以将所有自动扫描组件都用 @Component 注释,Spring 将会扫描所有用 @Component 注释过得组件。 实际上,@Repository 、 @Service 、 @Controller 三种注释是为了加强代码的阅读性而创造的,可以在不同的应用层中,用不同的注释,我们可以在上一个项目的基础上改一下注释
package com.springination.springidol import org.springframework,stereotype.Component; @Component public class Guitar implements Instrument{ public void play(){ System.out.println("strum strum strum"); } }
spring 扫描com.springination.springidol包时,会发现使用@Component 注解所标注的Guitar,并将其自动注册为spring的bean ,bean的id=guitar(无限定类名)
如果出现@Component("eddie"),则beanid=eddie,而不是无限定类名
<context:component-scan>也会自动加载使用@Configuration注解所标注的类,@Configuration注解会做为一个标识告诉spring,这个类包含一个或多个spring bean的定义,这此bean的定义是使用@Bean注解所标注的方法
@Bean public Peromer duke(){ return new Juggler(); }
这个简单方法就是Java配置,它等价于我们之前使用xml所配置的<bean>元素,@Bean告知spring这个方法将返回一个对象,该对象应该被注册为spring 上下文中的一个bean,方法名将作为该bean的id,在该方法中所实现的所有逻辑都是为了创建一个bean,即new Juggler()对象出来。
@Bean private Poem sonnet29(){ return new Sonnet29(); } @Bean public Performer poeticDuke(){ return new PoeticJuggler(sonnet29()); }
创建一个PoeticJuggler bean,通过构造器为它装配sonnet29 bean.
注意:在spring的java配置中,通过声明方法引用一个bean并不等同于调用该方法,如果真是这样,每次调用sonnet29(),都将得到该bean 的一个新的实例
通过使用@Bean注解标注sonnet29()方法,会告知spring 我们希望该方法定义的bean要被注册到spring的应用上下文中,因此,在其他bean的声明方法中引用这个方法时
spring会拦截该方法的调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。
带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。最简单可行的 @Configuration 类如下所示:
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
上面的代码将等同于下面的 XML 配置:
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>
example:Instrumentlist 类中有instrument属性,期望自动装配instrument属性
@Autowired
private Instrument instrument;//此处需要删除相应的setter方法
===》等同于
@Autowired
public void setInstrument(Instrument instrument){
this.instrument = instrument;
}
//当spring发现我们对setInstrument方法使用了@Autowired注解时,spring会尝试对该方法执行byType自动装配
<bean id="instrument" class="com.zf.springidol.Instrument" />
<bean id="kenny" class="com.zf.springidol.Instrumentlist">
<property name="animal" ref="instrument" /> //若使用了@Autowired注解其属性instrument后,则这个property就可以删除了
</bean>
如果有足够多的bean都完全满足装配条件,并且都可以被装配到属性或参数中,@Autowired注解没有办法选择哪一个bean才是它真正需要的,此时会抛出一个
NoSuchBeanDefinitionException异常,表示装配失败了。此时可以配合使用@Qualifier注解(此注解是通过指定beanid来缩小装配范围。)
自动装配就是让Spring自动满足bean依赖的一种方式,在满足依赖的过程中,会在Spring的上下文中寻找匹配一个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的 @Autowried注解。
比如说CDPlayer类,它在构造器上添加了 @Autowried注解,这表明当创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。
package soundsystem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CDPlayer implements MediaPlayer { private CompactDisc cd; @Autowired public CDPlayer(CompactDisc cd) { this.cd = cd; } public void play() { cd.play(); } }
总结一下,自动装配bean的过程:
一、把需要被扫描的类,添加 @Component注解,使它能够被Spring自动发现。
二、通过显示的设置Java代码 @ComponentScan注解或XML配置,让Spring开启组件扫描,并将扫描的结果类创建bean。
三、@Autowried注解能偶实现bean的自动装配,实现依赖注入。
创建CompactDisc接口:
package soundsystem;
public interface CompactDisc {
void play();
}
实现CompactDisc接口:
package soundsystem;
import org.springframework.stereotype.Component;
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
在SgtPeppers类上使用了 @Component注解,这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean,不需要显示配置SgtPeppers bean。
@Autowired与@Resource异同:
1° @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2° @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。
3° @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
一个创建自定义注解的例子
public class AnnotionOperator { public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException { SayHiEmlement element = new SayHiEmlement(); // 初始化一个实例,用于方法调用 Method[] methods = SayHiEmlement.class.getDeclaredMethods(); // 获得所有方法 for (Method method : methods) { SayHiAnnotation annotationTmp = null; if((annotationTmp = method.getAnnotation(SayHiAnnotation.class))!=null) // 检测是否使用了我们的注解 method.invoke(element,annotationTmp.paramValue()); // 如果使用了我们的注解,我们就把注解里的"paramValue"参数值作为方法参数来调用方法 else method.invoke(element, "Rose"); // 如果没有使用我们的注解,我们就需要使用普通的方式来调用方法了 } }