参考:http://www.cnblogs.com/xrq730/p/5313412.html
http://blog.youkuaiyun.com/baple/article/details/17891755
什么是注解
传统的Spring做法是使用.xml文件来对bean进行注入或者配置aop、事物,这么做有两个缺点:
(1)如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多总之这将导致配置文件的可读性和可维护性变得很低
(2)在开发中.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率
为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。本次主要讲解@Autowired、@Resource和@Service注解
不使用注解
先定义三个Bean
public class Monkey {
private String monkeyName = "MonkeyKing";
public String toString()
{
return "MonkeyName:" + monkeyName;
}
}
public class Tiger {
private String tigerName = "TigerKing";
public String toString()
{
return "TigerName:" + tigerName;
}
}
public class Zoo {
private Tiger tiger;
private Monkey monkey;
public void setTiger(Tiger tiger)
{
this.tiger = tiger;
}
public void setMonkey(Monkey monkey)
{
this.monkey = monkey;
}
public Tiger getTiger()
{
return tiger;
}
public Monkey getMonkey()
{
return monkey;
}
public String toString()
{
return tiger + "\n" + monkey;
}
}
applicationContext.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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="zoo" class="com.spring.ioc.Zoo">
<property name="tiger" ref="t"></property>
<property name="monkey" ref="m"></property>
</bean>
<bean id="t" class="com.spring.ioc.Tiger"></bean>
<bean id="m" class="com.spring.ioc.Monkey"></bean>
</beans>
Test
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Zoo z=(Zoo)ac.getBean("zoo");
System.out.println(z.toString());
}
输出:
TigerName:TigerKing
MonkeyName:MonkeyKing
使用@Autowired
默认按照类型自动装配注入,如果需要进行接口注入,也就是说可能遇到这种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,比如实现同一个接口的不同类,这时候需要结合@Qualifier使用,这时候是按照名称装配
###按照类型装配
把上面的Zoo.java中的set/get方法去掉
public class Zoo {
@Autowired
private Tiger tiger;
@Autowired
private Monkey monkey;
public String toString()
{
return tiger + "\n" + monkey;
}
}
applicationContext.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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.spring.car"></context:component-scan>
<bean id="zoo" class="com.spring.ioc.Zoo"></bean>
</beans>
测试代码不变,输出结果也是:
TigerName:TigerKing
MonkeyName:MonkeyKing
注意:上面的applicationContext有关Tiger和Cat的定义必须要定义,否则会报错,因为@Autowired注解要去寻找的是一个Bean,Tiger和Monkey的Bean定义都给去掉了,自然就不是一个Bean了,Spring容器找不到就抛出异常。
如果不想要抛出异常,需要在@Autowired加上required=false:
public class Zoo {
@Autowired(required=false)
private Tiger tiger;
@Autowired(required=false)
private Monkey monkey;
public String toString()
{
return tiger + "\n" + monkey;
}
}
这个时候尽管没有Tiger和Cat的两个bean,也只是输出两个null不会抛出异常
###按照名称装配
首先定义一个接口
public interface Car {
public String name();
}
然后定义两个实现类:
public class BMW implements Car{
public String name() {
// TODO Auto-generated method stub
return "BMW";
}
}
public class BenC implements Car{
public String name() {
// TODO Auto-generated method stub
return "BenC";
}
}
实现一个工厂类:
public class CarFactory {
@Autowired
private Car car;
public String toString()
{
return car.name();
}
}
<?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:component-scan base-package="com.spring.car"></context:component-scan>
<bean id="cf" class="com.spring.car.CarFactory"></bean>
<bean id="bMW" class="com.spring.car.BMW"></bean>
<bean id="benz" class="com.spring.car.BenC"></bean>
</beans>
显然如果只是按照上面这样写肯定会报错,因为无法识别Car到底对应的是哪个实现类,这时候就要用到@Qualifier,让它指明是哪一个实现类,在@Qualifier后面的是对应bean的名字
public class CarFactory {
@Autowired
@Qualifier("bMW")
private Car car;
public String toString()
{
return car.name();
}
}
执行结果:
BMW
使用@Resource
@Resource和@Autowired功能类似,但是也有区别(后面说)
在使用@Resource的时候要导入包common-annotations.jar,否则会报错
上面的CarFactory可以写成这样:
ublic class CarFactory {
@Resource(name="bMW")
private Car car;
public String toString()
{
return car.name();
}
}
其他都不变,也能得到预期结果
@Autowired和@Resource的区别:
(1)@Resource默认按照名称装配,可以通过name属性指定,如果没有指定name属 性,当注解标注在字段上,就会默认按照被标注字段的类型进行注入(比如前面的Zoo的Tiger变量,如果使用@Resource,没有指定name属性,name就会按照Tiger类型寻找bean),当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找 依赖对象
(2)@Autowired默认按照类型装配,按照类型装配的bean必须存在否则报错,可以通过使用required=false避免报错。
(3)当创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配(比如实现同一个接口的不同类),使用@Autowired和Qualifier的组合,也可以使用@Resource,这时两者是等效的
(4)@Resource注解是又J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用 @Resource的方式;
(5)@Resource和@Autowired都可以书写标注在字段或者该字段的setter方法之上
使用@Service
@Service可以自动把被标注的类声明为一个bean
以前面的Car举例,BenC和BMW前面都加一个@Service
@Service
public class BMW implements Car{
public String name() {
// TODO Auto-generated method stub
return "BMW";
}
}
@Service
public class BenC implements Car{
public String name() {
// TODO Auto-generated method stub
return "BenC";
}
}
CarFactory也要加:
@Service
public class CarFactory {
@Autowired
@Qualifier("benC")
private Car car;
public String toString()
{
return car.name();
}
}
applicationContext.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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-s
can base-package="com.spring.car"></context:component-scan>
</beans>
测试:
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
CarFactory cf=(CarFactory)ac.getBean("carFactory");//注意这里要把前面写的getBean("cf")改成getBean("carFactory")
System.out.println(cf.toString());
}
}
输出:
BenC
注意:@Service把被注解的类声明为bean,默认的bean的id是类名首字母小写,其他的不变,但是有一个发现就是我用BMW测试的时候,bean的id是BMW,所以如果全是大写,bean的id和类名相同。
当然,bean的id可以按照我们自己的意愿自己改变,比如CarFactory的bean的id我仍然改为cf:
@Service("cf")
public class CarFactory {
......
}
测试:
public class Test {
public static void main(String[] args) {
......
CarFactory cf=(CarFactory)ac.getBean("cf");
System.out.println(cf.toString());
}
}
仍然能得到预期结果
补充
从这里可以看出@Autowired等注解是为了方便维护bean之间的关系而产生的,如果没有这些注解,那么每次要定义一个包含很多个其他bean的bean(比如上面的zoo包含了Monkey和Tiger),都放到xml文件里面的话,写起来会非常崩溃,在大型项目里面,一个bean可能会被多个其他文件引用,如果每次引用都在xml文件里面定义一遍,写起来和后期维护都是非常麻烦的,有了这些注解,只需要在需要引用其他bean的时候引入就可以了,非常方便。
但是有一个前提是,这些注解方便其他bean的引入,但是引入其他bean的类本身一定要是一个已经被定义的bean,什么意思呢,比如上面Zoo Monkey Tiger的例子,如果我这样写:
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//Zoo z=(Zoo)ac.getBean("zoo");
Zoo z = new Zoo();
System.out.println(z.toString());
最后得到的结果是这样的:
也就是说,tiger和monkey并没有被注入进来,因为你new一个Zoo的时候,这个时候的实例就不是我们xml中定义的bean了,这个bean是你自己new出来的,不是同一个东西,所以这个时候的monkey和tiger属性并没有被初始化