当前位置 : 主页 > 编程语言 > java >

Spring IoC全注解开发-装配Bean

来源:互联网 收集:自由互联 发布时间:2022-07-13
在Spring中允许我们通过XML或者Java配置文件装配Bean,但是由于Spring Boot是基于注解开发,因此下面是通过注解来介绍Spring的用法,以满足Spring Boot开发者的需要。 通过扫描装配你的Bean 如


在Spring中允许我们通过XML或者Java配置文件装配Bean,但是由于Spring Boot是基于注解开发,因此下面是通过注解来介绍Spring的用法,以满足Spring Boot开发者的需要。

  • 通过扫描装配你的Bean
    如果一个个的Bean使用@Bean注解注入Spring IoC容器中,那么将是一件非常麻烦的事情。好在Spring还允许我们进行扫描装配Bean到IoC容器中,对于扫描装配而言使用的注解是@Component和@ComponentScan,@Component标注那个类被扫描进Spring Ioc容器,而@ComponnetScan则是表明采用何种策略去扫描装配Bean
    这里我们把代码清单中的User移动到包package cn.hctech2006.boot.bootall.config中,然后对其进行修改。代码如下:
  • package cn.hctech2006.boot.bootall.config;

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;

    @Component("user")
    public class User {
    @Value("1")
    private Long id;
    @Value("USER_NAME_1")
    private String userName;
    @Value("NOTE_1")
    private String note;

    public Long getId() {
    return id;
    }

    public void setId(Long id) {
    this.id = id;
    }

    public String getUserName() {
    return userName;
    }

    public void setUserName(String userName) {
    this.userName = userName;
    }

    public String getNote() {
    return note;
    }

    public void setNote(String note) {
    this.note = note;
    }
    }

    这里的注解@Component表明这个类将被Spring IoC容器扫描装配,其中配置的user则会作为Bean的名称,当然你也可以不配置这个字符串,那么IoC容器就会把类名第一个字母作为小写,其他不变作为Bean名称放入IoC容器中;注解@Value则是指定具体的值,使的Spring IOC给予指定的属性注入对应的值。为了让Spring IoC容器装配这个类,需要改造AppConfig,代码如下:

    package cn.hctech2006.boot.bootall.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    @ComponentScan
    public class AppConfig {

    }

    这里加入@ComponentScan,意味着他会进行扫描,但是它只会扫描AppConfig所在的当前包和其子包,之前把User.java转移到package cn.hctech2006.boot.bootall.config,就是这个原因。这样就可以删掉之前使用的@Bean标注的创建对象方法,然后进行测试,测试清单如下:

    package cn.hctech2006.boot.bootall.context;

    import cn.hctech2006.boot.bootall.config.User;
    import cn.hctech2006.boot.bootall.config.AppConfig;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    import java.util.logging.Logger;

    public class IoCTest {
    private static Logger log = Logger.getLogger(String.valueOf(IoCTest.class));
    public static void main(String[] args){
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    User user = ctx.getBean(User.class);
    log.info(user.getUserName());
    }
    }

    这样就可以运行了,但是为了使的USer类能够被扫描,上面我们把它迁移到了不该放置他的配置包,这就显得太不合理了。为了更家合理,@ComponentScan还允许我们自定义扫描的包。下面探讨他的配置项。
     首先探讨@ComponentScan的源码

    (RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    //在一个类中可以重复定义
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    //定义扫描的包
    @AliasFor("basePackages")
    String[] value() default {};
    //定义扫描的包
    @AliasFor("value")
    String[] basePackages() default {};
    //定义扫描的类
    Class<?>[] basePackageClasses() default {};
    //Bean name生成器
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    //作用域解析器
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    //作用域代理解析器
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    //资源匹配模式
    String resourcePattern() default "**/*.class";
    //是否启用默认的过滤器
    boolean useDefaultFilters() default true;
    //当满足过滤条件时扫描
    ComponentScan.Filter[] includeFilters() default {};
    //当不满足过滤条件时扫描
    ComponentScan.Filter[] excludeFilters() default {};
    //是否延迟初始化
    boolean lazyInit() default false;
    //定义过滤器
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
    //过滤器类型,可以按照注解类型或者正则表达式等过滤
    FilterType type() default FilterType.ANNOTATION;
    //定义过滤的类
    @AliasFor("classes")
    Class<?>[] value() default {};
    //定义过滤的类
    @AliasFor("value")
    Class<?>[] classes() default {};
    //匹配方式
    String[] pattern() default {};
    }
    }

    首先通过配置项basePackages定义扫描的包名,在没有定义的情况下,它只会扫描当前包和其子包下的路径:还可以通过basePackageClasses定义扫描的类:其中还有includeFilters和excludeFilters,includeFilters是定义满足过滤器条件的Bean去扫描,excludeFilter是排除过滤器条件的Bean,他们都需要通过注解@Filter去定义,他又一个type类型,这里可以定义为注解或者正则式等类型。classes定义注解类,pattern定义正则式类。
    此时我们再把User类放到package cn.hctech2006.boot.bootall.bean;中,这样User和AppConfig就不再同包,那么我们将AppConfig的注解修改为:

    @Configuration
    @ComponentScan("cn.hctech2006.boot.bootall.*")
    public class AppConfig {

    }

    包名可以采用正则表达式去匹配。但是有时候,有时候我们的需求是想扫描一些包,将一些bean装配到Spring IoC容器,而不是加载这个包里面的某些bean。比方说,现在我们有一个UserService类,为了标注他是服务类,将类标注为@Service(该标准注入了@Component,所以在默认的情况下他会被Spring扫描装配到IoC容器中),这里再假设我们采用了策略:

    @ComponentScan("cn.hctech2006.boot.bootall.*")

    这样cn.hctech2006.boot.bootall.bean和cn.hctech2006.boot.bootall.service这两个包都会被扫描,此时我们定义的UserService类如下:

    @Service
    public class UserService {
    public void printUser(User user){
    System.out.println("编号:"+user.getId());
    System.out.println("用户名称:"+user.getUserName());

    }
    }

    按照以上装配策略,他将会被扫描到Spring IoC容器中。为了不被装配,需要把扫描的策略修改为:

    @ComponentScan(value = "cn.hctech2006.boot.bootall.*",excludeFilters = {@ComponentScan.Filter(classes = {Service.class})})

    这样由于加入了excludeFilters的配置,使得标注了@Service的类将不能被IoC容器扫描注入。事实上,之前zaiSpring Boot上述实例中看到的注解@SpringBootApplication也注入了@ComponentScan,这里不妨探究其源代码

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    //自定义排除的扫描类
    @ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
    ), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
    //通过类型排除自动配置类
    @AliasFor(
    annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};
    //通过名称排除自动配置类
    @AliasFor(
    annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};
    //自定义扫描包
    @AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackages"
    )
    String[] scanBasePackages() default {};
    //自定义扫描的类
    @AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
    annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
    }

    显然它能够定义扫描那些包,但是要注意,它提供的exclude和excludeName两个方法是对于其内部的自动装配类菜生效的。为了排除其他类,可以再加入@ComponentScan以达到我们的目的。例如扫描User而不扫描UserService,可以吧启动配置文件写成:

    (excludeFilters = {@ComponentScan.Filter(classes = Service.class)})
    public class BootAllApplication {

    public static void main(String[] args) {
    SpringApplication.run(BootAllApplication.class, args);
    }

    }

    这样就能够扫描指定的包并且排除对应的类了。

    Spring IoC全注解开发-装配Bean_spring


    确实没有生成service

    2. 自定义第三方Bean

    现实的Java应用往往需要引入许多来自第三方的包,并且很有可能希望把第三方的类对象也放入到Spring IoC容器中,这时@Bean注解就可以发挥作用了。

    例如,要引入一个DBCP数据源,我们先在pom.xml上加入项目所需的DBCP包和数据库Mysql的驱动依赖。代码如下

    <!--DBCP数据源-->
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    </dependency>
    <!--MySQL驱动-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>

    这里DBCP和数据库驱动就被加入到项目中,接着使用他提供的机制来生成数据源。这时候可以吧代码清单的代码放入Appconfig.java中。

    package cn.hctech2006.boot.bootall.config;

    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Service;

    import javax.sql.DataSource;
    import java.util.Properties;

    @Configuration
    @ComponentScan(value = "cn.hctech2006.boot.bootall.*",excludeFilters = {@ComponentScan.Filter(classes = {Service.class})})

    public class AppConfig {
    @Bean(name = "dataSource")
    public DataSource getDataSource(){
    Properties props = new Properties();
    props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
    props.setProperty("url",
    "jdbc:mysql://172.17.0.1:3306/hc_official_website_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
    props.setProperty("username", "root");
    props.setProperty("password", "123456");
    DataSource dataSource = null;
    try{
    dataSource = BasicDataSourceFactory.createDataSource(props);
    }catch (Exception e){
    e.printStackTrace();
    }
    return dataSource;
    }

    }这里通过@Bean定义了其配置项name为“dataSource”,那么Spring就会把他返回的对象用名称“dataSource”保存在IoC容器中。当然你也可以不填写这个名称,那么他就会用你的方法名称作为Bean的名称保存到IoC容器中。通过这样,就可以将第三方类装配到Spring IoC容器中。


    上一篇:Spring IoC全注解开发-依赖注入
    下一篇:没有了
    网友评论