Spring注解驱动开发——自动装配

一、@Autowired注解

BookService类使用@Autowired注解自动装配BookDao属性

@Service
public class BookService {

    @Autowired
    private BookDao bookDao;

    public void print(){
        System.out.println(bookDao);
    }

}

配置类

/**
 * 自定装配
 *
 * spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
 */
@ComponentScan({"spring.annotation.service","spring.annotation.dao","spring.annotation.controller"})
@Configuration
public class MainConfigAutowired {
}

测试

    @Test
    public void testAutowired(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigAutowired.class);

        BookService bookService = context.getBean(BookService.class);
        bookService.print();

        BookDao bookDao = context.getBean(BookDao.class);
        System.out.println(bookDao);
    }

输出结果:
在这里插入图片描述
通过打印的booDao对象可以看出,bookService对象里面注入的bookDao属性和IOC容器中的是同一个对象。

@Autowired注解实现自动注入,默认优先按照类型去容器中找对应的组件,如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。

可以使用@Qualifier注解明确指定需要装配的组件id,而不是使用属性名称去查找。

自动装配默认必须赋值,如果在容器中没有找到对应的组件就会报错,可以使用@Autowired(required = false)在没有装配成功的情况下不会报错,而是赋值为null

@Primary注解可以指定首选组件,即有多个相同类型组件时,默认装配标有@Primary注解的组件。也可以同时使用@Qualifier注解强制装配指定的组件。

二、@Resource@Inject

@Resource@Inject是****,也能完成自动注入

@Resource注解默认是按照组件名称进行装配的,不支持和@Primary@Autowired注解配合使用

    @Resource
    private BookDao bookDao;

@Inject注解也是可以自动装配,需要单独导入依赖

	@Inject
	private BookDao bookDao;

所有的自动装配注解都是由AutowiredAnnotationBeanPostProcessor这个后置处理器来解析完成自动装配功能

三、方法、构造器位置的自动装配

@Autowired注解可以标注在方法

@Controller
public class BookController {

    private BookService bookService;

    //@Autowired注解标注在方法上,spring容器创建当前对象就会调用方法完成赋值
    //方法使用的参数,自定义类型的值从ioc容器中获取
    @Autowired
    public void setBookService(BookService bookService) {
        this.bookService = bookService;
    }

    @Override
    public String toString() {
        return "BookController{" +
                "bookService=" + bookService +
                '}';
    }
}

测试

    @Test
    public void testAutowiredMethod(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigAutowired.class);

        BookController bookController = context.getBean(BookController.class);
        System.out.println(bookController);

        BookService bookService = context.getBean(BookService.class);
        System.out.println(bookService);
    }

输出结果:
在这里插入图片描述
通过打印日志可以看出,BookController中通过set方法自动装配的BookService组件,和IOC容器的BookService组件是同一个

@Bean标注的方法创建对象的时候,方法参数的值从容器中获取

@ComponentScan({"spring.annotation.service","spring.annotation.dao","spring.annotation.controller"})
@Configuration
public class MainConfigAutowired {

    @Bean
    public Boy boy(BookService bookService){
        Boy boy = new Boy();
        boy.setBookService(bookService);
        return boy;
    }
}

测试

    @Test
    public void testAutowiredMethod(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigAutowired.class);
        
        BookService bookService = context.getBean(BookService.class);
        System.out.println(bookService);

        Boy boy = context.getBean(Boy.class);
        System.out.println(boy.getBookService());
    }

输出结果:
在这里插入图片描述

@Autowired注解可以标注在构造器

默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作。可以把@Autowired注解标注在构造器上来自动装配组件。如果组件只有一个有参构造器,这个有参构造器@Autowired注解可以省略。

    @Autowired
    public BookController(BookService bookService) {
        System.out.println("调用BookController有参构造器...");
        this.bookService = bookService;
    }

输出结果:
在这里插入图片描述

@Autowired注解可以标注在参数

    public BookController(@Autowired BookService bookService) {
        System.out.println("调用BookController有参构造器...");
        this.bookService = bookService;
    }
    public void setBookService(@Autowired BookService bookService) {
        this.bookService = bookService;
    }

四、Aware注入Spring底层组件及原理

自定义组件想要使用Spring容器底层的一些组件,如ApplicationContextBeanFactory等等,需要自定义组件实现对应的Aware接口,在创建对象的时候,就会调用接口规定的方法注入相关组件。每个Aware都有与之对应的BeanProcessor后置处理器,通过后置处理器来处理实现了对应接口的Bean。

创建Guy组件,Guy用到的其它组件功能都会以接口方法回调的方式传入相关的组件,进而调用组件相应的方法

@Component
public class Guy implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println("传入的IOC容器:" + applicationContext);
    }

    public void setBeanName(String name) {
        System.out.println("当前Bean的名字:" + name);
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        //解析字符串占位符
        String resolveStringValue = resolver.resolveStringValue("本机OS:${os.name}");
        System.out.println("解析的字符串:" + resolveStringValue);
    }
}

容器加载Guy组件输出结果:
在这里插入图片描述

原理:

Guy组件初始化的时候利用后置处理器判断Guy实现了ApplicationContextAware接口,然后后置处理器调用setApplicationContext方法将ioc容器传了过来
Spring注解驱动开发——自动装配_第1张图片
Spring注解驱动开发——自动装配_第2张图片

五、@Profile根据环境注册Bean

Profile是spring提供可以根据当前环境,动态的激活和切换一系列组件的功能

配置类

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{

    @Value("${db.user}")
    private String user;

    private StringValueResolver valueResolver;

    private String driverClass;

    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/myemployees");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
    }
}

在没有使用@Profile注解时,IOC容器启动时所有的bean都会注册到容器中
Spring注解驱动开发——自动装配_第3张图片
使用@Profile指定组件在哪个环境的情况下才能被注册到容器中

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{

    @Value("${db.user}")
    private String user;

    private StringValueResolver valueResolver;

    private String driverClass;

    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/myemployees");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
        ComboPooledDataSource dataSource= new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
    }
}

使用命令行动态参数,在虚拟机参数位置设置运行时环境为test
Spring注解驱动开发——自动装配_第4张图片
输出结果:
Spring注解驱动开发——自动装配_第5张图片
还可以使用代码方式激活某种环境

    @Test
    public void testProfile(){
        //创建AnnotationConfigApplicationContext
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //设置需要激活的环境
        context.getEnvironment().setActiveProfiles("test","dev");
        //注册主配置类
        context.register(MainConfigOfProfile.class);
        //启动刷新容器
        context.refresh();


        String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
        for (String beanName : beanNamesForType){
            System.out.println(beanName);
        }
    }

输出结果:
Spring注解驱动开发——自动装配_第6张图片
如果@Profile注解标注在配置类上,只有是指定的环境的情况下,整个配置类里面的所有配置才能开始生效

你可能感兴趣的:(Spring框架,Spring,注解驱动,自动装配)