代码已经发布在GIT 源代码
如何让我们自定义的注解可以像 @Service @Controller @Componet等被Spring加载到应用上下文?
方法1 自定义注解添加能被Spring识别的注解比如@Component
下面的例子增加了@Component于是自定义注解@SService 可以通过它来获取上下文中被@SService注解的bean。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Inherited
@Component
public @interface SService {
}
但是有个问题Component没有标注@Inherited因此 这个@SService注解标注的类的子类其实是获取不到Componet的也就是子类不会被Spring识别。不过这也不是问题所在。我们更需要的是自定义的注解不用增加其他的条件就被Spring识别。
方法2 BeanFactoryPostProcessor
public class SonicScanner extends ClassPathBeanDefinitionScanner {
public SonicScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
public void registerDefaultFilters() {
this.addIncludeFilter(new AnnotationTypeFilter(SService.class));
}
@Override
public int scan(String... basePackages) {
return super.scan(basePackages);
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
LogCore.BASE.info("all mine is{}", beanDefinitions);
return beanDefinitions;
}
@Override
public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return super.isCandidateComponent(beanDefinition)
&& beanDefinition.getMetadata().hasAnnotation(SService.class.getName());
}
}
@Component
public class SonicBeanFactoryPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SonicScanner scanner = new SonicScanner((BeanDefinitionRegistry) beanFactory);
scanner.setResourceLoader(this.applicationContext);
scanner.scan("");// org.sonic
}
}
RPC框架 加载所有的自定义@SService注解的bean并发布到zookeeper
```
@Configuration
public class ProviderConfig {
@Bean
ProviderHandler getProviderHandler(
@Value("${provider.target}") String target,
@Value("${provider.port}") int port) {
LogCore.BASE.info("target=={}",target,port);
return new ProviderHandler(target, port).start();
}
@Bean
@Autowired
ProviderProxyFactory getProviderProxyFactory(ProviderHandler providerConfig, ApplicationContext ct){
ProviderProxyFactory pf = new ProviderProxyFactory(providerConfig);
Map<String,Object> map = ct.getBeansWithAnnotation(SService.class);
map.values().forEach(pf::register);
LogCore.BASE.info("要发布的SService服务列表 {}",map);
return pf;
}
}
```
public void register(Object obj) {
Class<?> interFaceClazz = obj.getClass().getInterfaces()[0];
providers.put(interFaceClazz, obj);
providerHandler.register(interFaceClazz);
}
public void register(Class<?> clazz) {
String path = RpcUtil.getZkRootPath(clazz);
String childrenPath = path + "/node";
zClient.createPersistent(path);
zClient.createEphemeral(childrenPath, getNodeInfo());
}
public String getNodeInfo() {
try {
String info = "http://" + Inet4Address.getLocalHost().getHostAddress() + ":" + port;
LogCore.BASE.info("info={}", info);
return info;
} catch (UnknownHostException e) {
LogCore.RPC.error("getNodeInfo", e);
return null;
}
}
最后
我们已经将自己的服务通过一个不依赖任何第三方框架的自定义注解,成功加载到Spring中。接下来我们下一节将自定义Spring的IOC。改变接口引用指向实例为代理实例。