通过@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对象。

@Componentpublic 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类中退出同样的内容:

@Componentpublic 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类中退出测试方法:

@Componentpublic 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@d706f19daspringTest.DependencyA@79efed2dda1springTest.DependencyA@2928854bmyConfigurationspringTest.MyConfiguration$$EnhancerBySpringCGLIB$$97084d15@27ae2fd0da2springTest.DependencyA@d706f19da3springTest.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