Spring Boot2.x-04Spring Boot基础-使用注解装配bean

简介: Spring Boot2.x-04Spring Boot基础-使用注解装配bean

概述


Spring Boot主要是通过注解来装配 Bean 到 Spring IoC 容器中,使用注解装配Bean就不得不提AnnotationConfigApplicationContext,很显然它是一个基于注解的 IoC 容器。


之前的博文 Spring-基于Java类的配置


通过Java配置文件@Bean的方式定义Bean

POJO类


package com.artisan.springbootmaster.pojo;
public class Artisan {
    public String name;
    public int age;
    // setter/getter
}


然后编写一个配置文件

package com.artisan.springbootmaster;
import com.artisan.springbootmaster.pojo.Artisan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
    @Bean(name = "artisan")
    public Artisan initArtisan(){
        Artisan artisan = new Artisan();
        artisan.setName("小工匠");
        artisan.setAge(20);
        return artisan;
    }
}


@Configuration 代表是一个 Java 配置文件 , Spring会根据它来生成 IoC 容器去装配 Bean

@Bean 代表将 initArtisan方法返回的 POJO 装配到 IoC 容器中,属性 name 定义 Bean 的名称,如果没有配置它,则会将方法名称“initArtisan作为 Bean 的名称保存到 Spring IoC 容器中 。


使用 AnnotationConfigApplicationContext 来构建

package com.artisan.springbootmaster;
import com.artisan.springbootmaster.pojo.Artisan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class LoadTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        //Artisan artisan =  applicationContext.getBean(Artisan.class);
        Artisan artisan = (Artisan) applicationContext.getBean("artisan");
        System.out.println(artisan.getName() + " || "  + artisan.getAge());
    }
}



new AnnotationConfigApplicationContext(AppConfig.class) 将 Java 配置文件 AppConfig 传递给 AnnotationConfigApplicationContext 的构造方法,这样它就能够实例化该配置类中定义的信息,然后将配置里面的 Bean 装配到 IoC 容器中

装载到IoC容器以后,就可以使用getBean来获取对应实例化的bean信息了


输出 :

20181024230911696.png

23:08:36.164 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'artisan'
小工匠 || 20


关键日志:Returning cached instance of singleton bean 'artisan' ,可以知道配置在配置文件中 的名称为 artisan的 Bean 已经被装配到 IoC 容器中 ,并且可以通过 getBean方法获取对应的 Bean.


通过注解扫描的方式(@Component/@ComponentScan)装配Bean


Spring 中可以使用 XML 或者 Java 配置文件的方式装配 Bean , 但是由于 Spring Boot 是基于注解的方式,因此我们来说下基于注解的方式.


上面的例子使用Java配置文件的方式,使注解@Bean 注入 Spring loC 容器中,假设有多个bean的话,就需要多个@Bean来标注多次。


Spring也提供通过扫描的方式去装配bean到IoC容器中。 对于扫描装配而言使用的注解是@Component和@ComponentScan.


@Component:标明哪个类被扫描进入 Spring IoC 容器

@ComponentScan:标明采用何种策略去扫描装配 Bean

同样的,我们还是用上个例子来演示下用法


我们先假设AppConfig1.java 和 Artisan.java在同一个包下面 ,

20181025230434620.png


然后对Artisan这个类加上@Component注解,如下

package com.artisan.springbootmaster.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("artisan")
public class Artisan {
    @Value("little_artisan")
    public String name;
    @Value("99")
    public int age;
  // setter/getter
}


注解@Component 表明这个类将被Spring IoC容器扫描装配,bean的名称为artisan。 如果不配置这个值 ,那IoC 容器就会把类名第一个字母作为小写,其他的不变作为 Bean 名称放入到 IoC 容器中。


注解@Value 则是指定具体的值,使得 Spring IoC 给予对应的属性注入对应的值


为了让 Spring IoC 容器装配这个类 , 我们来改造下AppConfig,重新命名为AppConfig1,加入注解@ComponentScan,并取消掉其中的@Bean的配置。

package com.artisan.springbootmaster.pojo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class AppConfig1 {
}


加入了@ComponentScan,意味着它会进行扫描,但是只是个干巴巴的注解,什么属性都没设置,这就意味着它只会扫描类 AppConfig1 所在的当前包和其子包。 因为Artisan和它在同一个目录下,所以可以删掉之前使用@Bean 标注的创建对象方法。

测试同第一个例子

package com.artisan.springbootmaster;
import com.artisan.springbootmaster.pojo.AppConfig1;
import com.artisan.springbootmaster.pojo.Artisan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class LoadTest2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig1.class);
        Artisan artisan =  applicationContext.getBean(Artisan.class);
        System.out.println(artisan.getName() + " || "  + artisan.getAge());
    }

20181025231754869.png


23:17:05.981 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'artisan'
little_artisan || 99


来,继续优化,上面为了让Spring扫描到Artisan, 把Artisan.java类和AppConfig1.java放在一块,实在是不合理。所以:使用 @ComponentScan自定义扫包

那就去看下@ComponentScan源码吧

定义:

20181025232251382.png


@Repeatable(ComponentScans.class) 是说可重复定义,可定义多个。

方法:


20181025232516529.png


说几个比较常用的


basePackages: 定义扫描的包名,在没有定义的情况下,只会扫描当前包和其子包下的路径。

includeFilters :定义满足过滤器( Filter )条件的 Bean 才去 扫描,

excludeFilters :排除过滤器条件的 Bean , 和includeFilters 一样都需要通过注解@Filter 去定义,@Filter中的type 类型,可以定义为注解或者正则式等类型

@Filter中classes属性定义注解类, pattern属性 定义正则式类。


