一、前言
本文承接上一节:Spring_总结_02_依赖注入
在上一节我们了解到依赖注入的实质就是装配。
这一节我们来学习下装配Bean的相关知识。
二、Bean的装配机制
1.三种装配机制
Spring提供了三种主要的bean的装配机制:
(1)注解配置——隐式的bean发现机制和自动装配
(2)Java配置——在Java中进行显示配置
(3)XML配置——在XML中进行显示配置
2.如何选择
(1)第一考虑自动装配,显示配置越少越好。
(2)当必须要显示配置的时候,再使用类型安全并且比XML更强大的JavaConfig
(3)最后,只有当你想用使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。
这里以一个小例子来阐述组件扫描和装配。创建CompactDisc类,Spring会发现它并为其创建一个bean。然后,创建一个CDPlayer类,Spring会发现它,并将CompactDisc bean注入进来。
二、自动装配的过程
Spring从两个角度来实现自动化装配:
(1)组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的bean
(2)自动装配(autowiring):Spring 自动满足bean之间的依赖
实现自动装配的过程如下:
(1)启用组件扫描: @ComponentScan
(2)声明bean : @Component
(3) 自动装配bean : @Autowired
三、启用组件扫描
组件扫描默认是不开启的,我们需要显示配置一下Spring,从而让它寻找带有@Component注解的类,并为其创建Bean。
配置类 CDPlayerConfig
@Configuration //1 @ComponentScan //2 public class CDPlayerConfig { }
1. 默认扫描包
1处:声明这是一个配置类
2处:开启组件扫描。默认会扫描配置类所在同级包及其子包,查找带有@Component注解的类。
2. 设置组件扫描的基础包
ComponentScan源码:
/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.core.annotation.AliasFor; import org.springframework.core.type.filter.TypeFilter; /** * Configures component scanning directives for use with @{@link Configuration} classes. * Provides support parallel with Spring XML's {@code <context:component-scan>} element. * * <p>Either {@link #basePackageClasses} or {@link #basePackages} (or its alias * {@link #value}) may be specified to define specific packages to scan. If specific * packages are not defined, scanning will occur from the package of the * class that declares this annotation. * * <p>Note that the {@code <context:component-scan>} element has an * {@code annotation-config} attribute; however, this annotation does not. This is because * in almost all cases when using {@code @ComponentScan}, default annotation config * processing (e.g. processing {@code @Autowired} and friends) is assumed. Furthermore, * when using {@link AnnotationConfigApplicationContext}, annotation config processors are * always registered, meaning that any attempt to disable them at the * {@code @ComponentScan} level would be ignored. * * <p>See {@link Configuration @Configuration}'s Javadoc for usage examples. * * @author Chris Beams * @author Juergen Hoeller * @author Sam Brannen * @since 3.1 * @see Configuration */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { /** * Alias for {@link #basePackages}. * <p>Allows for more concise annotation declarations if no other attributes * are needed — for example, {@code @ComponentScan("org.my.pkg")} * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}. */ @AliasFor("basePackages") String[] value() default {}; /** * Base packages to scan for annotated components. * <p>{@link #value} is an alias for (and mutually exclusive with) this * attribute. * <p>Use {@link #basePackageClasses} for a type-safe alternative to * String-based package names. */ @AliasFor("value") String[] basePackages() default {}; /** * Type-safe alternative to {@link #basePackages} for specifying the packages * to scan for annotated components. The package of each class specified will be scanned. * <p>Consider creating a special no-op marker class or interface in each package * that serves no purpose other than being referenced by this attribute. */ Class<?>[] basePackageClasses() default {}; /** * The {@link BeanNameGenerator} class to be used for naming detected components * within the Spring container. * <p>The default value of the {@link BeanNameGenerator} interface itself indicates * that the scanner used to process this {@code @ComponentScan} annotation should * use its inherited bean name generator, e.g. the default * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the * application context at bootstrap time. * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator) */ Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; /** * The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components. */ Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; /** * Indicates whether proxies should be generated for detected components, which may be * necessary when using scopes in a proxy-style fashion. * <p>The default is defer to the default behavior of the component scanner used to * execute the actual scan. * <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}. * @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode) */ ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; /** * Controls the class files eligible for component detection. * <p>Consider use of {@link #includeFilters} and {@link #excludeFilters} * for a more flexible approach. */ String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; /** * Indicates whether automatic detection of classes annotated with {@code @Component} * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled. */ boolean useDefaultFilters() default true; /** * Specifies which types are eligible for component scanning. * <p>Further narrows the set of candidate components from everything in {@link #basePackages} * to everything in the base packages that matches the given filter or filters. * <p>Note that these filters will be applied in addition to the default filters, if specified. * Any type under the specified base packages which matches a given filter will be included, * even if it does not match the default filters (i.e. is not annotated with {@code @Component}). * @see #resourcePattern() * @see #useDefaultFilters() */ Filter[] includeFilters() default {}; /** * Specifies which types are not eligible for component scanning. * @see #resourcePattern */ Filter[] excludeFilters() default {}; /** * Specify whether scanned beans should be registered for lazy initialization. * <p>Default is {@code false}; switch this to {@code true} when desired. * @since 4.1 */ boolean lazyInit() default false; /** * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters * include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}. */ @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter { /** * The type of filter to use. * <p>Default is {@link FilterType#ANNOTATION}. * @see #classes * @see #pattern */ FilterType type() default FilterType.ANNOTATION; /** * Alias for {@link #classes}. * @see #classes */ @AliasFor("classes") Class<?>[] value() default {}; /** * The class or classes to use as the filter. * <p>The following table explains how the classes will be interpreted * based on the configured value of the {@link #type} attribute. * <table border="1"> * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr> * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td> * <td>the annotation itself</td></tr> * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td> * <td>the type that detected components should be assignable to</td></tr> * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td> * <td>an implementation of {@link TypeFilter}</td></tr> * </table> * <p>When multiple classes are specified, <em>OR</em> logic is applied * — for example, "include types annotated with {@code @Foo} OR {@code @Bar}". * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and * their respective methods will be called prior to {@link TypeFilter#match match}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware} * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware} * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware} * </ul> * <p>Specifying zero classes is permitted but will have no effect on component * scanning. * @since 4.2 * @see #value * @see #type */ @AliasFor("value") Class<?>[] classes() default {}; /** * The pattern (or patterns) to use for the filter, as an alternative * to specifying a Class {@link #value}. * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ}, * this is an AspectJ type pattern expression. If {@link #type} is * set to {@link FilterType#REGEX REGEX}, this is a regex pattern * for the fully-qualified class names to match. * @see #type * @see #classes */ String[] pattern() default {}; } }
其中,通过 basePackages 可用String类型来指定基础包。
通过 basePackageClasses 可用类或接口(建议使用空标记接口)来指定基础包,这些类或接口所在的包将会作为组件扫描的基础包。
示例如下:
@Configuration @ComponentScan(basePackages = "com.ray.soundsystem") public class CDPlayerConfig { } @Configuration @ComponentScan(basePackages = {"com.ray.soundsystem","com.ray.video"}) public class CDPlayerConfig { }
public interface SoundSystemScanConfig { } public interface VideoScanConfig { } @Configuration @ComponentScan(basePackageClasses = {SoundSystemScanConfig.class,VideoScanConfig.class}) public class CDPlayerConfig { }
四、声明bean
1. @Component声明bean
@Component public class ComPactDisc { public void play(){ System.out.println("play music"); } }
如上述代码所示,通过@Component来声明一个组件,表明该类会作为组件类,并告知Spring要为这个类创建bean。
可用以下注解来声明一个组件:
- @Component : 标注通用组件
- @Controller : 标注控制器
- @Service : 标注Service
- @Respository : 标注数据访问层
通过查看源码,可知,@Controller、@Service、@Repository 能声明bean 是因为他们都组合了@Component。
以Controller为例:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller
2.为组件命名
Spring 会为所有的bean都指定一个ID,默认为类名首字母小写。
但也可通过@Component的value属性来将为bean命名,如:
@Component("compactDisc1") public class ComPactDisc { public void play(){ System.out.println("play music"); } }
五、自动装配bean
public class CDPlayer { private ComPactDisc cd; @Autowired public CDPlayer( ComPactDisc cd){ this.cd =cd; } public void play(){ cd.play(); } }
(1)通过 @Autowired 注解,声明让Spring来自动注入符合要求的bean
(2)@Autowired 注解可用在属性上以及任何方法上。
(3)当@Autowired 注解用在方法上时,Spring会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求,那么这个bean将会被装配进来。
在Spring中,只要容器中存在某个bean,就可以在另外一个Bean的声明方法的参数中注入。
用来注入bean的注解还有:
@Inject
@Resource