spring 通过注解装配Bean
  使用注解的方式可以减少XML的配置,注解功能更为强大,它既能实现XML的功能,也提供了自动装配的功能,采用了自动装配后,程序员所需要做的决断就少了,更加有利于对程序的开发,这就是“约定优于配置”的开发原则。
  在Spring中,它提供了两种方式来让Spring IoC容器发现Bean。   
  •组件扫描:通过定义资源的方式,让Spring IoC容器扫描对应的包,从而把Bean装配进来。   
  •自动装配:通过注解定义,使得一些依赖关系可以通过注解完成。
  通过扫描和自动装配,大部分的工程都可以用Java配置完成,而不是XML,这样可以有效减少配置和引入大量XML,它解决了在Spring 3之前的版本需要大量的XML配置的问题,这些问题曾被许多开发者诟病。由于目前注解已经成为Spring开发的主流,在之后的章节里,笔者也会以注解的方式为主介绍Spring的开发,但是请注意只是为主,而不是全部以注解的方式去实现。因为不使用XML也存在着一定的弊端,比如系统存在多个公共的配置文件(比如多个properties和XML文件),如果写在注解里,那么那些公共资源的配置就会比较分散了,这样不利于统一的管理,又或者一些类来自于第三方,而不是我们系统开发的配置文件,这时利用XML的方式来完成会更加明确一些,因此目前企业所流行的方式是,以注解为主,以XML为辅,本书的介绍也是如此。
使用@Component装配Bean
@Component(value = "role")
public class Role {
@Value("1")
private Long id;
@Value("role_name_1")
private String roleName;
@Value("role_note_1")
private String note;
}
  •注解@Component代表Spring IoC会把这个类扫描生成Bean实例,而其中的value属性代表这个类在Spring中的id,这就相当于XML方式定义的Bean的id,也可以简写成@Component("role"),甚至直接写成@Component,对于不写的,Spring IoC容器就默认类名,但是以首字母小写的形式作为id,为其生成对象,配置到容器中。 
  •注解@Value代表的是值的注入,这里只是简单注入一些值,其中id是一个long型,注入的时候Spring会为其转化类型。
  现在有了这个类,但是还不能进行测试,因为Spring IoC并不知道需要去哪里扫描对象,这个时候可以使用一个Java Config来去告诉它,如代码清单所示。
  代码清单:Java Config类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan
public class PojoConfig {
}
  这个类十分简单,几乎没有逻辑,但是要注意两处加粗的代码。 
   •包名和代码清单的POJO保持一致。  
   •@ComponentScan代表进行扫描,默认是扫描当前包的路径,POJO的包名和它保持一致才能扫描,否则是没有的。
  可以通过Spring定义好的Spring IoC容器的实现类——AnnotationConfigApplicationContext去生成IoC容器了。它十分简单,如代码清单所示。
  代码清单:使用注解生成Spring IoC容器
ApplicationContext context = new AnnotationConfigApplicationContext(PojoConfig.class);
Role role = context.getBean(Role.class);
System.err.println(role.getId());
  @ComponentScan存在着两个配置项:第1个是basePackages,它是由base和package两个单词组成的,而package还使用了复数,意味着它可以配置一个Java包的数组,Spring会根据它的配置扫描对应的包和子包,将配置好的Bean装配进来;第2个是basePackageClasses,它由base、package和class三个单词组成的,采用复数,意味着它可以配置多个类,Spring会根据配置的类所在的包,为包和子包进行扫描装配对应配置的Bean。
  代码清单:RoleService接口
