技术标签: spring spring boot java
简单的说就是扫描依赖starter包下含有MyMapper注解的interface,在即将创建Bean实例前,修改BeanDefinition的BeanClass为FactoryBean接口。Bean类实现FactoryBean接口,spring-boot使用FactoryBean接口定义的工厂方法创建Bean,而不是Bean的构造方法。FactoryBean接口的实现类使用Proxy创建代理对象来替换实际的interface Bean,Proxy的handler通过拦截Method,获取Method上标记的注解来动态实现interface声明的方法。
创建resource/META-INF/spring.factories,配置AutoConfiguration。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mystarter.autoconfigure.MyAutoConfiguration
spring-boot会读取这个文件的内容,读取rg.springframework.boot.autoconfigure.EnableAutoConfiguration的值来创建AutoConfiguration并注入到容器。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface MyMapper {
}
@Target({
ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {
};
@AliasFor("value")
String[] path() default {
};
RequestMethod[] method() default {
};
String[] params() default {
};
}
@MyMapper
public interface DemoMapper {
@MyRequestMapping(name = "my", path = "hello", method = RequestMethod.GET, params = {
"world", "java"})
String helloMapper();
}
public class MyAutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
return;
}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperScannerConfigurer.class);
builder.addPropertyValue("annotationClass", MyMapper.class);
builder.addPropertyValue("factoryBeanClass", MyMapperFactoryBean.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
registry.registerBeanDefinition(MyMapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
List packages = AutoConfigurationPackages.get(this.beanFactory);
这里是为了获取导入自定义sping-boot-starter的工厂包名。不是自定义sping-boot-starter的包名
public class MyMapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private String beanName;
private ApplicationContext applicationContext;
private String basePackage;
private Class<? extends Annotation> annotationClass;
private Class<? extends FactoryBean<?>> factoryBeanClass;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
public void setFactoryBeanClass(Class<? extends FactoryBean<?>> factoryBeanClass) {
this.factoryBeanClass = factoryBeanClass;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry);
scanner.setAnnotationClass(annotationClass);
scanner.setFactoryBeanClass(factoryBeanClass);
scanner.registerFilters();
scanner.scan(basePackage);
}
}
BeanDefinitionRegistryPostProcessor接口 在注入容器的Bean的BeanDefinition创建好后还没有创建实例注入到容器前回调,在postProcessBeanDefinitionRegistry方法进一步修改BeanDefinition。这里实现BeanDefinitionRegistryPostProcessor是为了防止扫描到的interface已经被注入到容器,这时已经无法修改BeanDefinition。
public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
private Class<? extends Annotation> annotationClass;
private Class<? extends FactoryBean<?>> factoryBeanClass;
public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
public void setFactoryBeanClass(Class<? extends FactoryBean<?>> factoryBeanClass) {
this.factoryBeanClass = factoryBeanClass;
}
public void registerFilters() {
addIncludeFilter(new AnnotationTypeFilter(annotationClass));
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
for (BeanDefinitionHolder holder : beanDefinitions) {
AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
definition.setBeanClass(factoryBeanClass);
// 从构造方法传入参数
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
}
return beanDefinitions;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isInterface() && metadata.isIndependent();
}
}
AnnotationTypeFilter扫描MyMapper注解的类和接口,isCandidateComponent(AnnotatedBeanDefinition beanDefinition)回调,再次判断beanDefinition是否需要实例化为组件并注入到容器。这里只包含interface类型且标记MyMapper注解。
public class MyMapperFactoryBean<T> implements FactoryBean<T> {
private Class<T> mapperInterface;
MyMapperFactoryBean() {
}
MyMapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{
mapperInterface}, new MyMapperImpl<T>());
}
@Override
public Class<?> getObjectType() {
return this.mapperInterface;
}
}
FactoryBean接口标记这个Bean类是具有工厂方法的Bean类,直接通过工厂方法创建Bean,不通过构造方法创建。这里的Bean类就是之前扫描的interface
public class MyMapperImpl<T> implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
if (method.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping mapping = method.getAnnotation(MyRequestMapping.class);
String pathString = "";
for (String path : mapping.path()) {
pathString += (path + " ");
}
String methodString = "";
for (RequestMethod m : mapping.method()) {
methodString += (m.name() + " ");
}
String paramsString = "";
for (String param : mapping.params()) {
paramsString += (param + " ");
}
return String.format("name=%s path=%s method=%s params=%s", mapping.name(), pathString, methodString, paramsString);
}
return "hello world";
}
}
}
代理handler根据代理的interface类的方法注解,动态实现interface的方法。
@EnableAutoConfiguration
@SpringBootTest(classes = MyAutoConfiguration.class)
class MystarterApplicationTests {
@Resource
DemoMapper mapper;
@Test
void contextLoads() {
System.out.println(mapper.helloMapper());
}
}
输出
name=my path=hello method=GET params=world java
文章浏览阅读3.3k次,点赞4次,收藏9次。首先,我们先来看一下效果_scratch母亲节作品
文章浏览阅读686次。package mainimport ( "awesomeProject1/src/github.com/hyperledger/fabric/core/chaincode/shim" "awesomeProject1/src/github.com/hyperledger/fabric/protos/peer" "bytes" "encoding/json" "..._开源投票系统 github
文章浏览阅读3.9k次。需要再Terminal里面输入tensorboard --logdir logs才能打开Tensorboard# tensorboard --logdir=logs --port=6007 是将端口号改为了6007#一般用到一下几个指令:# 一、打开图片显示图片# writer.add_images("input",imgs,step) 第一个是标签,第二个是图像,注意图像必须是numpy或者tentor类型的,而且必须是通道数在前面,比如RGB三通道的就要是3*w*h#如果通道数在后面,则._tensorboard代码
文章浏览阅读1.2k次。reverse的音标英 [rɪˈvɜːs]美 [rɪˈvɜːrs]reverse的用法v. 颠倒;彻底转变;使完全相反;撤销,废除(决定、法律等);使反转;使次序颠倒n. 相反的情况(或事物);后面;背面;反面;倒挡adj. 相反的;反面的;反向的;背面的;后面的第三人称单数: reverses 复数: reverses 现在分词: reversing 过去式: reversed 过去分词: re..._reverseorder
文章浏览阅读1w次。import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class Demo {public static void main(String[] args) throws ParseException {//获..._创建一个指定时间用什么语句
文章浏览阅读1.6k次。Sublime Text 2和3的对比相比于2,Sublime Text 3就秒启动一项,就压倒性地胜利了。因此在之后的叙述中都以Sublime Text 3为主角。并且3一直在不断的完善更新,具体的差异可参看Sublime Blog.简单的说:ST3支持在项目目录里面寻找变量 提供了对标签页更好地支持(更多的命令和快捷键) 加快了程序运行的速度 更新了API,使用Pytho..._folder_exclude_patterns
文章浏览阅读60次。go-mysqlA pure go library to handle MySQL network protocol and replication.ReplicationReplication package handles MySQL replication protocol like python-mysql-replication.You can use it as a MySQL sla..._replication.newbinlogsyncer
文章浏览阅读236次。C/C++头文件一览 C、传统 C++#include //设定插入点#include //字符处理#include //定义错误码#include //浮点数处理#include //文件输入/输出#include //参数化输入/输出#include
文章浏览阅读307次。在PHP中读取二进制文件2012-10-30[1715]technologyphp次阅读很多时候,数据并不是用文本的方式保存的,这就需要将二进制数据读取出来,还原成我们需要的格式。PHP在二进制处理方面也提供了强大的支持。任务下面以读取并分析一个PNG图像的文件头为例,讲解如何使用PHP读取和分析二进制文件。涉及函数PNG格式简介为了完成任务,下面简单介绍一下PNG文件格式。PNG是一种无损压缩的..._php 查找字符串中的二进制
文章浏览阅读1.4w次。kettle报错couldn't convert string [1970-01-01 00:00:00] to a date using format [yyyy/MM/dd HH:mm:ss.SSS]1.报错如下2019/01/08 12:04:18 - 替换NULL值.0 - ERROR (version 8.1.0.0-365, build 8.1.0.0-365 from 2018-..._kettle导出date报错
文章浏览阅读922次。串口通信(Serial Communication),是指外设和计算机间通过数据信号线、地线等按位进行传输数据的一种通信方式,属于串行通信方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。(1)接口标准串口通信的接口标准有很多,有 RS-232C、RS-232、RS-422A、RS-485 等。常用的就是 RS-232 和 RS-485。RS-232 其实是 R..._多串口电脑有什么用途
文章浏览阅读2.5w次,点赞7次,收藏18次。转载请注明出处:http://blog.csdn.net/forevercbb/article/details/51037833 由于Google不再支持Eclipse之后,转向Google的亲儿子——AndroidStudio是必然的,但是AndroidStudio的配置以及使用都比Eclipse复杂,不熟悉的情况下经常会遇到一些莫名其妙的问题,导致应用无法正常编译。代码出现的bug可以根据错_run configuration app is not supported in the current project. cannot obtain