前言
我们知道,在spring中,我们只需要加上注解@Component
,就可以自动注入到spring容器中,如果我们自定义注解,怎么让spring识别到,注入到容器中呢,下面我们来看看。
基础使用
自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomizeComponent {
}
开启自定义注解的注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomizeRegistrar.class) // 注意该注解,负责和spring建立关联
@Documented
public @interface EnableCustomizeComponent {String basePackages() default "";
}
自定义bean注册
public class CustomizeRegistrar implements ImportBeanDefinitionRegistrar {@SneakyThrows@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableCustomizeComponent.class.getName());// 获得要扫描的类String basePackages = (String) map.get("basePackages");if (!StringUtils.hasLength(basePackages)){ // basePackages为空的时候获取注解所在类的包路径basePackages = ClassUtil.getPackage(Class.forName(importingClassMetadata.getClassName()));}// 创建scanner,该类负责扫描包,并注册beanClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);// 添加我们的自定义注解,要注意这里是IncludeFilter,不是excludeFilter,别写错了scanner.addIncludeFilter(new AnnotationTypeFilter(CustomizeComponent.class));scanner.scan(basePackages);}
}
测试
@CustomizeComponent
public class Person {public String getInfo(){return "这是测试信息";}
}@Component
public class AppRunner implements ApplicationRunner {@Autowiredprivate Person person;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("===" + person.getInfo());}
}@SpringBootApplication
@EnableCustomizeComponent
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}
进阶使用
在使用mybatis、openFeign的时候我们是定义了接口,然后不需要实现也会帮助我们注入到容器中,然后可以相正常的bean一样使用,接下来我们看看是怎么实现的
开启自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(MyFeignRegistrar.class)
@Documented
public @interface EnableMyFeign {String basePackages() default "";
}
方法上的注解
我们只需要在接口的方法上添加该注解,就可以发起网络请求
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomizeGet {String url();}
注册器
public class MyFeignRegistrar implements ImportBeanDefinitionRegistrar {@SneakyThrows@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableMyFeign.class.getName());String basePackages = (String) map.get("basePackages");if (!StringUtils.hasLength(basePackages)){basePackages = ClassUtil.getPackage(Class.forName(importingClassMetadata.getClassName()));}FeignScanner scanner = new FeignScanner(registry);// 这里我们换了一种方式,定义的接口要继承Feign,而不需要添加注解scanner.addIncludeFilter(new AssignableTypeFilter(Feign.class){@Overrideprotected boolean matchClassName(String className) {return false;}});scanner.scan(basePackages);}// 继承ClassPathBeanDefinitionScanner,自定义自己的扫描器static class FeignScanner extends ClassPathBeanDefinitionScanner {public FeignScanner(BeanDefinitionRegistry registry) {super(registry);}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {GenericBeanDefinition beanDefinition =(GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();String beanClassName = beanDefinition.getBeanClassName();beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);// 拿到beanDefinition,修改里面的beanClass为FeignFactoryBeanbeanDefinition.setBeanClass(FeignFactoryBean.class);}return beanDefinitionHolders;}// 注意,扫描的时候里面有个条件会判断对应的class是否非接口类型,这里要重写,我们的要求是接口类型,后边我们会通过FactoryBean创建动态代理@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();}}
}// 空实现,担当一个标识的作用
public interface Feign {
}
工厂Bean
public class FeignFactoryBean<T extends Feign> implements FactoryBean<T> {private final Class<T> interfaceClass;public FeignFactoryBean(Class<T> interfaceClass) {this.interfaceClass = interfaceClass;}@Overridepublic T getObject() throws Exception {// 使用动态代理来创建Beanreturn (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new Handler());}@Overridepublic Class<?> getObjectType() {return interfaceClass;}static class Handler<T> implements InvocationHandler {private final RestTemplate restTemplate = new RestTemplate();@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getDeclaringClass().equals(Object.class)) {return method.invoke(this, args);}// 方法上有CustomizeGet注解的发起网络请求CustomizeGet customizeGet = method.getAnnotation(CustomizeGet.class);if (customizeGet != null) {return restTemplate.getForObject(customizeGet.url(), String.class);}return null;}}
}
测试
public interface Request extends Feign {@CustomizeGet(url = "https://www.baidu.com")String requestBaiDu();}@Component
public class AppRunner implements ApplicationRunner {@Autowiredprivate Request request;@Overridepublic void run(ApplicationArguments args) throws Exception {String str = request.requestBaiDu();System.out.println(str);}
}@SpringBootApplication
@EnableMyFeign
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}