006-Spring IoC 基于注解配置

自动扫描自定义的Bean

xml方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- 
		context:component-scan包含了context:annotation-config 
		base-package属性定义了spring自动扫描的类的路径,此路径下所有被相关注解的类都将被扫描
	-->
    <context:component-scan base-package="com.yyoo"/>

</beans>

本文将使用此方式来讲解

<context:annotation-config>配置隐式注册了如下处理器:
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor

Java配置的方式

@Configuration
@ComponentScan(basePackages = "com.yyoo")
public class AppConfig  {
    // ...
}

此方式我们将在后续讲解

@Component及其引伸的注解

  • @Component 所有的bean我们都可以使用该注解,只是为了后续区分新增了下面的注解
  • @Controller MVC中控制层的注解,也就是我们的Controller用该注解
  • @Service 服务层的注解,我们所有的Service使用该注解
  • @Repository 持久层的注解
  • @Configuration Java编程配置使用的注解
  • @RestController 由@Controller和Spring MVC中的@ResponseBody组合起来的注解
  • @Scope 定义bean的作用范围

在我们定义的bean有明确的定义时我们就用对应的注解,如果我们的Controller,就用@Controller注解,当我们的Bean没有明确的定义时,我们就是要@Component注解进行配置即可。

@Component及其相关注解的命名规则

默认情况下,如果@Component注解没有定义value属性,那么默认使用对应类的类名首字母小写为bean的名称。

BeanNameGenerator接口

我们可以通过实现BeanNameGenerator接口来自定义bean的名称规则。
在这里插入图片描述
特殊情况,如在不同的包下有相同类名的类,那么默认的规则下,会出现相同名称的bean,这个时候我们可以使用FullyQualifiedAnnotationBeanNameGenerator在排除这种情况。

虽然我们自定义命名规则可以达到效果,但实际工作中我们还是建议在出现冲突的类的@Component及其相关注解上使用value属性定义名称。

依赖注入注解

@Autowired

该注解可以作用在字段上、方法上、构造方法上、方法参数上

  1. 作用在字段上时,该字段可以没有setter方法
  2. 作用在构造方法上时,如果同时作用在多个构造方法,那么所有的@Autowired的required属性都必须为false。
  3. required属性表示依赖的类是否必须能查找到。默认为true,如果没有查找到对应依赖将会报错。如果为false,没有查找到对应依赖将不会报错,对应依赖项为空。

@Autowired注入过程

  1. 首先按照byType方式查找对应的Bean,如果对应的结果唯一,则直接装配。如果不唯一,进行第二步。
    2.按照 byName方式筛选第一步的结果,如果结果唯一,则装配。如果不唯一且required属性为true,则抛出异常,如果required属性为false,则不注入,也不拋异常。

@Autowired自动注入ApplicationContext

在容器Bean中加入如下代码即可直接使用ApplicationContext实例

@Autowired
private ApplicationContext applicationContext;

无需再实现对应的Aware接口。相应的我们也可以通过同样的方式得到如下实例

  • BeanFactory
  • ApplicationContext
  • Environment
  • ResourceLoader
  • ApplicationEventPublisher
  • MessageSource

@Primary

用于控制byType装配时有多个匹配的结果,如果结果中某一个bean使用了@Primary注解,那么这个bean就会被自动装配,而不会抛出异常。
@Primary有如下两个地方可以使用
java编程试配置

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

直接在对应bean上注解

@Primary
@Component
public class MovieCatalog{
	// ...
}

@Qualifier

用于提供更细范围的Bean装配控制,@Qualifier对应bean的id或名称,可以限定为某一个特定的bean作为装配结果。
与@Autowired结合使用

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

会自动装配类型为MovieCatalog,且id或名称是main的bean。
@Qualifier还可以在构造函数或方法上使用

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

@Resource

javax.annotation.Resource注解,只能作用于字段和普通方法上,不能作用于构造函数。
@Resource有两个重要的属性,name和type,默认情况下@Resource使用byName进行装配。如果同时指定name和type属性,则按照name和type共同匹配的规则进行装配(也就是name和type都必须匹配)。

示例

MyService接口
package com.yyoo.boot.annotation;

public interface MyService {

    void print();

}

对应的两个实现类
package com.yyoo.boot.annotation;

import org.springframework.stereotype.Service;

@Service
public class MyServiceImplOne implements MyService{

    @Override
    public void print() {
        System.out.println("MyServiceImplOne print");
    }
}

package com.yyoo.boot.annotation;

import org.springframework.stereotype.Service;

@Service
public class MyServiceImplTwo implements MyService{

    @Override
    public void print() {
        System.out.println("MyServiceImplTwo print");
    }
}

TestController
package com.yyoo.boot.annotation;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class TestController {

    @Resource(name = "myServiceImplOne")
    private MyService service;

    public void print(){
        service.print();
    }

}

我们的@Service注解没有定义bean的名字,其默认使用类名首字母小写为名称,所以这里我们的@Resource的name属性对应写为类名首字母小写的名称。

xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        context:component-scan包含了context:annotation-config
        base-package属性定义了spring自动扫描的类的路径,此路径下所有被相关注解的类都将被扫描
    -->
    <context:component-scan base-package="com.yyoo"/>

</beans>
Demo主程序
package com.yyoo.boot.annotation;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo8 {

    @Test
    public void test(){

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
        // 添加一个关闭的钩子(hook)
        context.registerShutdownHook();

        TestController controller = context.getBean(TestController.class);
        controller.print();

    }

}

打印结果
MyServiceImplOne print

如果我们在示例的@Resource注解中添加type属性,那么如果我们定义的type不是MyService接口类型,则代码报错。因为没有名称为myServiceOne且类型不是MyService接口类型的bean。

我们对比@Autowired,其实@Resource实现了@Autowired和@Primary、@Qualifier组合的功能,而且在底层实现上也会更简单明了,所以官方推荐使用@Resource代替@Autowired。

@Autowired可以作用在字段上、构造函数上、方法上、方法参数上
@Resource不能作用在构造函数上,也不能作用在方法参数上

JSR 330的标准注解

@Inject和@Named组合

import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.findMovies(...);
        // ...
    }
}

与 一样@Autowired,您可以@Inject在字段级别、方法级别和构造函数参数级别使用。
@Named用于限定名称

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

@Inject没有required属性,我们可以使用@Nullable在达到同样的效果

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        // ...
    }
}

@Named和@ManagedBean与@Component等效

import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

不直接命名的方式也一样

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

JSR 330注解这里介绍得很粗略,有些可能还没见过,但基本的意思都表达了,示例也是官网示例。

上一篇:005-Spring Bean的生命周期与扩展处理器
下一篇:007-Spring IoC 基于Java编程的配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值