JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(10):通过注解(annotation)装配Bean之(@Configguration、@Component、@Value、@ComponentScan、@Autowired、@Primary、@Qualifier、@Bean)
一、通过注解(annotation)装配Bean
通过之前的学习,我们已经知道如何使用XML装配Bean,但是更多的时候已经不再推荐使用XML的方式去装配Bean,更多的时候会考虑注解(annotation)的方式去装配Bean。
使用注解的方式可以减少XML的配置,注解功能更为强大,它既能实现XML的功能,也能提供自动装配的功能,采用了自动装配后,程序员所需要做的决断就减少了,更加有利于对程序的开发,
这就是"约定优于配置"的开发原则。
在Spring中,它提供了两种方式来让Spring IoC容器发现Bean。
1.组件扫描:通过定义资源的方式,让Spring IoC容器扫描对应的包,从而把Bean装配进来。
2.自动装配:通过注解定义,使得一些依赖关系可以通过注解来完成。
通过扫描和自动装配,大部分的工程师都可以使用Java配置完成,而不是XML,这样可以有效地减少配置和引入大量ML,它解决了在Spring3之前的版本需要大量的XML的配置的问题,
这些问题曾被许多开发者诟病。由于目前注解已经成为Spring开发的主流,同时也必须了解不使用XML同样会存在一些弊端,比如系统存在多个公共配置文件(比如多个properties和xml文件),
如果写在注解里,那么这些公共配置文件就会比较分散,这样 不利于统一的管理,又或者一些来至第三方的类,而不是我们系统开发的配置文件,这时利用xml配置就会比较容易管理。
目前企业所流行的方式是,注解为主,xml为辅。
1、使用@Component装配Bean
@Component:代表Spring IoC容器会把这个类扫描生成Bean实例,其中value属性表示这个Bean的id,语法格式:@Component("beanId")或者@Component(value="beanId")
@Value:代表值的注入,注入的时候Spring IoC会自动转型。
@ComponentScan:代表进行扫描,默认是扫描当前把的路径,POJO的包名和它必须保持一致才能扫描,否则是不会自动装配Bean的。这里需要特别说明一下:
@ComponentScan存在2个配置项:第一个是basePackages,它是由base和package两个单词组成的,而package还使用了复数,意味着它可以配置一个Java包的数组,Spring会根据他的配置
扫描对应的包和子包,将配置好的Bean装配进来;第二个是basePackageClasses,他是由base,package,class三个单词组成,采用复数,意味着可以配置多个类,Spring可以根据配置的类所在的包
进行扫描装配对应配置的Bean。
贴出测试代码:
---------------------->以下着重测试注解:@Component、@Value()<-----------------------------
创建一个:UserBean.java
1 package com.xfwl.spring.annotation; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 测试注解:@Component装配Bean 9 * @author Jason 10 * 11 */ 12 @ComponentScan //启动扫描当前所在包的Bean,注入Spring IoC容器 13 @Component("user") //注入user //@Component("user")或者@Component(value="user") 14 public class UserBean { 15 @Value("xfww") 16 private String uname; 17 @Value("123456") 18 private String upwd; 19 public UserBean(){} 20 public UserBean(String uname,String upwd){ 21 this.uname=uname; 22 this.upwd=upwd; 23 } 24 public String getUname() { 25 return uname; 26 } 27 public void setUname(String uname) { 28 this.uname = uname; 29 } 30 public String getUpwd() { 31 return upwd; 32 } 33 public void setUpwd(String upwd) { 34 this.upwd = upwd; 35 } 36 @Override 37 public String toString() { 38 return "UserBean [uname=" + uname + ", upwd=" + upwd + "]"; 39 } 40 }
创建一个:Manager.java
1 package com.xfwl.spring.annotation; 2 3 import java.util.List; 4 import java.util.Map; 5 import java.util.Properties; 6 import java.util.Set; 7 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.stereotype.Component; 10 /** 11 * 注解测试:@Autoired 自动装配 12 * @function 13 * @author 小风微凉 14 * @time 2018-7-10 上午11:32:01 15 */ 16 @Component("manager") 17 public class Manager{ 18 @Autowired 19 private long id; 20 @Autowired 21 private String mname; 22 @Autowired 23 private int mage; 24 /******getter和setter***********/ 25 public long getId() { 26 return id; 27 } 28 public void setId(long id) { 29 this.id = id; 30 } 31 public String getMname() { 32 return mname; 33 } 34 public void setMname(String mname) { 35 this.mname = mname; 36 } 37 public int getMage() { 38 return mage; 39 } 40 public void setMage(int mage) { 41 this.mage = mage; 42 } 43 }
创建一个测试类:TestAnnotation.java
package com.xfwl.spring.annotation; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * Spring Ioc测试 * @function * @author 小风微凉 * @time 2018-7-10 上午9:55:15 */ public class TestAnnotation { //项目相对路径 private static final String xmlRelPath="com/xfwl/spring/assem/applicationContext.xml"; public static void main(String[] args) { //通过注解拿到Spring IoC容器对象 ApplicationContext ctx=new AnnotationConfigApplicationContext(UserBean.class); //获取Bean对象 UserBean user=(UserBean) ctx.getBean("user"); System.out.println(user.toString()); } }
测试结果:(上面注解正常运行)
1 log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). 2 log4j:WARN Please initialize the log4j system properly. 3 log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 4 UserBean [uname=xfww, upwd=123456]
---------------------->以下着重测试注解:@ComponentScan<-----------------------------
创建几个测试类如下:
ComponScan_1.java
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:55:26 13 */ 14 @Component("serviceImp") 15 //@Primary 16 public class ServiceImpl implements IServices { 17 @Autowired(required=true) 18 private UserBean user; 19 @Override 20 public void show() { 21 System.out.println(this.user.toString()); 22 } 23 /**********getter和setter*******************/ 24 public UserBean getUser() { 25 return user; 26 } 27 public void setUser(UserBean user) { 28 this.user = user; 29 } 30 }
ComponScan_2.java
1 package com.xfwl.spring.annotation.scans; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.stereotype.Component; 5 6 /** 7 * 测试: 8 * [1].扫描注解@ComponnetScan的属性basePackages和basePackageClasses配置项的使用 9 * @function 10 * @author 小风微凉 11 * @time 2018-7-12 上午9:16:15 12 */ 13 @Component("scan_2") 14 public class ComponScan_2 { 15 @Value("scan_2_id") 16 private String id; 17 @Value("scan_2_info") 18 private String info; 19 /*******getter和setter************/ 20 public String getId() { 21 return id; 22 } 23 public void setId(String id) { 24 this.id = id; 25 } 26 public String getInfo() { 27 return info; 28 } 29 public void setInfo(String info) { 30 this.info = info; 31 } 32 /*******重写toString方法******************/ 33 @Override 34 public String toString() { 35 return "ComponScan_2 [id=" + id + ", info=" + info + "]"; 36 } 37 }
一个管理类:ScanManage.java,且这个类中,使用扫面注解:@ComponentScan(basePackageClasses={ComponScan_1.class,ComponScan_2.class,ScanManage.class})
1 package com.xfwl.spring.annotation.scans; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 测试: 9 * [1].扫描注解@ComponnetScan的属性basePackages和basePackageClasses配置项的使用。 10 * [2].自动装配注解@Autowired,配置在属性字段或者属性字段对应的setter方法上。 11 * @function 12 * @author 小风微凉 13 * @time 2018-7-12 上午10:06:55 14 */ 15 //配置注解 16 @Component("scanManage") 17 //第二种方式:配置指定的类,通过反射注入IoC 18 @ComponentScan(basePackageClasses={ComponScan_1.class,ComponScan_2.class,ScanManage.class}) 19 public class ScanManage { 20 @Autowired(required=true) 21 private ComponScan_1 scan_1; 22 private ComponScan_2 scan_2; 23 /*******getter和setter************/ 24 public ComponScan_1 getScan_1() { 25 return scan_1; 26 } 27 public void setScan_1(ComponScan_1 scan_1) { 28 this.scan_1 = scan_1; 29 } 30 public ComponScan_2 getScan_2() { 31 return scan_2; 32 } 33 @Autowired(required=true) 34 public void setScan_2(ComponScan_2 scan_2) { 35 this.scan_2 = scan_2; 36 } 37 /*******重写toString方法******************/ 38 @Override 39 public String toString() { 40 return "ScanManage [scan_1=" + scan_1.getId() + ", scan_2=" + scan_2.getId() + "]"; 41 } 42 }
一个测试类:TestScan.java
1 package com.xfwl.spring.annotation; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 6 import com.xfwl.spring.annotation.scans.ComponScan_1; 7 import com.xfwl.spring.annotation.scans.ComponScan_2; 8 import com.xfwl.spring.annotation.scans.ScanManage; 9 import com.xfwl.spring.annotation.scans2.ScanManage2; 10 11 public class TestScan { 12 //项目相对路径 13 private static final String xmlRelPath="com/xfwl/spring/assem/applicationContext.xml"; 14 public static void main(String[] args) { 15 //通过注解拿到Spring IoC容器对象 16 /** 17 * ScanManage:使用了@ComponnetScan的basePackageClasses的配置项 18 * ScanManage2:使用了@ComponnetScan的basePackages的配置项 19 */ 20 //ApplicationContext ctx=new AnnotationConfigApplicationContext(ScanManage.class);//不可以把@ComponentScan注解在接口中,会报错 21 ApplicationContext ctx=new AnnotationConfigApplicationContext(ScanManage2.class); 22 //获取Bean对象 23 ComponScan_1 scan1_1=(ComponScan_1) ctx.getBean("scan_1"); 24 System.out.println(scan1_1.toString()); 25 ComponScan_2 scan1_2=(ComponScan_2) ctx.getBean("scan_2"); 26 System.out.println(scan1_2.toString()); 27 ScanManage scan1Manage1=(ScanManage) ctx.getBean("scanManage"); 28 System.out.println(scan1Manage1.toString()); 29 } 30 }
测试结果:以上加粗,红色的代码,均可以正常运行,说明注解正常在跑。
总结一下:
不可以把@ComponentScan注解在接口中,会报错
2、获取Spring IoC容器对象的2中形式:
第一种:获取xml配置文件,产生Spring IoC容器对象,有如下几种形式。
FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext(xmlAbsPath);
ApplicationContext ctx=new ClassPathXmlApplicationContext(xmlRelPath);
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext(xmlRelPath);
第二种:通过注解获取Spring IoC容器对象
ApplicationContext ctx=new AnnotationConfigApplicationContext(UserBean.class);
特别说明:UserBean类中必须使用注解:@ComponentScan才可以扫描包下面的POJO装配Bean注入到Spring Ioc容器中。
3、自动装配-@Autowired
上面的注解都没有给Bean对象的属性注入对象,关于这个问题,在注解中略微有点复杂,在大部分情况下建议使用自动装配,因为这样可以减少配置的复杂度,
所以这里先介绍自动装配。
通过学习Spring IoC容器,我们知道Spring是先完成Bean的定义和生成,然后寻找需要注入的资源。也就是当Spring生成所有的Bean后,然后发现这个注解,
它就会在Bean中查找,然后找到对应的类型,将其注入进来,这样就完成了依赖注入了。所谓自动装配技术是一种由Spring自己发现对应的Bean,自动完成装配工作的方式,
它会应用到一个十分常用的注解@Autowrired,这个时候Spring IoC容器会自动根据类型去寻找定义的Bean然后将其注入。
需要注意的是,一旦配置了@Autowrired,在Spring IoC容器中就必须存在对应bean对象,否则就会Bean查找失败,在默认的情况下寻找失败它就会抛出异常,也就是说默认
情况下,Spring IoC容器会认为一定要找到对应的Bean来注入这个属性字段,有时候这并不是一个真实的需要,比如日志,有时候我们会觉得这个字段可有可无,这个时候可以通过
@Autowired的配置项required来改变它,比如@Autowired(required=false)。
以下贴出测试代码:
@Autowired 注解普通属性字段:
1 package com.xfwl.spring.annotation; 2 3 import java.util.List; 4 import java.util.Map; 5 import java.util.Properties; 6 import java.util.Set; 7 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.stereotype.Component; 10 /** 11 * 注解测试:@Autoired 自动装配 12 * @function 13 * @author 小风微凉 14 * @time 2018-7-10 上午11:32:01 15 */ 16 @Component("manager") 17 public class Manager{ 18 @Autowired 19 private long id; 20 @Autowired 21 private String mname; 22 @Autowired 23 private int mage; 24 /******getter和setter***********/ 25 public long getId() { 26 return id; 27 } 28 public void setId(long id) { 29 this.id = id; 30 } 31 public String getMname() { 32 return mname; 33 } 34 public void setMname(String mname) { 35 this.mname = mname; 36 } 37 public int getMage() { 38 return mage; 39 } 40 public void setMage(int mage) { 41 this.mage = mage; 42 } 43 }
@Autowired 注解引用类型字段:
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:55:26 13 */ 14 @Component("serviceImp") 15 //@Primary 16 public class ServiceImpl implements IServices { 17 @Autowired(required=true) 18 private UserBean user; 19 @Override 20 public void show() { 21 System.out.println(this.user.toString()); 22 } 23 /**********getter和setter*******************/ 24 public UserBean getUser() { 25 return user; 26 } 27 public void setUser(UserBean user) { 28 this.user = user; 29 } 30 }
正如之前所谈到的在默认情况下是必须注入成功的,所以这里的required的默认值为true。当把配置修改为false时,就告诉Spring IoC容器,假如在已经定义好的Bean中查找
不到的对应的类型,允许不注入,这样也就没有异常抛出,只是这个字段可能为空,开发者自行校验,以免发生空指针异常。在大部分情况下,都不需要这样修改。
@Autowired除可以配置在属性字段上外,还可以配置在方法上,常见的Bean的setter方法也可以使用它完成注入。
1 private UserBean user; 2 /**********getter和setter*******************/ 3 @Autowired(required=true) 4 public void setUser(UserBean user) { 5 this.user = user; 6 }
在大部分配置中,推荐使用@Autowired注解,这是Spring IoC容器自动装配完成的,使得配置大幅度减少,满足约定优于配置的原则,增强程序的健壮性。
4、自动装配的歧义性(@Primary和@Qualifier)
上面谈到了@Autowired注解,它可以完成一些自动装配功能,并且使用方式十分简单,但是有时候这样的当时并不能使用。这一切的根源来至于按类型的方式,按照Spring的建议,
在大部分情况下会使用接口编程,但是定义一个接口,并不一定只有一个与之对应的实现类。换句话说,一个接口可以有多个实现类。
下面介绍2中消除歧义性的注解,其消除歧义性的理念也不同:
第一种注解@Primary:
注解@Primary代表首要的,当Spring IoC通过一个接口或者抽象类注入对象的时候,由于存在多个实现类或者具体类,就会犯糊涂,不知道采用哪个类为好。
注解@Primary则是告诉Spring IoC容器,请优先该类注入。也就是说,使用了@Primary注解的类将被Spring IoC容器作为优先选择被注入,这样可以消除歧义性。
同样的,或许你会想到将@Primary注解到同一个接口的多个实现类中,这样就会存在多个首选的接口了,但是在Spring IoC容器中这样的定义是允许的,只是在注入的时候将
会抛出异常。但是无论如何@Primary只能解决首要性的问题,而不能解决选择性的问题,简而言之,它不能选择使用接口具体的实现类去注入,仅仅提供一个首要性的选择倾向。
创建2个实现同一个接口的实现子类:
实体POJO类:UserBean.java
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 测试注解:@Component装配Bean 9 * @author Jason 10 * 11 */ 12 @Component("user") //注入user //@Component("user")或者@Component(value="user") 13 public class UserBean { 14 @Value("xfww") 15 private String uname; 16 @Value("123456") 17 private String upwd; 18 public UserBean(){} 19 public UserBean(String uname,String upwd){ 20 this.uname=uname; 21 this.upwd=upwd; 22 } 23 public String getUname() { 24 return uname; 25 } 26 public void setUname(String uname) { 27 this.uname = uname; 28 } 29 public String getUpwd() { 30 return upwd; 31 } 32 public void setUpwd(String upwd) { 33 this.upwd = upwd; 34 } 35 @Override 36 public String toString() { 37 return "UserBean [uname=" + uname + ", upwd=" + upwd + "]"; 38 } 39 40 }
接口:IServices.java
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.stereotype.Component; 5 6 /** 7 * 服务接口:测试注解@Qualifier 8 * @function 9 * @author 小风微凉 10 * @time 2018-7-12 上午11:52:29 11 */ 12 @Component(value="service") 13 public interface IServices { 14 public void show(); 15 }
第一个实现子类:ServiceImpl.java
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:55:26 13 */ 14 @Component("serviceImp") 15 @Primary 16 public class ServiceImpl implements IServices { 17 @Autowired(required=true) 18 private UserBean user; 19 @Override 20 public void show() { 21 System.out.println(this.user.toString()); 22 } 23 /**********getter和setter*******************/ 24 public UserBean getUser() { 25 return user; 26 } 27 public void setUser(UserBean user) { 28 this.user = user; 29 } 30 }
第二个实现子类:ServiceImpl2.java(在此类中使用注解@Primary)
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:55:26 13 */ 14 @Component("serviceImp") 15 @Primary 16 public class ServiceImpl implements IServices { 17 @Autowired(required=true) 18 private UserBean user; 19 @Override 20 public void show() { 21 System.out.println(this.user.toString()); 22 } 23 /**********getter和setter*******************/ 24 public UserBean getUser() { 25 return user; 26 } 27 public void setUser(UserBean user) { 28 this.user = user; 29 } 30 }
在创建一个逻辑管理类:UserManage.java(在此类中添加注解扫描器)
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.context.annotation.ComponentScan; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:58:56 13 */ 14 @Component("manager") 15 @ComponentScan(basePackages={"com.xfwl.spring.annotation.qualifier"})//启动扫描当前所在包的Bean,注入Spring IoC容器 16 public class UserManage { 17 @Autowired(required=true) 18 //@Qualifier("serviceImp2") 19 private IServices service; 20 21 public void show(){ 22 this.service.show(); 23 } 24 /**********getter和setter*******************/ 25 public IServices getService() { 26 return service; 27 } 28 public void setService(IServices service) { 29 this.service = service; 30 } 31 }
再来一个测试类:TestScan.java
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 6 import com.xfwl.spring.annotation.scans.ComponScan_1; 7 import com.xfwl.spring.annotation.scans.ComponScan_2; 8 import com.xfwl.spring.annotation.scans.ScanManage; 9 import com.xfwl.spring.annotation.scans2.ScanManage2; 10 11 public class TestScan { 12 //项目相对路径 13 private static final String xmlRelPath="com/xfwl/spring/assem/applicationContext.xml"; 14 public static void main(String[] args) { 15 ApplicationContext ctx=new AnnotationConfigApplicationContext(UserManage.class); 16 //获取Bean对象 17 UserManage manager=(UserManage) ctx.getBean("manager"); 18 manager.show(); 19 } 20 }
测试结果:注解正常运行
第二种注解@Qualifier:
正如上面谈及到的歧义性,一个重要的原因是Spring在寻找依赖注入的时候采用按类型注入引起的。处理按照类型查找Bean,Spring IoC容器最底层的接口BeanFactory,
也定义了按照名称查找的方法,如果采用名称查找的方法,而不是采用类型查找,那么不就可以消除歧义性了吗?答案是肯定的!而注解@Qualifier就是这样一个注解。
测试代码如下:(由于注解@Primary和注解@Qualifier的测试代码及其相似,所以以下会修改上面的代码来完成测试)
修改ServiceImpl.java,修改部分如下:
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Primary; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:55:26 13 */ 14 @Component("serviceImp") 15 //@Primary 16 public class ServiceImpl implements IServices { 17 @Autowired(required=true) 18 private UserBean user; 19 @Override 20 public void show() { 21 System.out.println(this.user.toString()); 22 } 23 /**********getter和setter*******************/ 24 public UserBean getUser() { 25 return user; 26 } 27 public void setUser(UserBean user) { 28 this.user = user; 29 } 30 }
在UserManage.java中指定:@Qualifier("serviceImp2")
1 package com.xfwl.spring.annotation.qualifier; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.context.annotation.ComponentScan; 6 import org.springframework.stereotype.Component; 7 8 /** 9 * 测试注解@Qualifier 10 * @function 11 * @author 小风微凉 12 * @time 2018-7-12 上午11:58:56 13 */ 14 @Component("manager") 15 @ComponentScan(basePackages={"com.xfwl.spring.annotation.qualifier"})//启动扫描当前所在包的Bean,注入Spring IoC容器 16 public class UserManage { 17 @Autowired(required=true) 18 @Qualifier("serviceImp2") 19 private IServices service; 20 21 public void show(){ 22 this.service.show(); 23 } 24 /**********getter和setter*******************/ 25 public IServices getService() { 26 return service; 27 } 28 public void setService(IServices service) { 29 this.service = service; 30 } 31 }
测试结果:注解正常运行
5、装载带有参数的构造方法类:
贴出测试代码:
1 package com.xfwl.spring.annotation.qualifier; 2 import org.springframework.beans.factory.annotation.Autowired; 3 import org.springframework.stereotype.Component; 4 @Component("serviceImp") 5 public class ServiceImpl implements IServices { 6 private UserBean user; 7 public ServiceImpl(@Autowired UserBean user){//测试通过 8 this.user=user; 9 } 10 @Override 11 public void show() { 12 System.out.println(this.user.toString()); 13 } 14 }
或者
1 package com.xfwl.spring.annotation.qualifier; 2 import org.springframework.beans.factory.annotation.Qualifier; 3 import org.springframework.stereotype.Component; 4 @Component("serviceImp3") 5 public class ServiceImpl3 implements IServices { 6 private UserBean user; 7 public ServiceImpl3(@Qualifier UserBean user){//测试不通过,不支持注解@Qualifier注入构造器的参数 8 this.user=user; 9 } 10 @Override 11 public void show() { 12 System.out.println(this.user.toString()); 13 } 14 }
从上面的代码,可以看出:这种注解并没有放在属性字段或者属性的setter方法上,而是直接放在构造器上的。
6、使用@Bean装配Bean
以上描述的用法都是通过@Componnet注解装配Bean,但是@Componnet只能注解在类上,不能注解在方法上。对于Java而言,大部分的开发都需要引入第三方的包(Jar文件),而且往往
并没有这些包的源码,这时候无法为这些包的类加入@Componnet注解,让他们变为开发环境的Bean.你可以使用新类扩展(extends)其包内的类,然后在新类上使用@Componnet,但是这样又显得
不伦不类。
这个时候Spring给与一个注解@Bean,它可以注解到方法上,并且讲方法返回的对象作为Spring的Bean存放在IoC容器中。比如我们需要用到的DBCP数据源。
现在来测试一下:
1 package com.xfwl.spring.annotation.bean; 2 3 import java.util.Properties; 4 5 import javax.sql.DataSource; 6 7 import org.apache.commons.dbcp2.BasicDataSourceFactory; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.ComponentScan; 10 11 @ComponentScan(basePackages={"com.xfwl.spring.annotation.bean"}) 12 public class ManagerScan { 13 @Bean(name="ds") 14 public DataSource getDataSource(){ 15 Properties props=new Properties(); 16 props.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver"); 17 props.setProperty("url", "jdbc:oracle:thin:@***.**.17.33:1521:**"); 18 props.setProperty("username", "***"); 19 props.setProperty("password", "***"); 20 DataSource dataSource=null; 21 try { 22 dataSource=BasicDataSourceFactory.createDataSource(props); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 return dataSource; 27 } 28 }
以及
1 package com.xfwl.spring.annotation.bean; 2 import java.util.Properties; 3 4 import javax.sql.DataSource; 5 import org.apache.commons.dbcp2.BasicDataSourceFactory; 6 import org.springframework.context.ApplicationContext; 7 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.ComponentScan; 10 import org.springframework.stereotype.Component; 11 @Component("test") 12 public class TestBean { 13 public static void main(String[] args) { 14 //通过注解获取IoC容器对象 15 ApplicationContext ctx=new AnnotationConfigApplicationContext(ManagerScan.class); 16 System.out.println(ctx.getBean("ds").toString()); 17 } 18 }
测试结果显示:可以从Spring IoC容器中拿到ds这个Bean对象。
7、注解自定义Bean的初始化和销毁方法:
注解@Bean不能使用在类的标注上,它主要使用在方法上,@Bean的配置项包含4个配置项:
- name:是一个字符串数组,允许配置多个BeanName。
- autowire:标志是否是一个引用的Bean对象,默认值是AutoWire.No。
- initKMethod:自定义初始化方法。
- destoryMethod:自定义销毁方法。
基于上面的描述,自定义初始化的方法是initMethod,销毁的方法则是destoryMethod。
贴出测试代码:
创建一个基本POJO类:UserBean.java (内置自定义的初始化和销毁方法)
1 package com.xfwl.spring.annotation.bean; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 测试注解:@Component装配Bean 9 * @author Jason 10 * 11 */ 12 @Component("user") //注入user //@Component("user")或者@Component(value="user") 13 public class UserBean { 14 @Value("xfww") 15 private String uname; 16 @Value("123456") 17 private String upwd; 18 public UserBean(){} 19 public UserBean(String uname,String upwd){ 20 this.uname=uname; 21 this.upwd=upwd; 22 } 23 public String getUname() { 24 return uname; 25 } 26 public void setUname(String uname) { 27 this.uname = uname; 28 } 29 public String getUpwd() { 30 return upwd; 31 } 32 public void setUpwd(String upwd) { 33 this.upwd = upwd; 34 } 35 @Override 36 public String toString() { 37 return "UserBean [uname=" + uname + ", upwd=" + upwd + "]"; 38 } 39 /**Bean生命周期测试**/ 40 public void init(){ 41 System.out.println("【"+this.getClass().getSimpleName()+"】执行自定义初始化方法!"); 42 } 43 public void mydestory(){ 44 System.out.println("【"+this.getClass().getSimpleName()+"】执行自定义销毁法!"); 45 } 46 }
创建一个测试@Bean的类:(特别说明一下:注解@Configuration表示是告诉spring这个类是一个配置类,相当于我们的xml配置文件)
1 package com.xfwl.spring.annotation.bean; 2 3 import org.springframework.beans.factory.BeanNameAware; 4 import org.springframework.beans.factory.DisposableBean; 5 import org.springframework.beans.factory.InitializingBean; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.ComponentScan; 9 import org.springframework.context.annotation.Configuration; 10 @Configuration 11 @ComponentScan(basePackages={"com.xfwl.spring.annotation.bean"}) 12 public class ManagerScan{ 13 @Bean(name={"tom","jack"},initMethod="init",destroyMethod="mydestory") 14 public UserBean getUser(@Autowired UserBean user){ 15 return user; 16 } 17 }
创建一个测试类:
1 package com.xfwl.spring.annotation.bean; 2 import java.util.Properties; 3 4 import javax.sql.DataSource; 5 6 import org.apache.commons.dbcp2.BasicDataSourceFactory; 7 import org.springframework.context.ApplicationContext; 8 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 9 import org.springframework.context.annotation.Bean; 10 import org.springframework.context.annotation.ComponentScan; 11 import org.springframework.stereotype.Component; 12 @Component("test") 13 public class TestBean { 14 public static void main(String[] args) { 15 //通过注解获取IoC容器对象 16 AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(ManagerScan.class); 17 UserBean user=(UserBean) ctx.getBean("tom"); 18 System.out.println(user.toString()); 19 ctx.close(); 20 } 21 }
测试结果:
1 log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). 2 log4j:WARN Please initialize the log4j system properly. 3 log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 4 【UserBean】执行自定义初始化方法! 5 UserBean [uname=xfww, upwd=123456] 6 【UserBean】执行自定义销毁法!