侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

spring中定义组件的注解和用于依赖注入的注解

孔子说JAVA
2022-06-27 / 0 评论 / 0 点赞 / 58 阅读 / 7,566 字 / 正在检测是否收录...

Spring第一版中定义组件的注解只有一个 @Component,从2.5版本开始,根据分层的需要,把它拆成了四个,达到所见即所得的目标,可以让开发人员一看到注解,立即知道类的性质,它们分别是@Component@Repository@Service@Controller。另外用于组件依赖注入的注解有3个,分别是@Resource@Autowired@Qualifier

1、定义组件(bean)的注解

当你需要定义某个类为一个bean时,需要在类名之前使用定义组件(bean)的注解,如@Service(“XXX”),这表示将该类定义一个bean,bean名称为 XXX。以下是@Component、@Repository、@Service、@Controller注解的详细介绍。

1.1 @Component 注解

该组件注解为通用注解,被该注解描述的类将被loC容器管理并实例化。一般情况下,我们会明确使用@Repository、@Service、@Controller注解中的一个来定义组件,只有当一个组件不好归类的时候,我们才使用@Component注解来进行标注。

  • 带此注解的类可以看为组件,当使用基于注解的配置和类路径扫描的时候,这些类就会被实例化。
  • @Repository、@Service、@Controller这3个注解和@Component注解功能是等效的,从语义方面来讲是@Component的细化。
  • 不指定bean的名称,默认为类名首字母小写。

代码示例:

  1. 不指定bean的名称,默认bean名称为类名首字母小写,该例中为 iocComponentImpl
import org.springframework.stereotype.Component;

@Component
public class IocComponentImpl implements IocComponent {
    ......
}
  1. 指定bean的名称,该例中指定为 iocComponent
import org.springframework.stereotype.Component;

@Component("iocComponent")
public class IocComponentImpl implements IocComponent {
    ......
}

1.2 @Controller 注解

该组件注解为语义注解,说明当前类是MVC应用中的控制器类,主要用于标注控制层组件(即通常定义的controller层)。将当前修饰的类注入Spring IOC容器,使得从该类所在的项目启动过程中,这个类能被实例化.

  • @Controller在SpringMVC里面有强制的要求,即SpringMVC的表示层必须使用@Controller组件注解。
  • 其他几个注解用乱了是不会报错的,一般不建议乱用,如果不遵守规范,别人就无法跟你协同开发了。

代码示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class IocAction {
    @Autowired
    private IocService service;
   
    public void add(){
        service.add();
    }
}

1.3 @Service 注解

该组件注解为语义注解,说明当前类是Service业务服务类,主要用于标注业务层组件(即通常定义的service层)。该注解默认生成的bean名称为类名首字母小写,但是还有一个特殊情况:当类的名字是两个或两个以上的大写字母开头的话,bean名称会与类名保持一致。如类名为 IIocServiceImpl,如果加了 @Service 注解,则bean名称和类名一致,为 IIocServiceImpl

我们可以看下源码:

image-1656163643422

image-1656163660912

代码示例:

  1. 不指定bean的名称,默认bean名称为类名首字母小写,该例中为 iocServiceImpl
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class IocServiceImpl implement impliments IIocService{
    @Resource
    private IIocDao iocDao;
    
    public void add(){
        iocDao.add();
    }
}
  1. 指定bean的名称,该例中指定为 iocService
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service("iocService")
public class IocServiceImpl implement impliments IIocService{
    @Resource
    private IIocDao iocDao;
    
    public void add(){
        iocDao.add();
    }
}

1.4 @Repository 注解

该组件注解为语义注解,说明当前类用于Dao业务持久层,主要用于标注数据访问组件(即通常定义的DAO层)。

  1. 不指定bean的名称,默认bean名称为类名首字母小写,该例中为 iocDaoImpl
import org.springframework.stereotype.Repository;

@Repository
public class IocDaoImpl implements IIocDao{
    public void add(){
        System.out.println("调用了dao");
    }
}
  1. 指定bean的名称,该例中指定为 iocDao
import org.springframework.stereotype.Repository;

@Repository("iocDao")
public class IocDaoImpl implements IIocDao{
    public void add(){
        System.out.println("调用了dao");
    }
}