import com.ssm.chapter10.annotation.pojo.Role;
public interface RoleService {
    public void printRoleInfo(Role role);
}
代码清单:RoleServiceImpl类
import org.springframework.stereotype.Component;
import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService; @Component
public class RoleServiceImpl implements RoleService { // @Override
public void printRoleInfo(Role role) {
System.out.println("id =" + role.getId());
System.out.println("roleName =" + role.getRoleName());
System.out.println("note =" + role.getNote());
} }
代码清单:配置@ComponentScan制定包扫描
import org.springframework.context.annotation.ComponentScan;
import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.impl.RoleServiceImpl; @ComponentScan(basePackageClasses = {Role.class, RoleServiceImpl.class})
// @ComponentScan(basePackages = {"com.ssm.chapter10.annotation.pojo", "com.ssm.chapter10.annotation.service"})
// @ComponentScan(basePackages = {"com.ssm.chapter10.annotation.pojo", "com.ssm.chapter10.annotation.service"}
//, basePackageClasses = {Role.class, RoleServiceImpl.class})
public class ApplicationConfig {
}
  •这是对扫描包的定义,可以采用任意一个@ComponentScan去定义,也可以取消代码中的注释。 
   •如果采用多个@ComponentScan去定义对应的包,但是每定义一个@ComponentScan,Spring就会为所定义的类生成一个新的对象,也就是所配置的Bean将会生成多个实例,这往往不是我们的需要。
  •对于已定义了basePackages和basePackageClasses的@ComponentScan,Spring会进行专门的区分,也就是说在同一个@ComponentScan中即使重复定义相同的包或者存在其子包定义,也不会造成因同一个Bean的多次扫描,而导致一次配置生成多个对象。
  基于上述的几点,建议不要采用多个@ComponentScan注解进行配置,因为一旦有重复的包和子包就会产生重复的对象,这往往不是真实的需求。对于basePackages和basePackageClasses的选择问题,basePackages的可读性会更好一些,因此在项目中会优先选择使用它,但是在需要大量重构的工程中,尽量不要使用basePackages定义,因为很多时候重构修改包名需要反复地配置,而IDE不会给你任何的提示。而采用basePackageClasses,当你对包移动的时候,IDE会报错提示,并且可以轻松处理这些错误。
  代码清单:测试basePackages和basePackageClasses配置
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Role role = context.getBean(Role.class);
RoleService roleService = context.getBean(RoleService.class);
roleService.printRoleInfo(role);
context.close();
自动装配——@Autowired
通过学习Spring IoC容器,我们知道Spring是先完成Bean的定义和生成,然后寻找需要注入的资源。也就是当Spring生成所有的Bean后,如果发现这个注解,它就会在Bean中查找,然后找到对应的类型,将其注入进来,这样就完成依赖注入了。所谓自动装配技术是一种由Spring自己发现对应的Bean,自动完成装配工作的方式,它会应用到一个十分常用的注解@Autowired上,这个时候Spring会根据类型去寻找定义的Bean然后将其注入,这里需要留意按类型(Role)的方式。
public interface RoleService2 {
    public void printRoleInfo();
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService2; @Component("RoleService2")
public class RoleServiceImpl2 implements RoleService2 { @Autowired
private Role role = null; // @Autowired
// public void setRole(Role role) {
// this.role = role;
// } // @Override
public void printRoleInfo() {
System.out.println("id =" + role.getId());
System.out.println("roleName =" + role.getRoleName());
System.out.println("note =" + role.getNote());
} /**** setter and getter ****/
public Role getRole() {
return role;
} public void setRole(Role role) {
this.role = role;
}
}
  这里的@Autowired注解,表示在Spring IoC定位所有的Bean后,这个字段需要按类型注入,这样IoC容器就会寻找资源,然后将其注入。比如代码清单10-15定义的Role和代码清单10-23定义RoleServiceImpl2的两个Bean,假设将其定义,那么Spring IoC容器会为它们先生成对应的实例,然后依据@Au-towired注解,按照类型找到定义的实例,将其注入。
  IoC容器有时候会寻找失败,在默认的情况下寻找失败它就会抛出异常,也就是说默认情况下,Spring IoC容器会认为一定要找到对应的Bean来注入这个字段,有些时候这并不是一个真实的需要,比如日志,有时候我们会觉得这是可有可无的,这个时候可以通过@Autowired的配置项required来改变它,比如@Autowired(required=false)。
  正如之前所谈到的在默认情况下是必须注入成功的,所以这里的required的默认值为true。当把配置修改为了false时,就告诉Spring IoC容器,假如在已经定义好的Bean中找不到对应的类型,允许不注入,这样也就没有了异常抛出,只是这样这个字段可能为空,读者要自行校验,以避免发生空指针异常。在大部分的情况下,都不需要这样修改。
  @Autowired除可以配置在属性之外,还允许方法配置,常见的Bean的setter方法也可以使用它来完成注入
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleService2 roleService2 = context.getBean(RoleService2.class);
roleService2.printRoleInfo();
context.close();
自动装配的歧义性(@Primary和@Qualifier)
@Autowired注解,它可以完成一些自动装配的功能,并且使用方式十分简单,但是有时候这样的方式并不能使用。这一切的根源来自于按类型的方式,按照Spring的建议,在大部分情况下会使用接口编程,但是定义一个接口,并不一定只有与之对应的一个实现类。换句话说,一个接口可以有多个实现类
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService; @Component("roleService3")
// @Primary//方法1
public class RoleServiceImpl3 implements RoleService { // @Override
public void printRoleInfo(Role role) {
System.out.print("{id =" + role.getId());
System.out.print(", roleName =" + role.getRoleName());
System.out.println(", note =" + role.getNote() + "}");
} }
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService; @Component
public class RoleController { @Autowired
// @Qualifier("roleService3")//方法2
private RoleService roleService = null; public void printRole(Role role) {
roleService.printRoleInfo(role);
} }
  这里的字段roleService是一个RoleService接口类型。RoleService有两个实现类,分别是RoleServiceImpl和RoleServiceImpl3,这个时候Spring IoC容器就会犯糊涂了,它无法判断把哪个对象注入进来,于是就会抛出异常,这样@Autowired注入就失败了。
  通过上面的分析,可以知道产生这样的状况是因为它采用的是按类型来注入对象,而在Java中接口可以有多个实现类,同样的抽象类也可以有多个实例化的类,这样就会造成通过类型(bytype)获取Bean的不唯一,从而导致Spring IoC类似于按类型的方法无法获得唯一的实例化类。
  为了消除歧义性,Spring提供了两个注解@Primary和@Qualifier,这是两个不同的注解,其消除歧义性的理念不太一样
  1. 注解@Primary
注解@Primary代表首要的,当Spring IoC通过一个接口或者抽象类注入对象的时候,由于存在多个实现类或者具体类,就会犯糊涂,不知道采用哪个类注入为好。注解@Primary则是告诉Spring IoC容器,请优先使用该类注入。
  2. 注解@Qualifier正如上面所谈及的歧义性,一个重要的原因是Spring在寻找依赖注入的时候采用按类型注入引起的。除了按类型查找Bean,Spring IoC容器最底层的接口BeanFactory,也定义了按名称查找的方法,如果采用名称查找的方法,而不是采用按类型查找的方法,那么不就可以消除歧义性了吗?答案是肯定的,而注解@Qualifier就是这样的一个注解。
spring 通过注解装配Bean的更多相关文章
- Spring通过注解装配Bean
		
通过注解实现ServiceImpl业务 一.使用@Component装配Bean 1. 定义类:User 在类上面加@Component注解,在属性上面加@Value值 package com.wbg ...
 - (转)java之Spring(IOC)注解装配Bean详解
		
java之Spring(IOC)注解装配Bean详解 在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...
 - Spring学习(六)bean装配详解之 【通过注解装配 Bean】【基础配置方式】
		
通过注解装配 Bean 1.前言 优势 1.可以减少 XML 的配置,当配置项多的时候,XML配置过多会导致项目臃肿难以维护 2.功能更加强大,既能实现 XML 的功能,也提供了自动装配的功能,采用了 ...
 - Spring总结 1.装配bean
		
本随笔内容要点如下: 依赖注入 Spring装配bean的方式 条件化装配 一.依赖注入 我理解的依赖注入是这样的:所谓的依赖,就是对象所依赖的其他对象.Spring提供了一个bean容器,它负责创建 ...
 - Spring 之自动化装配 bean 尝试
		
[Spring之自动化装配bean尝试] 1.添加dependencies如下所示(不是每一个都用得到 <dependencies> <dependency> <grou ...
 - spring中自动装配bean
		
首先用@Component注解类: package soundsystem: import org.springframework.stereotype.Component; @Component p ...
 - spring的自动装配Bean与自动检测Bean
		
spring可以通过编写XML来配置Bean,也可以通过使用spring的注解来装配Bean. 1.自动装配与自动检测: 自动装配:让spring自动识别如何装配bean的依赖关系,减少对<pr ...
 - SpringBoot(14)—注解装配Bean
		
SpringBoot(14)-注解装配Bean SpringBoot装配Bean方式主要有两种 通过Java配置文件@Bean的方式定义Bean. 通过注解扫描的方式@Component/@Compo ...
 - java之Spring(IOC)注解装配Bean详解
		
在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看Annotation的魅力所在吧. 先来看看之前的bean ...
 
随机推荐
- scanf()函数的调用:编写求正方形面积的通用程序
			
#include<stdio.h>void main(){ int a, area; scanf("%d",&a); //等待用户从键盘输入一个整数// are ...
 - 浏览器中点击链接,跳转qq添加好友的实现方式
			
做android三年了,都不知道到底干了啥,现在好好研究应该来得及,哈哈哈,希望看到文章的人共勉,哈哈哈(新手写文章,大佬轻喷,呜呜呜~) 好了,这篇只是记录下,项目中遇到的坑(MMP测试),哈哈哈, ...
 - Hadoop 格式化namenode时报错警告:WARN common.Util: Path /data/dfs/name should be specified as a URI in configuration
			
格式化namenode时报错警告:WARN common.Util: Path /data/dfs/name should be specified as a URI in configuration ...
 - 014_Python3 循环语句
			
1.while 循环 #!/usr/bin/env python3 n = 100 sum = 0 counter = 1 while counter <= n: sum = s ...
 - (尚010)Vue列表的搜素和排序
			
1.test010.html <!DOCTYPE html><html lang="en"><head> <meta charset=&q ...
 - 洛谷 P1638 逛画展 题解
			
P1638 逛画展 题目描述 博览馆正在展出由世上最佳的 M 位画家所画的图画. wangjy想到博览馆去看这几位大师的作品. 可是,那里的博览馆有一个很奇怪的规定,就是在购买门票时必须说明两个数字, ...
 - java  https
			
1. 异常突现 在这普通的一天,我写普通的代码,却突然收到不普通的报警 javax.net.ssl.SSLHandshakeException: server certificate change i ...
 - FWT快速沃尔什变换例题
			
模板题 传送门 #include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #de ...
 - Java 使用Jedis和RedisTemplate操作Redis缓存(SpringBoot)
			
package com.example.redis.controller; import com.example.redis.entity.User; import com.example.redis ...
 - mint-ui 做省市选择组件
			
省市的数据是动态的,其实不是动态的更好搞 <!-- 省市选择 --> <mt-popup v-model="popupVisible" position=&quo ...