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

  如今有了这个类,可是还不能进行测试,由于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容器ide

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制定包扫描ui

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配置this

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)的方式。spa

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就是这样的一个注解。

相关文章
相关标签/搜索