1.5 总结

如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。

  • @Controller用于标注控制层组件,即Controller组件。
  • @Service用于标注业务层组件,即Service组件。
  • @Repository用于标注数据访问组件,即DAO组件
  • @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

2、用于组件依赖注入的注解

用于组件依赖注入的注解有3个,分别是@Resource@Autowired@Qualifier

2.1 @Autowired 注解

@Autowired 注解全路径是 org.springframework.beans.factory.annotation.Autowired ,可以看出来这是spring的包。它可以对类成员变量、方法及构造函数进行标注,完成自动注入/装配的工作。通过 @Autowired 注解的使用来消除 set ,get方法。

在Spring服务启动的时候,会根据声明先扫描配置文件中的bean(注解配置同理), 再根据bean中类的全限定名去寻找它,一般是我们自己声明的pojo(实体类), 然后这时如果有@Autowired注解则会根据类型寻找其它的被Spring托管的bean进行自动注入, 调用默认的构造函数。

  • @Autowired 注解是通过 byType 的方式去注入的,使用该注解,要求接口只能有一个实现类。

  • @Autowired 注解不支持设置别名,如果一个接口有多个实现类,则可以与@Qualifier(“alias”) 注解配合使用。

代码示例:

  1. 接口:IAnimal
public Interface IAnimal{
    ......
}
  1. 实现类:DogImpl ,实现了IAnimal接口
@Service("dogImpl")
public class DaoImpl impliments IAnimal{
    ...
}
  1. 业务类:AnimalController
public class AnimalController {
    @Autowired
    private IAnimal dogImpl;
    ......
}


假如有一个“动物”的接口 IAnimal, DogImpl类实现了接口 IAnimal,且该接口只有 DogImpl这一个实现类,那么在使用@Autowired 注解注入实现类的时候,我们使用的是实现类的接口(像上面程序步骤 3 展示的那样),这时Spring会按 byType 的方式寻找接口的实现类,并将其注入。

假如有另一个实现类 CatImpl 也实现了接口 IAnimal,这时候再按上面的方式去注入的话,IAnimal接口在同时存在两个实现类的情况下,会出现什么情况呢?

  • 这时候spring会报错。这是因为 @Autowired 的注入方式是 byType 注入,当要注入的类型在容器中存在多个时,Spring无法确定要注入哪个实现类,所以会报错。
  • 这种场景下,只能通过 byName 注入的方式。可以使用 @Resource 或 @Qualifier 注解。具体方式参考后续 2.2 @Qualifier 注解 或 ## 2.3 @Qualifier 注解

2.2 @Qualifier 注解


@Qualifier注解的用处是当一个接口有多个实现的时候,可以用来指定具体调用哪个类的实现(通过byName注入)。@Qualifier的参数为我们之前定义bean名称。使用 @Autowired 注解是通过 byType 注入,如果需要 byName(byName就是通过id去标识)注入,需要增加 @Qualifier 注解。一般在候选Bean数目不为1时应该加 @Qualifier 注解。

  • Qualifier的意思是合格者,通过这个注解,表明了哪个实现类才是我们所需要的。

  • 在默认情况下使用 @Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。

  • 当找不到一个匹配的 Bean 时,Spring 容器将抛出BeanCreationException 异常,并指出必须至少拥有一个匹配的 Bean。

  • 和找不到一个类型匹配 Bean 相反的一个错误是:如果 Spring 容器中拥有多个候选 Bean,Spring 容器在启动时也会抛出 BeanCreationException 异常。

  • Spring 允许我们通过 @Qualifier 注解指定注入 Bean 的名称,这样歧义就消除了。

2.1 @Autowired 注解 中接口 IAnimal有多个实现,若使用 @Autowired 注解自动注入,则spring容器启动的时候会报无法创建bean的异常,可以使用 @Qualifier 注解指定注入 Bean 的名称,这样歧义就消除了。具体示例如下:

  • 注意 @Qualifier 注解需要与 @Autowired 注解结合使用。

代码示例:

public class AnimalController {

    @Autowired
    @Qualifier("dogImpl")    //实现类1中 @Service注解中标定的名称
    private IAnimal dogImpl;
    ......
}