来吧,把Artisan还是放在pojo下,AppConfig1.java换个地方吧 ,并通过以下任意方式指定使得 IoC 容器去扫描到 User 类即可

20181025233835520.png

package com.artisan.springbootmaster;
import com.artisan.springbootmaster.pojo.Artisan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//@ComponentScan(basePackages = "com.artisan.springbootmaster.*")
@ComponentScan(basePackages = "com.artisan.springbootmaster.pojo")
//@ComponentScan(basePackageClasses = Artisan.class)
public class AppConfig1 {
}


运行测试,结果同样可以获取到

2018102523384726.png


使用excludeFilters属性不让IoC加载某些Bean


假设AppConfig1上配置的basePackages 属性为basePackages = "com.artisan.springbootmaster.*", 在springbootmaster目录下还有个service包,里面的类都标注了@Service注解,假设我们只想让IoC容器扫描到Artisan类,而不扫描ArtisanService类呢?

20181025235047592.png


只需要加上excludeFilters属性,通过excludeFilters 指定排除掉标注了Service注解的类即可

package com.artisan.springbootmaster;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
@Configuration
@ComponentScan(basePackages = "com.artisan.springbootmaster.*",
                excludeFilters = {@ComponentScan.Filter(classes = {Service.class})})
public class AppConfig1 {
}


验证下吧

package com.artisan.springbootmaster;
import com.artisan.springbootmaster.pojo.Artisan;
import com.artisan.springbootmaster.service.ArtisanService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class LoadTest2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig1.class);
        Artisan artisan =  applicationContext.getBean(Artisan.class);
        System.out.println(artisan.getName() + " || "  + artisan.getAge());
        ArtisanService artisanService =  applicationContext.getBean(ArtisanService.class);
        artisanService.doSomething();
    }
}


23:54:04.274 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'artisan'
little_artisan || 99
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.artisan.springbootmaster.service.ArtisanService' available
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:346)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
  at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
  at com.artisan.springbootmaster.LoadTest2.main(LoadTest2.java:16)
Process finished with exit code 1

可以看到,No qualifying bean of type 'com.artisan.springbootmaster.service.ArtisanService' available,Spring IoC容器并没有在启动的时候去扫表标注了@Service的ArtisanService类,说明excludeFilters 起了作用 。由于加入了 excludeFilters 的配置,使标注了@Service 的类将不被 IoC 容器扫描注入


装配第三方 Bean


一个项目中,不可避免的要使用到第三方的jar,如果希望把第三方包的类对象也放入到 Spring IoC 容器中,@Bean 注解就发挥用处了。

如下

package com.artisan.redpacket.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
@Configuration
//定义Spring 扫描的包
@ComponentScan(value= "com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value ={Service.class})})
//使用事务驱动管理器
@EnableTransactionManagement
//实现接口TransactionManagementConfigurer,这样可以配置注解驱动事务
public class RootConfig implements TransactionManagementConfigurer {
  private DataSource dataSource = null;
  /**
   * 配置数据库.
   * @return 数据连接池
   */
  @Bean(name = "dataSource")
  public DataSource initDataSource() {
    if (dataSource != null) {
      return dataSource;
    }
    try {
      Properties props = new Properties();
      props.load(RootConfig.class.getClassLoader().getResourceAsStream("jdbc.properties"));
      props.setProperty("driverClassName", props.getProperty("jdbc.driver"));
      props.setProperty("url", props.getProperty("jdbc.url"));
      props.setProperty("username", props.getProperty("jdbc.username"));
      props.setProperty("password", props.getProperty("jdbc.password"));
      dataSource = BasicDataSourceFactory.createDataSource(props);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return dataSource;
  }
  /***
   * 配置SqlSessionFactoryBean
   * @return SqlSessionFactoryBean
   */
  @Bean(name="sqlSessionFactory")
  public SqlSessionFactoryBean initSqlSessionFactory() {
    SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
    sqlSessionFactory.setDataSource(initDataSource());
    //配置MyBatis配置文件
    Resource resource = new ClassPathResource("mybatis/mybatis-config.xml");
    sqlSessionFactory.setConfigLocation(resource);
    return sqlSessionFactory;
  }
  /***
   * 通过自动扫描,发现MyBatis Mapper接口
   * @return Mapper扫描器
   */
  @Bean 
  public MapperScannerConfigurer initMapperScannerConfigurer() {
    MapperScannerConfigurer msc = new MapperScannerConfigurer();
    msc.setBasePackage("com.*");
    msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
    msc.setAnnotationClass(Repository.class);
    return msc;
  }
  /**
   * 实现接口方法,注册注解事务,当@Transactional 使用的时候产生数据库事务 
   */
  @Override
  @Bean(name="annotationDrivenTransactionManager")
  public PlatformTransactionManager annotationDrivenTransactionManager() {
    DataSourceTransactionManager transactionManager = 
           new DataSourceTransactionManager();
    transactionManager.setDataSource(initDataSource());
    return transactionManager;
  }
}


上面通过@Bean 如果指定了name属性的名字,Spring 就会把该name的值作为bean的名称 保存在 loC 容器中如果不填name的值,Spring就会用方法名作为 Bean 名称保存到IoC 容器中。

相关文章
|
1月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
121 26
|
2月前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
50 6
|
2月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
499 12
|
2月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
93 8
|
3月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
3月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
3月前
|
缓存 前端开发 Java
【Spring】——SpringBoot项目创建
SpringBoot项目创建,SpringBootApplication启动类,target文件,web服务器,tomcat,访问服务器
|
3月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
78 6
|
9月前
|
Java 开发者 Spring
解析Spring中Bean的生命周期
解析Spring中Bean的生命周期
78 2
|
9月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
94 0