乐趣区

关于spring:Spring-FrameWork从入门到NB-classpath扫描和组件托管

通过 @Configuration 注解指定包扫描门路后,注解形式能够齐全代替 xml 配置的形式实现 Bean 的注入,

@Configuration & @ComponentScan

@Configuration 注解作用在 class 上,指定以后类为配置类:

@Configuration
@ComponentScan(value={"springTest"})
@PropertySource("classpath:application.properties")
public class MyConfiguration {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();
    }
}

@ComponentScan 指定包扫描门路,Spring IoC 容器将主动扫描该门路,加载门路下的基于 @Component 的组件注解(包含 @Component、@Service、@Controller、@Repository)的类,作为 Bean 退出到 Spring IoC 容器中。

定制化包扫描

能够通过 @ComponentScan 注解的 includeFilters 和 excludeFilters 过滤器定制化 Spring 的扫描行为:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {// ...}

Spring 反对的过滤器类型:

也就是说,能够通过 includeFilters 或 excludeFilters 指定某一个 class 文件、aspectj 类型、正则表达式、或者实现 org.springframework.core.type.TypeFilter 指定某一个或某一类组件加载或者不加载到 Spring IoC 容器中。

Spring IoC 的通过注解主动扫描加载 Bean 的形式还是非常灵活的。如果再联合 SpringBoot 的一系列 @Conditional 注解,主动拆卸的能力又上了一个台阶,给开发带来的灵活性就更大了。

组件注解

Spring 提供了包含 @Component,@Service,@Controller 和 @Repository 等属性注解,等同于 xml 配置文件的:

<bean id=”…” …>

这类注解加到 class 上,只有该 class 文件处于 Spring 的包扫描门路下,就会在 Spring 启动过程中被主动注入到 Spring IoC 容器中。

其中,@Component 是根底的组件注解,@Service 示意 module 层的服务组件,@Controller 示意是 controller 层的管制组件,@Repository 示意是 dao 层的数据组件。

其实 @Component,@Service,@Controller 和 @Repository 注解底层都是基于 @Component 实现的,@Service、@Controller 和 @Repository 很大意义上只是一种应用标准,除了 @Repository 注解有特定的异样解决机制之外,其实注入 Spring 的时候并没有太大的区别。

@Bean 注解

@Bean 是办法注解,能够和 @Component 以及 @Configuration 配合应用。被 @Bean 注解的办法的返回对象会作为 bean 退出 Spring IoC 容器。所以加了 @Bean 办法的类就相当于一个工厂类,@Bean 办法相当于工厂办法,负责生成 Spring Bean 对象。

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance(@Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {return new TestBean("requestScopedInstance", 3);
    }
}

以上代码会生成一个 name 为 prototypeInstance、type 为 TestBean 的原型对象,注入到 Spring IoC 容器中。

@Bea 也能够作用在静态方法上,次要与 @Configuration 配合应用,作用在静态方法上容许宿主对象尚未实例化的状况下被调用。这一场景应该只有非凡状况下有用,Spring 官网就说了一种非凡状况:定制化实现 post-processor bean(比方 BeanFactoryPostProcessor or BeanPostProcessor),因为 post-processor bean 在 Spring 初始化的晚期就会被调用,那个时候咱们的 @Configuration 还没实现创立了,所以正好能够利用这一个性、定义为 static 办法。

然而静态方法不会被拦挡、所以无奈实现 AOP,因为 Spring 应用 CGLIB 实现 AOP,而 CGLIB 通过子类实现性能扩大的,因而静态方法是不会被拦挡、无奈实现 AOP 的。

同样的起因,因为 @Configuration 中的 @Bean 办法是须要被 CGLIB 扩大实现的,所以,@Configuration 中的 @Bean 办法必须是可扩大的,不能是公有办法。

@Configuration 和 @Component 下的 @Bean 注解的区别?

先看 Spring 官网的解释:

The @Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Bean methods in @Configuration classes creates bean metadata references to collaborating objects. Such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans, even when referring to other beans through programmatic calls to @Bean methods. In contrast, invoking a method or field in a @Bean method within a plain @Component class has standard Java semantics, with no special CGLIB processing or other constraints applying.

大略意思是说,@Bean 办法在 @Configuration 和 @Component 类中是不同的,不同之处在于:因为 Spring 中的 @Component 类并没有采纳 CGLIB 进行加强,所有他的办法不会被拦挡,而 @Configuration 在 Spring 中是通过 CGLIB 加强的,其中的办法是能够被拦挡的,因而,@Configuration 中的 @Bean 创立的类是能够退出到 Spring 的 IoC 容器中并且能够被 Spring 的 Bean 生命周期治理的,然而,@Component 中的尽管能够被退出到 Spring IoC 容器中然而无奈被 Spring 的 Bean 申明周期治理。

看明确了吗?解释一下。

对于 Spring 的 Bean 生命周期,咱们前面会具体说,就是 Spring 容器中 Bean 的创立、初始化、BeanPostProcessor 解决、销毁等等一系列过程,依照官网文档所说,通过 @Component 的 @Bean 办法因为宿主类不能被 Spring 依照生命周期进行治理、也就无奈通过 CGLIB 实现的代理。

结果是什么呢?

@Configuration 类的底层原理其实是,在 Spring 初始化的过程中通过 CGLIB 实现代理,对 @Bean 办法做了加强,将 @Bean 办法创立的对象作为 Spring 的 Bean 放入 IoC 容器,下次利用再次调用 @Bean 办法的时候,加强过的 @Bean 办法理论是从 IoC 容器获取 Bean 后返回、不会再次执行 @Bean 办法中的内容再次创建对象了。

再看一下例子,就高深莫测了。

还是用咱们后面的例子,首先在 MyConfiguration 中退出 @Bean:

@Configuration
@ComponentScan(value={"springTest"})
@PropertySource("classpath:application.properties")
public class MyConfiguration {@Value("${dependencyA.name}")
    public String dependencyAName;
    @Bean
    @Primary
    public DependencyA createDependencyA(){return new DependencyA(dependencyAName);
    }
}

而后在 Component 类中退出同样的内容:

@Component
public class DependencyA2 implements IDependencyA{
    @Override
    public void test() {System.out.println("This is dependencyA2 running...");
    }

    @Value("${dependencyA.name}")
    public String dependencyAName;
    @Bean
    @Primary
    public DependencyA createDependencyA(){return new DependencyA(dependencyAName);
    }

最初,在 DependencyB 类中退出测试方法:

@Component
public class DependencyB implements IDependencyB{
    @Autowired
    //@Qualifier(value="dependencyA3")
    private IDependencyA dependencyA;

    @Autowired
    DependencyA2 dependencyA2;
    @Autowired
    MyConfiguration myConfiguration;
    @Autowired
    private TestBean prototypeInstance;
    public static void main(String[] args) {System.out.println("I am a new comer...hello world");
    }
    @Override
    public void testB(){DependencyA da = dependencyA2.createDependencyA();
        DependencyA da1 = dependencyA2.createDependencyA();
        System.out.println("dependencyA"+dependencyA);
        System.out.println("da"+da);
        System.out.println("da1"+da1);
        DependencyA da2 = myConfiguration.createDependencyA();
        DependencyA da3 = myConfiguration.createDependencyA();
        System.out.println("myConfiguration"+myConfiguration);
        System.out.println("da2"+da2);
        System.out.println("da3"+da3);
    }

解释一下:在 Configuration 类和 Component 类中退出了同样的 @Bean 办法,创立 DependencyA 对象。

咱们在测试类中首先通过 @Autowired 注解主动封装了 Configuration 和 Component 对象,在测试方法中别离调用了他们的 @Bean 办法,并且别离打印了 Configuration 对象和 Component 对象。

运行启动类,测试:

dependencyAspringTest.DependencyA@d706f19
daspringTest.DependencyA@79efed2d
da1springTest.DependencyA@2928854b
myConfigurationspringTest.MyConfiguration$$EnhancerBySpringCGLIB$$97084d15@27ae2fd0
da2springTest.DependencyA@d706f19
da3springTest.DependencyA@d706f19

运行后果:

  1. 打印的 DependencyA 对象是 DependencyA@d706f19,是一般的 java 对象。
  2. 打印的 MyConfiguration 对象是 MyConfiguration$EnhancerBySpringCGLIB$$97084d15@27ae2fd0,是 CGLIB 代理对象。
  3. 两次调用 Component 类的 @Bean 办法,返回的是不同的 DependencyA 对象。
  4. 两次调用 Configuration 的 @Bean 办法,返回的是雷同的 DependencyA 对象。

例子很好的验证了咱们上面对官网文档的解释。

上一篇 Spring FrameWork 从入门到 NB -@primary、@Qualifier、@Resource&@Value

退出移动版