深入理解Spring(三)、容器扩展

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

深入理解Spring源码分为7小节,本小节为Spring源码第三小节,各个小节目录如下。

  1. 扫描过程
  2. bean创建过程

3.  容器扩展

  1. AOP源码分析
  2. 事务源码分析
  3. Spring JDBC源码分析
  4. Spring常用设计模式

Spring提供了众多对外接口,这些接口有一个统一的父接口叫Aware,共有十多个,通过这些接口,我们可以修改Spring容器中大部分东西。

ApplicationContextAware

此接口用于Spring告诉bean应用程序上下文对象,调用时机在对象创建后,我们在上节说过,对象创建成功后,会调用所有BeanPostProcessor尝试修改此对象,具体的实现类是ApplicationContextAwareProcessor。

private void invokeAwareInterfaces(Object bean) {
   if (bean instanceof EnvironmentAware) {
      ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
   }
   if (bean instanceof EmbeddedValueResolverAware) {
      ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
   }
   if (bean instanceof ResourceLoaderAware) {
      ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
   }
   if (bean instanceof ApplicationEventPublisherAware) {
      ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
   }
   if (bean instanceof MessageSourceAware) {
      ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
   }
   if (bean instanceof ApplicationStartupAware) {
      ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
   }
   if (bean instanceof ApplicationContextAware) {
      ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
   }
}

在invokeAwareInterfaces()方法下一目了然。

通过EnvironmentAware可以获取所有属性文件中的值。

通过EmbeddedValueResolverAware也可以获取到属性文件中的值,但是获取的方式是这样的。

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
   System.out.println(resolver.resolveStringValue("${name}"));
}

由于应用程序上下文对象都是实现了ResourceLoaderAware的,所以这里传的还是applicationContext,其余也是一样,只不过各个用处不一样。

BeanClassLoaderAware

设置bean的类加载器,调用时期同样在对象创建后初始化对象阶段,这个阶段会调用三个接口,他比上面调用的时机要早。

private void invokeAwareMethods(String beanName, Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof BeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName);
      }
      if (bean instanceof BeanClassLoaderAware) {
         ClassLoader bcl = getBeanClassLoader();
         if (bcl != null) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
         }
      }
      if (bean instanceof BeanFactoryAware) {
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
   }
}

BeanNameAware用来设置bean的名称,BeanFactoryAware用来设置bean工厂,他的功能比较多,比较实用。

实战

下面假如有以下接口,我们不添加任何实现。

public interface UserMapping {
   int count(String id);
}

我们想直接这样使用。

@Component
public class UserTest {
   @Autowired
   UserMapping userMapping;
}

那要怎么做呢?

为这个接口创建一个代理,添加到容器即可,但问题是,Spring不支持直接添加实例对象到容器,只可以添加BeanDefinition,而BeanDefinition又只能设置class名称,未来会进行实例化,这就僵持住了。

打破的关键是FactoryBean。

首先实现一个ImportBeanDefinitionRegistrar,用来向容器中注册BeanDefinition,而注册的都是FactoryBean对象,Spring会在内部调用他的getObject()方法,并把他放入容器。

public class DatabaseImport implements ImportBeanDefinitionRegistrar {
   static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      Map<String, Object> mappingScan = importingClassMetadata.getAnnotationAttributes(MappingScan.class.getName());
      PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
      String basePackage = ClassUtils.convertClassNameToResourcePath(((String) mappingScan.get("value")));
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + basePackage + '/' + DEFAULT_RESOURCE_PATTERN;
      try {
         Resource[] resources = pathMatchingResourcePatternResolver.getResources(packageSearchPath);
         CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();
         for (Resource resource : resources) {
            MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            boolean anInterface = classMetadata.isInterface()
                  && !classMetadata.isAnnotation();
            if (anInterface) {
               String className = metadataReader.getClassMetadata().getClassName();
               AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                     .genericBeanDefinition(MappingFactoryBean.class)
                     .addConstructorArgValue(className)
                     .getBeanDefinition()
;
               String beanName = className.substring(className.lastIndexOf(".") + 1);
               registry.registerBeanDefinition(beanName, beanDefinition);
            }
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

这样我们在FactoryBean对象返回代理类即可。

public class MappingFactoryBean<Timplements FactoryBean<T{
   private Class<T> classType;
   public MappingFactoryBean(String className) {
      try {
         this.classType = (Class<T>) Class.forName(className);
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      }
   }
   @Override
   public T getObject() {
      Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{classType}, new MappingInvocationHandler());
      return (T) o;
   }
   @Override
   public Class<T> getObjectType() {
      return classType;
   }
}

定义一个注解,用于Import和指定包路径。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DatabaseImport.class)
public @interface MappingScan 
{
   String value() default "";
}
@MappingScan("org.springframework.test.dao")
@Component
public class UserTest {
   @Autowired
   UserMapping userMapping;
}

测试

@MappingScan("org.springframework.test.dao")
@Component
public class UserTest implements InitializingBean {
   @Autowired
   UserMapping userMapping;
   @Override
   public void afterPropertiesSet() throws Exception {
      for (int i = 0; i < 10; i++) {
         System.out.println(userMapping.count("a"));
      }
   }
}

还可以使用InstantiationAwareBeanPostProcessor。

@Component
public class MappingCreateInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
   @Override
   public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
      if ("UserMapping".equals(beanName)) {
         return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{beanClass}, new MappingInvocationHandler());
      }
      return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
   }
}

- END -


原文始发于微信公众号(十四个字节):深入理解Spring(三)、容器扩展