博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入Spring:自定义注解加载和使用
阅读量:5978 次
发布时间:2019-06-20

本文共 9727 字,大约阅读时间需要 32 分钟。

转自:https://blog.csdn.net/z69183787/article/details/53784845

前言

在工作中经常使用Spring的相关框架,免不了去看一下Spring的实现方法,了解一下Spring内部的处理逻辑。特别是开发Web应用时,我们会频繁的定义*@Controller*,*@Service*等JavaBean组件,通过注解,Spring自动扫描加载了这些组件,并提供相关的服务。

Spring是如何读取注解信息,并注入到bean容器中的,本文就是通过嵌入Spring的Bean加载,来描述Spring的实现方法。完整的例子都在上了。

自定义注解

先看一个最简单的例子,在使用SpringWeb应用中的过程中,大家免不了会使用*@Controller*,*@Service*,*@Repository*等注解来定义JavaBean。那么怎么自己定义一个注解,Spring可以自动加载呢。所以就有了第一个例子。

1 @Target({ ElementType.TYPE })2 @Retention(RetentionPolicy.RUNTIME)3 @Documented4 @Component5 public @interface MyComponent {6     String value() default "";7 }
1 @Configuration 2 public class ComponentAnnotationTest { 3   public static void main(String[] args) { 4     AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); 5 annotationConfigApplicationContext.register(ComponentAnnotationTest.class); 6     annotationConfigApplicationContext.refresh(); 7     InjectClass injectClass = annotationConfigApplicationContext.getBean(InjectClass.class); 8         injectClass.print(); 9   }10   @MyComponent11   public static class InjectClass {12     public void print() {13         System.out.println("hello world");14     }15   }16 }

 

运行这个例子,就会发现,* 注解的类,也被Spring加载进来了,而且可以当成普通的JavaBean正常的使用。查看Spring的源码会发现,Spring是使用ClassPathScanningCandidateComponentProvider扫描package,这个类有这样的注释

 

A component provider that scans the classpath from a base package. It then applies exclude and include filters to the resulting classes to find candidates.

这个类的 registerDefaultFilters 方法有这样几行代码

1 protected void registerDefaultFilters() {    2    this.includeFilters.add(new AnnotationTypeFilter(Component.class)); 3    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); 4    try {     5       this.includeFilters.add(new AnnotationTypeFilter(((Class
) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); 6 logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); 7 } catch (ClassNotFoundException ex) { 8 // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. 9 } 10 try { 11 this.includeFilters.add(new AnnotationTypeFilter(((Class
) ClassUtils.forName("javax.inject.Named", cl)), false)); 12 logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); 13 } 14 catch (ClassNotFoundException ex) { 15 // JSR-330 API not available - simply skip. 16 }17 }

这里就会发现Spring在扫描类信息的使用只会判断被*@Component*注解的类,所以任何自定义的注解只要带上*@Component*(当然还要有String value() default "";的方法,因为Spring的Bean都是有beanName唯一标示的),都可以被Spring扫描到,并注入容器内。

定制功能

但上面的方法太局限了,没办法定制,而且也没有实际的意义。如何用特殊的注解来实现定制的功能呢,一般有两种方式:

还是用上面的方法,在注入Spring的容器后,再取出来做自己定制的功能,Spring-MVC就是使用这样的方法。AbstractDetectingUrlHandlerMapping 中的 detectHandlers方法,这个方法取出了所有的bean,然后循环查找带有Controller的bean,并提取其中的RequestMapping信息

1 protected void detectHandlers() throws BeansException { 2     if (logger.isDebugEnabled()) { 3         logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); 4     } 5     String[] beanNames = (this.detectHandlersInAncestorContexts ? 6             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : 7             getApplicationContext().getBeanNamesForType(Object.class)); 8  9     // Take any bean name that we can determine URLs for.10     for (String beanName : beanNames) {11         String[] urls = determineUrlsForHandler(beanName);12         if (!ObjectUtils.isEmpty(urls)) {13             // URL paths found: Let's consider it a handler.14             registerHandler(urls, beanName);15         }16         else {17             if (logger.isDebugEnabled()) {18                 logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");19             }20         }21     }22 }

2.不依赖*@Component*,自定义扫描。所以就有了第二个例子。

自定义扫描

结构比较复杂,可以参考完整的,这里是关键的几个类

1.还是定义一个注解,只不过不再需要*@Component*了

1 @Target({ ElementType.TYPE })2 @Retention(RetentionPolicy.RUNTIME)3 @Documented4 public @interface CustomizeComponent {5  String value() default "";6 }

2.注解修饰的类

1 @CustomizeComponent2 public class ScanClass1 {3 public void print() {4     System.out.println("scanClass1");5 }6 }

3.BeanScannerConfigurer用于嵌入到Spring的加载过程的中,这里用到了BeanFactoryPostProcessorApplicationContextAware

Spring提供了一些的接口使程序可以嵌入Spring的加载过程。这个类中的继承ApplicationContextAware接口,Spring会读取ApplicationContextAware类型的的JavaBean,并调用setApplicationContext(ApplicationContext applicationContext)传入Spring的applicationContext
同样继承BeanFactoryPostProcessor接口,Spring会在BeanFactory的相关处理完成后调用postProcessBeanFactory方法,进行定制的功能。

1 @Component 2 public static class BeanScannerConfigurer implements  BeanFactoryPostProcessor, ApplicationContextAware { 3 private ApplicationContext applicationContext; 4  5 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 6   this.applicationContext = applicationContext; 7 } 8 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 9   Scanner scanner = new Scanner((BeanDefinitionRegistry) beanFactory);10   scanner.setResourceLoader(this.applicationContext);11   scanner.scan("org.wcong.test.spring.scan");12 }13   }