@Qualifier 注解也可以在方法签名中使用:

  • @Qualifier(“office”)中的office是Bean的名称,所以@Autowired和@Qualifier结合使用时,自动注入的策略就从byType转变成byName了。
@Autowired  
public void setOffice(@Qualifier("office")Office office) {  
    this.office =office;  
} 

@Autowired可以对成员变量、方法以及构造函数进行注释,而@Qualifier的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以Spring不将 @Autowired和@Qualifier统一成一个注释类。

  • @Qualifier 只能和@Autowired 结合使用,是对@Autowired有益的补充。

  • 一般来讲,@Qualifier对方法签名中入参进行注释会降低代码的可读性,而对成员变量注释则相对好一些。

2.3 @Resource 注解


@Resource 注解的作用相当于 @Autowired 注解,只不过 @Autowired 按 byType 自动注入,而 @Resource 默认按 byName 自动注入罢了。@Resource 注解的全路径是 javax.annotation.Resource,可以看到它是javaEE中的包,并不是spring中的。

  • @Resource 有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。
  • @Resource 可以通过 byName 和 byType的方式注入,默认先按 byName的方式进行匹配,如果匹配不到,再按 byType的方式进行匹配。
  • @Resource 如果使用name属性,则使用 byName 的自动注入策略,而使用type属性时则使用 byType 自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用 byName 自动注入策略。

@Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

2.1 @Autowired 注解 中接口 IAnimal有多个实现,若使用 @Autowired 注解自动注入,则spring容器启动的时候会报无法创建bean的异常,可以使用 @Resource 注解指定注入 Bean 的名称,这样歧义就消除了。具体示例如下:

  • 注意:@Resource 注解单独使用,不需要和 @Autowired 注解结合使用。

代码示例:

public class AnimalController {
    @Resource(name="dogImpl")  //实现类1中 @Service注解中标定的名称
    private IAnimal dogImpl;
    ......
}
  • 需要注意:@Resource(name="dogImpl") 中需要指定 name

其他代码示例:

  1. 当@Resource不设置任何值时,isDefaultName会为true,当对应字段名称的bean或者BeanDefinition已存在时会走byName的形式,否则走byType的形式。
public class AnimalController {
    @Resource
    private IAnimal dogImpl;
    ......
}
  1. 只指定了type属性时,首先还是会通过注解的属性名去匹配bean,只有当对应的名称不存在对应的bean或BeanDefinition,才会通过byType找到唯一的一个类型匹配的bean。
    • isDefaultName的值还是为true,所以执行的还是resolveDependency方法。但是由于添加了类型的限制,所以也就不会匹配到多个Bean,而产生异常。
public class AnimalController {
    @Resource(type = DaoImpl.class)
    private IAnimal dogImpl;
    ......
}
  1. 只指定了name属性,会执行getBean方法,根据指定的name来获取bean;
public class AnimalController {
    @Resource(name="dogImpl")
    private IAnimal dogImpl;
    ......
}
  1. 既指定了name属性,又指定了type属性,会先根据 name 查找对应的bean,然后进行 type 类型比较。
  • 示例中 name 属性被设置为 dogImpl,isDefaultName 变为false,执行 resolveBeanByName 方法,但是由于找不到对应beanName为 dogImpl,但是类型又为 Apple.class 的 bean,还是会抛出异常。所以既指定 name,又指定 type 时,必须 2 个属性都完全正确。
public class AnimalController {
    @Resource(name="dogImpl", type = Apple.class)
    private IAnimal dogImpl;
    ......
}

多种@Resource不同使用情况所执行方法如下所示;

image-1656163169143

2.4 总结

  1. @Resource是java的注解,@Autowired是spring的注解。
  2. @Autowired是byType注入的,可以独立使用。
  3. @Resource默认按name注入,可以通过name和type属性进行选择性注入。
  4. @Qualifier是在无法按类型区分(一个接口有多个实现类)的情况下和@Autowired结合使用,这俩个注解组合起来相当于@Resource注解。
  5. @Qualifier的作用是在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用(但是在给方法参数注入时可以单独使用),因此@Qualifier注解很受限制,因此用的不是很多。@Qualifier常常组合@Autowired一起使用,用来指明具体名字的自动装配
  6. @Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。如果没有指定name,则回退为按照byType注入。
0

评论区