注解只是一个标记,作用是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML注解只是告诉框架中的Java代码如何执行。

在标识组件时常用的注解有

  • @Component:将类标识为普通组件
  • @Controller:将类标识为控制层组件
  • @Service:将类标识为业务层组件
  • @Repository:将类标识为持久层组件

注意,后三个注解都由@Component注解拓展而来,对于Spring使用IOC容器管理这些组件来说没有区别,但出于代码可读性还是要严谨标记。

创建组件与扫描注解

要实现基于注解来管理bean,要先创建组件、再扫描组件,

通过注解+扫描,就可以将扫描的包下加上注解的类作为组件作为管理(其实就是在IOC容器内有了加上注解的这个类所对应的bean对象)

以下面这个例子为例 :

创建组件

创建控制层组件

1
2
3
@Controller
public class UserController {
}

创建接口UserService

注意 : 标记注解就是把加上注解的类在IOC容器中去配置了一个这个类型的bean,但bean的类型不能是一个接口类型,所以这个接口要加在实现上。

1
2
3
public interface UserService {

}

创建业务层组件UserServiceImpl

1
2
3
4
@Service
public class UserServiceImpl implements UserService {

}

创建接口UserDao

1
2
3
public interface UserDao {

}

创建持久层组件UserDaoImpl

1
2
3
@Repository
public class UserDaoImpl implements UserDao {
}

扫描组件(三种方式)

扫描组件在配置文件中进行扫描,目的在于让Spring知道哪些类加了什么注解。

使用的标签是<context:component-scan> ,扫描的包通过 base-package字段指定。

分为三种情况 : 最基本的扫描方式、指定要排除的方式、指定要扫描的方式

  • 最基本的扫描

    1
    2
    <context:component-scan base-package="com.atguigu">
    </context:component-scan>
  • 指定要排除的组件

    < context:exclude-filter >标签:指定排除规则

    type:设置排除或包含的依据

    type="annotation",根据注解排除,expression中设置要排除的注解的全类名(全类名可以在注解上右键copyreference获取)
    type="assignable",根据类型排除,expression中设置要排除的类型的全类名

    1
    2
    3
    4
    5
    6
    <context:component-scan base-package="com.atguigu">
    <context:exclude-filter type="annotation"
    expression="org.springframework.stereotype.Controller"/> //不扫描控制层
    <!--<context:exclude-filter type="assignable"
    expression="com.atguigu.controller.UserController"/>--> //不扫描UserController类
    </context:component-scan>
  • 扫描指定组件

    <context:include-filter >标签:指定在原有扫描规则的基础上追加的规则

    use-default-filters属性:取值false表示关闭默认扫描规则 ,此时必须设置use-default-filters=”false”,因为默认规则即扫描指定包下所有类

    type:设置排除或包含的依据
    type="annotation",根据注解扫描,expression中设置要扫描的注解的全类名
    type="assignable",根据类型扫描,expression中设置要扫描的类型的全类名

    1
    2
    3
    4
    5
    6
    7
    <context:component-scan base-package="com.atguigu" use-default-filters="false">

    <context:include-filter type="annotation"
    expression="org.springframework.stereotype.Controller"/>
    <!--<context:include-filter type="assignable"
    expression="com.atguigu.controller.UserController"/>-->
    </context:component-scan>

基于注解的bean的id

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。

  • 默认情况 :

    类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。

  • 自定义bean的id

    可通过标识组件的注解的value属性设置自定义的bean的id

    1
    2
    @Service("userService")
    //默认为userServiceImpl public class UserServiceImpl implementsUserService {}

基于注解的自动装配

基于注解的自动装配使用 @Autowired 注解实现。只需要在成员变量上添加 @Autowired 注解即可 :

注意 : 使用了该注解之后就不需要再写setter和getter方法了。

在基于xml管理bean的环节中,自动装配的方法是在配置文件中使用autowire字段

1
2
3
4
5
6
@Controller
public class UserController {
@Autowired
private UserService userService;
...
}

@Autowired能标记的位置

其实就是在哪儿能为成员变量赋值。

  • 标记在哪成员变量上,此时不需要为这个成员变量设置setter和getter方法(常用)
  • 标记在set方法上
  • 为当前成员变量赋值的有参构造上

@Autowired原理

  1. 默认通过 byType方式,在IOC容器中通过匹配某个bean为属性赋值

  2. 在通过 byType方式,发现由多个类型匹配的bean时,会自动转化byName的方式来实现自动装配的效果。

    byName :将要赋值的属性的属性名作为bean的id来匹配某个bean

  3. byTypebyName都无法实现自动装配时(即IOC容器中有多个类型匹配的bean,且这些bean的id和要赋值的属性的属性名都不一样时),此时会抛出异常 :NoUniqueBeanDefinitionException (byType方式的报错)

  4. 要解决第3点,可以在要赋值的属性上 ,添加 @Qualifier来指定某个bean的id,来为这个bean属性赋值(基本不会用的)

    1
    2
    3
    4
    5
    6
    7
    @Controller
    public class UserController {
    @Autowired
    @Qualifier("userServiceImpl") //指定id为userServiceImpl的bean,来为当前bean赋值
    private UserService userService;
    ...
    }