4.Scanner继承的ClassPathBeanDefinitionScanner是Spring内置的Bean定义的扫描器。

includeFilter里定义了类的过滤器,newAnnotationTypeFilter(CustomizeComponent.class)表示只取被CustomizeComponent修饰的类。
doScan里扫面了包底下的读取道德BeanDefinitionHolder,自定义GenericBeanDefinition相关功能

1 public final static class Scanner extends ClassPathBeanDefinitionScanner { 2   public Scanner(BeanDefinitionRegistry registry) { 3       super(registry); 4   } 5   public void registerDefaultFilters() { 6       this.addIncludeFilter(new AnnotationTypeFilter(CustomizeComponent.class)); 7   } 8   public Set
doScan(String... basePackages) { 9 Set
beanDefinitions = super.doScan(basePackages);10 for (BeanDefinitionHolder holder : beanDefinitions) {11 GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();12 definition.getPropertyValues().add("innerClassName", definition.getBeanClassName());13 definition.setBeanClass(FactoryBeanTest.class);14 }15 return beanDefinitions;16 }17 public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {18 return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata()19 .hasAnnotation(CustomizeComponent.class.getName());20 }21 }

5.FactoryBean是Spring中比较重要的一个类。它的描述如下

Interface to be implemented by objects used within a BeanFactory which are themselves factories. If a bean implements this interface, it is used as a factory for an object to expose, not directly as a bean* instance that will be exposed itself 普通的JavaBean是直接使用类的实例,但是如果一个Bean继承了这个借口,就可以通过getObject()方法来自定义实例的内容,在FactoryBeanTest的getObject()就通过代理了原始类的方法,自定义类的方法。
1 public static class FactoryBeanTest
implements InitializingBean, FactoryBean
{ 2 private String innerClassName; 3 public void setInnerClassName(String innerClassName) { 4 this.innerClassName = innerClassName; 5 } 6 public T getObject() throws Exception { 7 Class innerClass = Class.forName(innerClassName); 8 if (innerClass.isInterface()) { 9 return (T) InterfaceProxy.newInstance(innerClass);10 } else {11 Enhancer enhancer = new Enhancer();12 enhancer.setSuperclass(innerClass);13 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);14 enhancer.setCallback(new MethodInterceptorImpl());15 return (T) enhancer.create();16 }17 }18 public Class
getObjectType() {19 try {20 return Class.forName(innerClassName);21 } catch (ClassNotFoundException e) {22 e.printStackTrace();23 }24 return null;25 }26 public boolean isSingleton() {27 return true;28 }29 public void afterPropertiesSet() throws Exception {30 }31 }32 public static class InterfaceProxy implements InvocationHandler {33 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {34 System.out.println("ObjectProxy execute:" + method.getName());35 return method.invoke(proxy, args);36 }37 public static
T newInstance(Class
innerInterface) {38 ClassLoader classLoader = innerInterface.getClassLoader();39 Class[] interfaces = new Class[] { innerInterface };40 InterfaceProxy proxy = new InterfaceProxy();41 return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);42 }43 }44 public static class MethodInterceptorImpl implements MethodInterceptor {45 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {46 System.out.println("MethodInterceptorImpl:" + method.getName());47 return methodProxy.invokeSuper(o, objects);48 }49 }

6.main函数

1 @Configuration 2 public class CustomizeScanTest { 3 public static void main(String[] args) { 4     AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();                 5     annotationConfigApplicationContext.register(CustomizeScanTest.class); 6     annotationConfigApplicationContext.refresh(); 7     ScanClass1 injectClass = annotationConfigApplicationContext.getBean(ScanClass1.class); 8     injectClass.print(); 9 }10  }

至此一个完整的例子就完成了,这里主要用到了BeanFactoryPostProcessorApplicationContextAwareFactoryBean等Spring内置的接口,来嵌入Spring的加载和使用过程,这样就实现了自定义注解,和自定义代理了。

分类:

 

你可能感兴趣的文章
ARTS训练第三周
查看>>
基本操作题
查看>>
unicone 字体 规范
查看>>
浅显易懂的跨域理解
查看>>
Leetcode PHP题解--D25 500. Keyboard Row
查看>>
简化路径
查看>>
让Redis突破内存大小的限制
查看>>
安全研究人受够!再公布WordPress 3大外挂漏洞
查看>>
12月21日云栖精选夜读:阿里云总裁胡晓明:AI泡沫过后,下一站是“产业AI”...
查看>>
除了BAT,国内还有哪些值得关注的人工智能公司?
查看>>
一出好戏不止是部电影,它也正接近你的生活。
查看>>
1月23日云栖精选夜读:一张图解读阿里云数据管理DMS企业版
查看>>
spring cloud构建互联网分布式微服务云平台-docker部署spring cloud项目
查看>>
Deepmind顺练了人工智能14天成为星海2最强玩家
查看>>
Angular 表单验证类库 ngx-validator 1.0 正式发布
查看>>
刨根问底——Handler
查看>>
H5活动刮刮卡功能的实现与注意事项
查看>>
搞定Go单元测试(三)—— 断言(testify)
查看>>
web前端—面试2
查看>>
设计模式之 - 简单工厂模式
查看>>