注解管理bean

76 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

基于注解管理bean

基于注解

基于注解管理bean和xml配置文件管理一样,注解本身并不能执行,注解本身仅仅只做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置,按照注解标记的功能来执行具体的操作

扫描

spring为了知道程序员在那些地方标记了什么注解就需要通过扫描的方式来进行检测,然后根据注解来进行后续操作(将扫描到的包内的类交给spring容器来保存)

常用注解

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

注: 这四个注解功能一模一样,没有任何区别,都是将对应的类交给spring容器来保存,只不过名字不一样(便于分辨组件的作用)

配置个扫描器,扫描对应的注解

<!--扫描组件-->
<context:component-scan base-package="com.sentiment"></context:component-scan>

测试

public void test(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-annotation.xml");
    UserController controller = ioc.getBean(UserController.class);
    System.out.println(controller);
    UserService service = ioc.getBean(UserService.class);
    System.out.println(service);
    UserDao dao = ioc.getBean(UserDao.class);
    System.out.println(dao);
}

扫描组件

在exclude-filter标签中可以设置两个参数:

  • exclude-filter:排除扫描
  • include-filter:包含扫描

其中包含几个属性:

  • type:设置扫描的方式有两个常用值—annotation、assignable

    • annotation:根据注解的类型进行排除
    • assignable:根据类的类型进行排除
  • expression:设置排除的类的全类名

  • use-default-filters:设置是否扫描包下所有的包,默认为true,但若使用include-filte包含扫描时,需设置为flase(包含扫描是指只扫描哪个标签,而若use-default-filters设为true则会默认扫描包下的所有标签,就是去了include-filte的意义)

    <context:component-scan base-package="com.sentiment" use-default-filters="false">
<!--        排除扫描-->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!--        包含扫描-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="assignable" expression="com.sentiment.service.impl.UserServiceImpl"/>
    </context:component-scan>

bean的id

  • id默认为类的小驼峰例:userController
  • 也可自定义id,在标签中设置value值即可,例:@Controller("controller")

测试

getbean方法可以通过bean的id获取ioc容器

ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-annotation.xml");
UserController controller = ioc.getBean("controller",UserController.class);
Syste标识在成员变量上,此时不需要设置成员变量的set方法(直接根据策略在ioc容器中查找对应对象)
标识在set方法上
为当前成员变量赋值的有参构造上m.out.println(controller);

@Autowierd自动装配

标识位置

  • 标识在成员变量上,此时不需要设置成员变量的set方法(直接根据策略在ioc容器中查找对应对象)
  • 标识在set方法上
  • 为当前成员变量赋值的有参构造上

建议标识在成员变量上

@Controller("controller")
public class UserController {
    @Autowired
    private UserService userService;
    public void saveUser(){
        userService.saveUser();
    }
}

自动装配原理

  1. 默认通过byType的方式,在IOC容器中通过类型匹配某个bean为属性赋值
  2. 若有多个类型匹配的bean,此时会自动转换为byName的方式实现自动装配的效果即将要赋值的属性的属性名作为bean的id匹配某个bean为属性赋值
  3. 若byType和byName的方式都无妨实现自动装配,即IOC容器中有多个类型匹配的bean且这些bean的id和要赋值的属性的属性名都不一致,此时抛异常:NouniqueBeanDefinitionException,此时可以在要赋值的属性上,添加一个注解@Qualifier通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值

例:

② 若此时在下边添加两个bean标签,则默认会通过byName方式获取,因为这里当调用UserService类型是,这里存在两个所以无法匹配。

验证方法也很简单,只需要把id随意修改一下便会报错,出现找不到对应属性的问题

<context:component-scan base-package="com.sentiment"></context:component-scan>
<bean id="userService" class="com.sentiment.service.impl.UserServiceImpl"></bean>
<bean id="userDao" class="com.sentiment.dao.impl.UserDaoImpl"></bean>

③ 如上 边所说id随意修改一下后,便会出现找不到对应的id的问题而且此时也有两个同类型bean,byName和byType就都是失效了,所以就用到了@Qualifier注解

<context:component-scan base-package="com.sentiment"></context:component-scan>
<bean id="Service" class="com.sentiment.service.impl.UserServiceImpl"></bean>
<bean id="Dao" class="com.sentiment.dao.impl.UserDaoImpl"></bean>

测试

通过@Qualifier来指定对应bean的id即可

@Controller("controller")
public class UserController {
    @Autowired
    @Qualifier("service")
    private UserService userService;
    public void saveUser(){
        userService.saveUser();
    }
}

注: 在@Autowired注解属性里有个required属性,默认为true,要求必须完成自动装配,可以将required设置为false,此时能装配则装配,不能装配则使用属性默认值