spring自定义扫描实现

由于篇幅很长,所以将思路和实现分开了,本篇幅是自定义扫描的实现。

自定义扫描的思路及原理在我的另一篇文章https://blog.csdn.net/weixin_43795033/article/details/109516474

业务需求:

自定义一个注解@MyMapping使当spring启动的时候自动扫描到环境中所有加了@MyMapping注解的Controller的方法

的url路径,并注册一个拦截器或者过滤器去拦截/忽略这些动态匹配到的路由。

首先定义注解@MyMapping

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyMapping {String value() default "";String comment() default "";
}

接着定义一个扫描注解@MyMapperScan

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({MyUrlImportBeanDefinitionRegistrar.class})
public @interface MyMapperScan {@AliasFor("basePackage")String value() default "";@AliasFor("value")String basePackage() default "";
}

然后在启动类或者一个有@Configuration注解的类上面加上@MyMapperScan

//路径自己写
@MyMapperScan("xxx.**.xxx")
@SpringBootApplication
public class AppConfig {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(AppConfig.class, args);//        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//        context.register(AppConfig.class);
//        context.refresh();}
}

然后定义拦截器MyInterceptor

public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("拦截器执行!");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("拦截器结束!");}
}

然后定义MyUrlClassPathBeanDefinitionScanner自定义扫描器

public class MyUrlClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {private Class annotationClass;private Set URLS = new HashSet<>();static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";@Nullableprivate ResourcePatternResolver resourcePatternResolver;private String resourcePattern = DEFAULT_RESOURCE_PATTERN;public MyUrlClassPathBeanDefinitionScanner(boolean useDefaultFilters, Class annotationClass) {super(useDefaultFilters);this.annotationClass = annotationClass;registerFilters();}public MyUrlClassPathBeanDefinitionScanner(boolean useDefaultFilters) {super(useDefaultFilters);registerFilters();}private void registerFilters() {//放行指定annotationClass的类型if (this.annotationClass != null) {this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));} else {//放行所有的类型this.addIncludeFilter(new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {return true;}});}
//        this.addExcludeFilter(new TypeFilter() {
//            @Override
//            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//                return false;
//            }
//        });}private ResourcePatternResolver getResourcePatternResolver() {if (this.resourcePatternResolver == null) {this.resourcePatternResolver = new PathMatchingResourcePatternResolver();}return this.resourcePatternResolver;}public void doScan(String... basePackages) {for (String basePackage : basePackages) {scanCandidateComponents(basePackage);}}private void scanCandidateComponents(String basePackage) {try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {createUrls(metadataReader.getAnnotationMetadata().getClassName());}} catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}}} catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}}private void createUrls(String beanClassName) {try {Class aClass = Class.forName(beanClassName);RequestMapping requestMapping = (RequestMapping) aClass.getAnnotation(RequestMapping.class);GetMapping getMapping;PostMapping postMapping;String[] path_parents = requestMapping.value();String[] paths;Method[] methods = aClass.getMethods();for (Method method : methods) {MyUrl myUrl = method.getAnnotation(MyMapping.class);if (myUrl != null) {requestMapping = method.getAnnotation(RequestMapping.class);if (requestMapping == null) {getMapping = method.getAnnotation(GetMapping.class);if (getMapping == null) {postMapping = method.getAnnotation(PostMapping.class);if (postMapping == null) {paths = null;} else {paths = postMapping.value();}} else {paths = getMapping.value();}} else {paths = requestMapping.value();}if (paths == null) {continue;} else {for (String path_parent : path_parents) {for (String path : paths) {URLS.add("/" + path_parent + "/" + path);}}}}}} catch (ClassNotFoundException e) {e.printStackTrace();}}public Set getURLS() {return URLS;}
}

然后定义一个配置类去作为注册我们拦截器的包装类

public class MyInterceptorConfig implements WebMvcConfigurer {private final HandlerInterceptor interceptor;private final List urls;public MyInterceptorConfig(HandlerInterceptor interceptor, List urls) {this.interceptor = interceptor;this.urls = urls;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(interceptor).addPathPatterns(urls);}
}

接着定义一个执行扫描的切入点MyUrlImportBeanDefinitionRegistrar

public class MyUrlImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {private ResourceLoader resourceLoader;private Environment environment;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}/*此方法会在spring自定义扫描执行之后执行,这个时候beanDefinitionMap已经有扫描到的beanDefinition对象了*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));MyUrlClassPathBeanDefinitionScanner scanner = new MyUrlClassPathBeanDefinitionScanner(false, Controller.class);if (this.resourceLoader != null) {scanner.setResourceLoader(resourceLoader);}if (this.environment != null) {scanner.setEnvironment(environment);}//扫描到所有的满足要求的bean的路径scanner.doScan(attributes.getStringArray("basePackage"));//随便起一个bean的名字(不能与其他beanName重复)String beanName = "myInterceptorConfig";//构造beanDefinitionBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();//beanDefinition的beanClassbeanDefinition.setBeanClass(MyInterceptorConfig.class);//给beanDefintion的beanClass构造方法传入参数ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();constructorArgumentValues.addIndexedArgumentValue(0, new MyInterceptor());constructorArgumentValues.addIndexedArgumentValue(1, new ArrayList(scanner.getURLS()));//注册这个beanDefinition,spring会在之后的流程中去调用getBean方法创建这个beanregistry.registerBeanDefinition(beanName, beanDefinition);}
}

 

至此我们的动态注册自定义路径拦截器的需求就完成了

我们测试一下:

@RestController
@RequestMapping("myTest")
public class TestController {@MyMapping@GetMapping("test")public void test() {//会被匹配到}@MyMapping@GetMapping("test1")public void test1() {//会被匹配到}@MyMappingpublic void test2() {//不会被匹配到,因为没有@GetMapping、@PostMapping、@RequestMapping}@GetMapping("test3")public void test3() {//不会被匹配到,因为没有@MyMapping}
}

我们访问可以匹配到的路径,可以发现拦截器生效了,没有匹配到的路径访问则没有生效。

嗯,标特否!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部