Spring IoC全注解开发-装配Bean

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

  1. 通过扫描装配你的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的源码


@Retention(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,可以吧启动配置文件写成:


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

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

}

这样就能够扫描指定的包并且排除对应的类了。
在这里插入图片描述
确实没有生成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容器中。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值