接触过Spring的同学肯定都听过IOC。在传统的Java编程中,当需要用到某个对象的时候,我们都是主动显式创建一个对象实例(new)。使用Spring后就不需要这样做了,因为Spring会帮我们在需要用到某些对象的地方自动注入该对象,而无须我们自己去创建。这种模式俗称控制反转,即IOC(Inversion of Control)。那么Spring是从什么地方获取到我们所需要的对象呢?其实Spring给我们提供了一个IOC容器,里面管理着所有我们需要的对象,组件注册就是我们去告诉Spring哪些类需要交给IOC容器管理。
// 返回 IOC 容器,基于 XML配置,传入配置文件的位置 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xxx.xml"); User user = (User) applicationContext.getBean("user");
Spring 4后推荐我们使用Java Config的方式来注册组件。
然后创建一个User类:
1 2 3 4 5 6 7
@ToString @AllArgsConstructor @Data public class User { private String name; private Integer age; }
接着创建一个配置类,在里面通过@Bean注解注册User类:
1 2 3 4 5 6 7
@Configuration public class WebConfig { @Bean() public User user() { return new User("mrbird", 18); } }
@RunWith(SpringRunner.class) @SpringBootTest public class RegistBeanApplicationTests { @Test public void test() { // 返回 IOC 容器,使用注解配置,传入配置类 ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class); User user = context.getBean(User.class); System.out.println(user); } }
@Configuration public class WebConfig { @Bean("myuser") public User user() { return new User("wno704", 28); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12
@RunWith(SpringRunner.class) @SpringBootTest public class RegistBeanApplicationTests { @Test public void test() { // 返回 IOC 容器,使用注解配置,传入配置类 ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class); // 查看 User 这个类在 Spring 容器中叫啥玩意 String[] beanNames = context.getBeanNamesForType(User.class); Arrays.stream(beanNames).forEach(System.out::println); } }
/** * 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 {};
其中Filter也是一个注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/** * 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 {
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Scope {
/** * Alias for {@link #scopeName}. * @see #scopeName */ @AliasFor("scopeName") String value() default "";
/** * Specifies the name of the scope to use for the annotated component/bean. * <p>Defaults to an empty string ({@code ""}) which implies * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}. * @since 4.2 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE * @see ConfigurableBeanFactory#SCOPE_SINGLETON * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION * @see #value */ @AliasFor("value") String scopeName() default "";
/** * Specifies whether a component should be configured as a scoped proxy * and if so, whether the proxy should be interface-based or subclass-based. * <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates * that no scoped proxy should be created unless a different default * has been configured at the component-scan instruction level. * <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML. * @see ScopedProxyMode */ ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
@Service @Profile("java7") public class Java7CalculateServiceImpl implements CalculateService {
@Override public Integer sum(Integer... value) { System.out.println("Java 7环境下执行"); int result = 0; for (int i = 0; i <= value.length; i++) { result += i; } return result; } }
1 2 3 4 5 6 7 8 9 10
@Service @Profile("java8") public class Java8CalculateServiceImpl implements CalculateService {
@RunWith(SpringRunner.class) @SpringBootTest public class RegistBeanApplicationTests { @Test public void test2(){ ConfigurableApplicationContext context1 = new SpringApplicationBuilder(RegistBeanApplication.class) .web(WebApplicationType.NONE) .profiles("java7") .run();
/** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. * @return the class names, or an empty array if none */ String[] selectImports(AnnotationMetadata importingClassMetadata);
/** * Return a predicate for excluding classes from the import candidates, to be * transitively applied to all classes found through this selector's imports. * <p>If this predicate returns {@code true} for a given fully-qualified * class name, said class will not be considered as an imported configuration * class, bypassing class file loading as well as metadata introspection. * @return the filter predicate for fully-qualified candidate class names * of transitively imported configuration classes, or {@code null} if none * @since 5.2.4 */ @Nullable default Predicate<String> getExclusionFilter() { return null; }
class com.wno704.boot.test.factory.CherryFactoryBean
为什么加上&前缀就可以获取到相应的工厂类了呢,查看BeanFactory的源码会发现原因:
1 2 3 4 5 6 7 8 9 10 11
public interface BeanFactory {
/** * Used to dereference a {@link FactoryBean} instance and distinguish it from * beans <i>created</i> by the FactoryBean. For example, if the bean named * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject} * will return the factory, not the instance returned by the factory. */ String FACTORY_BEAN_PREFIX = "&"; ...... }