总结:
此处对于BeanPostProcessor接口的调用应该属于高级应用了,该思路常用来解决扩展或集成Spring框架,其核心的思路可以分为以下几步:
1、自定义实现类路径扫描类,决定哪些类应该被注入进Spring容器。
2、采用Java动态代理来动态实现对于声明接口类的注入。
3、实现BeanDefinitionRegistryPostProcessor,在Spring初始化初期将需要扫描导入Spring容器的类进行注入。
4、通过代码动态创建
代码动态创建
我们通过getBean来获得对象,但这些对象都是事先定义好的,我们有时候要在程序中动态的加入对象.因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除。
(2)动态注入实现代码;
(3)多次注入同一个bean的情况;
(4)动态删除;
接下来我们看下具体的内容:
(1)动态注入bean思路;
在具体进行代码实现的时候,我们要知道,Spring管理bean的对象是BeanFactory,具体的是DefaultListableBeanFactory,在这个类当中有一个注入bean的方法:registerBeanDefinition,在调用registerBeanDefinition方法时,需要BeanDefinition参数,那么这个参数怎么获取呢?Spring提供了BeanDefinitionBuilder可以构建一个BeanDefinition,那么我们的问题就是如何获取BeanFactory了,这个就很简单了,只要获取到ApplicationContext对象即可获取到BeanFacory了。
(2)动态注入实现代码;
综上所述,如果我们要编写一个简单里的例子的话,那么分以个几个步骤进行编码即可进行动态注入了:
<2>. 通过ApplicationContext获取到BeanFacotory;
<3>. 通过BeanDefinitionBuilder构建BeanDefiniton;
<4>. 调用beanFactory的registerBeanDefinition注入beanDefinition;
<5>. 使用ApplicationContext.getBean获取bean进行测试;
很明显我们需要先定义个类进行测试,比如TestService代码如下:
package com.dxz.test; public class TestService { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void print() { System.out.println("动态载入bean,name=" + name); } }
注意:这里没有使用@Service和配置文件进行注入TestService。
那么下面我们的目标就是动态注入TestService了,根据以上的分析,我们进行编码,具体代码如下:
package com.dxz.test; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(Application.class, args); // 获取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx .getAutowireCapableBeanFactory(); // 创建bean信息. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name", "张三"); // 动态注册bean. defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition()); // 获取动态注册的bean. TestService testService = ctx.getBean(TestService.class); testService.print(); } }
执行代码我们会在控制台看到如下打印信息:
动态载入bean,name=张三
到这里,就证明我们的代码很成功了。
(3)多次注入同一个bean的情况;
多次注入同一个bean的,如果beanName不一样的话,那么会产生两个Bean;如果beanName一样的话,后面注入的会覆盖前面的。
第一种情况:beanName一样的代码:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name","李四"); defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition());
运行看控制台:
动态载入bean,name=李四
第二种情况:beanName不一样的代码:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name","李四"); defaultListableBeanFactory.registerBeanDefinition("testService1",beanDefinitionBuilder.getBeanDefinition());
此时如果没有更改别的代码直接运行的话,是会报如下错误的:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.kfit.demo.service.TestService] is defined: expected single matching bean but found 2: testService1,testService
大体意思就是在getBean的时候,找到了两个bean,这时候就不知道要获取哪个了,所以在获取的时候,我们就要指定我们是要获取的testService还是testService1,只需要修改一句代码:
将代码:
TestService testService =ctx.getBean(TestService.class); 修改为: TestService testService =ctx.getBean("testService");
(4)动态删除;
相对于动态注入,动态删除就很简单了,直接奉上代码:
//删除bean. defaultListableBeanFactory.removeBeanDefinition("testService");
实现BeanDefinitionRegistryPostProcessor,在Spring初始化初期将需要扫描导入Spring容器的类进行注入
"对于Spring框架,现实公司使用的非常广泛,但是由于业务的复杂程度不同,了解到很多小伙伴们利用Spring开发仅仅是利用了Spring的IOC,即使是AOP也很少用,但是目前的Spring是一个大家族,形成了一个很大的生态,覆盖了我们平时开发的方方面面,抛开特殊的苛刻要求之外,Spring的生态其实已经很全面了,所以在此开个系列来研究下Spring提供给我们的一些平时不太却又很实用的内容。"
上一篇我们分析了BeanPostProcessor的基本使用,接下来我们分析下如何使用该类实现动态的接口注入,示例说明:在BeetlSQL框架中,在使用自动扫描注入时,我们通常只需要配置上要扫描的包路径,然后在该路径下声明对应的Dao接口类,这些接口类都默认继承BaseMapper接口类,然后我们在使用这些Dao类的时候,直接根据类型注入(@Autowired)即可使用,这个其实和Mybatis的那一套相似,也和Spring自身的Spring-data框架也类似。这个经常用于框架的开发,那么我就该部分的实现做相应的解释,这三个框架具体实现可能有差距,感兴趣的小伙伴自行去查看源码,我会以一个很简单的例子来讲解大概的实现逻辑。
问题描述:
继承Spring框架,实现声明某个自定义接口(UserMapper),改接口继承通用接口BaseMapper,(通用接口BaseMapper有默认的实现类),实现通过类型注入UserMapper类,然后通过Spring框架的上下文类(ApplicationContext实现类)的getBean()方法拿到UserMapper类来调用内部提供的方法。
1、声明BaseMapper接口类
package com.dxz.test; public interface BaseMapper { public void add(String value); public void remove(String key); } public class CustomBaseMapper implements BaseMapper { private final Logger logger = Logger.getLogger(this.getClass().getName()); private ListdataList = new CopyOnWriteArrayList<>(); @Override public void add(String value) { logger.info("添加数据:" + value); dataList.add(value); } @Override public void remove(String key) { if (dataList.isEmpty()) throw new IllegalArgumentException("Can't remove because the list is Empty!"); } }
接下来是继承Spring的核心代码
3、首先我们要先定义一个扫描某路径下的类,该类继承ClassPathBeanDefinitionScanner,自定义扫描类:DefaultClassPathScanner
package com.dxz.test; import java.io.IOException; import java.util.Arrays; import java.util.Set; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; public class DefaultClassPathScanner extends ClassPathBeanDefinitionScanner { private final String DEFAULT_MAPPER_SUFFIX = "Mapper"; public DefaultClassPathScanner(BeanDefinitionRegistry registry) { super(registry, false); } private String mapperManagerFactoryBean; /** * 扫描包下的类-完成自定义的Bean定义类 * * @param basePackages * @return */ @Override protected SetdoScan(String... basePackages) { Set beanDefinitions = super.doScan(basePackages); // 如果指定的基础包路径中不存在任何类对象,则提示 if (beanDefinitions.isEmpty()) { logger.warn("系统没有在 '" + Arrays.toString(basePackages) + "' 包中找到任何Mapper,请检查配置"); } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } /** * 注册过滤器-保证正确的类被扫描注入 */ protected void registerFilters() { addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); // TODO 这里设置包含条件-此处是个扩展点,可以根据自定义的类后缀过滤出需要的类 return className.endsWith(DEFAULT_MAPPER_SUFFIX); } }); addExcludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); } }); } /** * 重写父类的判断是否能够实例化的组件-该方法是在确认是否真的是isCandidateComponent 原方法解释: * 确定给定的bean定义是否有资格成为候选人。 默认实现检查类是否不是接口,也不依赖于封闭类。 以在子类中重写。 * * @param beanDefinition * @return */ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { // 原方法这里是判断是否为顶级类和是否是依赖类(即接口会被排除掉-由于我们需要将接口加进来,所以需要覆盖该方法) return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); } /** * 扩展方法-对扫描到的含有BeetlSqlFactoryBean的Bean描述信息进行遍历 * * @param beanDefinitions */ void processBeanDefinitions(Set beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String mapperClassName = definition.getBeanClassName(); // 必须在这里加入泛型限定,要不然在spring下会有循环引用的问题 definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName); // 依赖注入 definition.getPropertyValues().add("mapperInterface", mapperClassName); // 根据工厂的名称创建出默认的BaseMapper实现 definition.getPropertyValues().add("mapperManagerFactoryBean", new RuntimeBeanReference(this.mapperManagerFactoryBean)); definition.setBeanClass(BaseMapperFactoryBean.class); // 设置Mapper按照接口组装 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); logger.info("已开启自动按照类型注入 '" + holder.getBeanName() + "'."); } } public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } }
4、核心的接口实现类:BaseMapperFactoryBean
package com.dxz.test; import java.lang.reflect.Proxy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; public class BaseMapperFactoryBeanimplements FactoryBean , InitializingBean, ApplicationListener , ApplicationContextAware { /** * 要注入的接口类定义 */ private Class mapperInterface; /** * Spring上下文 */ private ApplicationContext applicationContext; // 也因该走工厂方法注入得来 private BaseMapper mapperManagerFactoryBean; public BaseMapperFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; } @Override public T getObject() throws Exception { // 采用动态代理生成接口实现类,核心实现 return (T) Proxy.newProxyInstance(applicationContext.getClassLoader(), new Class[] { mapperInterface }, new MapperJavaProxy(mapperManagerFactoryBean, mapperInterface)); } @Override public Class> getObjectType() { return this.mapperInterface; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { // TODO 判断属性的注入是否正确-如mapperInterface判空 if (null == mapperInterface) throw new IllegalArgumentException("Mapper Interface Can't Be Null!!"); } /** * Handle an application event. * * @param event * the event to respond to */ @Override public void onApplicationEvent(ApplicationEvent event) { // TODO 可依据事件进行扩展 } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void setMapperInterface(Class mapperInterface) { this.mapperInterface = mapperInterface; } public void setMapperManagerFactoryBean(BaseMapper mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } }
5、定义默认的BaseMapper的FactoryBean-MapperManagerFactoryBean
package com.dxz.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MapperJavaProxy implements InvocationHandler { private BaseMapper baseMapper; private Class> interfaceClass; public MapperJavaProxy(BaseMapper baseMapper, Class> interfaceClass) { this.baseMapper = baseMapper; this.interfaceClass = interfaceClass; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!interfaceClass.isInterface()) { throw new IllegalArgumentException("mapperInterface is not interface."); } if (baseMapper == null) { baseMapper = new CustomBaseMapper(); } return method.invoke(baseMapper, args); } }
7、调用时的核心配置类:DefaultClassRegistryBeanFactory
package com.dxz.test; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; public class DefaultClassRegistryBeanFactory implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, BeanNameAware { private String scanPackage; private String beanName; private String mapperManagerFactoryBean; private ApplicationContext applicationContext; public String getScanPackage() { return scanPackage; } public void setScanPackage(String scanPackage) { this.scanPackage = scanPackage; } public String getMapperManagerFactoryBean() { return mapperManagerFactoryBean; } public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { if (StringUtils.isEmpty(this.scanPackage)) { throw new IllegalArgumentException("scanPackage can't be null"); } String basePackage2 = this.applicationContext.getEnvironment().resolvePlaceholders(this.scanPackage); String[] packages = StringUtils.tokenizeToStringArray(basePackage2, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); DefaultClassPathScanner defaultClassPathScanner = new DefaultClassPathScanner(beanDefinitionRegistry); defaultClassPathScanner.setMapperManagerFactoryBean(mapperManagerFactoryBean); defaultClassPathScanner.registerFilters(); defaultClassPathScanner.doScan(packages); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { this.beanName = name; } }
8、调用测试
8.1、假设你在包目录:colin.spring.basic.advanced.inject.dao下声明自定义的类UserMapper
public interface UserMapper extends BaseMapper { }
8.2、声明配置类:ClassRegistryBeanScannerConfig
package com.dxz.test; @Configuration public class ClassRegistryBeanScannerConfig { @Bean(name = "mapperManagerFactoryBean") public MapperManagerFactoryBean configMapperManagerFactoryBean() { MapperManagerFactoryBean mapperManagerFactoryBean = new MapperManagerFactoryBean(); return mapperManagerFactoryBean; } @Bean public DefaultClassRegistryBeanFactory configDefaultClassRegistryBeanFactory() { DefaultClassRegistryBeanFactory defaultClassRegistryBeanFactory = new DefaultClassRegistryBeanFactory(); defaultClassRegistryBeanFactory.setScanPackage("colin.spring.basic.advanced.inject.dao"); defaultClassRegistryBeanFactory.setMapperManagerFactoryBean("mapperManagerFactoryBean"); return defaultClassRegistryBeanFactory; } }
8.3、测试调用
package com.dxz.test; public class Snippet { public static void main(String[] args) { AnnotationConfigApplicationContext acApplicationCOntext = new AnnotationConfigApplicationContext("colin.spring.basic.advanced.inject"); UserMapper userMapper = acApplicationCOntext.getBean(UserMapper.class); userMapper.add("lalaldsf"); acApplicationCOntext.stop(); } }