需求背景:项目启动后初始化一个 url工厂,工厂中存放固定包路径下的controller中使用 自定义注解 UpperLimitAnnotation 的方法的请求 全路径;实现方式:反射+spring InitializingBean接口;
扩展场景:初始化其他类型工厂,如 策略模式中的策略工厂等 可以结合spring InitializingBean 实现
代码如下:
说明:0.9.12 版本依赖 发布到 生产环境 报 org.reflections.ReflectionsException: Scanner MethodAnnotationsScanner was not configured 异常,使用0.9.11版本则无此问题;
// 反射依赖 <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
工厂类代码:
package com.ruijie.demo.util; import com.alibaba.fastjson.JSON; import com.ruijie.demo.annotaion.UpperLimitAnnotation; import net.minidev.json.JSONUtil; import org.assertj.core.util.Lists; import org.reflections.Reflections; import org.reflections.scanners.*; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Set; /** * @ClassName UrlFactory * @Description 对外开放接口url工厂 * @Author WANGQW * @Date 2021/4/6 10:56 **/ @Component public class UrlFactory implements InitializingBean { public static final List<String> urlFactory = Lists.newArrayList(); @Value("${spring.application.name}") private String applicationName; /** * @Author wangqw * @Description 反射获取 所有api包下所有使用UpperLimitAnnotation 的接口全路径 * @Date 2021/4/6 15:31 * @Param [] * @return void **/ @Override public void afterPropertiesSet() throws Exception {
// Reflections reflections = new Reflections("com.ruijie.demo.api", Arrays.asList(
// new SubTypesScanner(false)//允许getAllTypes获取所有Object的子类, 不设置为false则 getAllTypes 会报错.默认为true.
// ,new MethodParameterNamesScanner()//设置方法参数名称 扫描器,否则调用getConstructorParamNames 会报错
// ,new MethodAnnotationsScanner() //设置方法注解 扫描器, 否则getConstructorsAnnotatedWith,getMethodsAnnotatedWith 会报错
// ,new MemberUsageScanner() //设置 member 扫描器,否则 getMethodUsage 会报错, 不推荐使用,有可能会报错 Caused by: java.lang.ClassCastException: javassist.bytecode.InterfaceMethodrefInfo cannot be cast to javassist.bytecode.MethodrefInfo
// ,new TypeAnnotationsScanner()//设置类注解 扫描器 ,否则 getTypesAnnotatedWith 会报错
// ));
Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("com.ruijie.demo.api")).setScanners(new MethodAnnotationsScanner()));
//获取所有带有 UpperLimitAnnotation 注解的方法对象 Set<Method> methods = reflections.getMethodsAnnotatedWith(UpperLimitAnnotation.class); methods.stream().forEach(item ->{ String clazzUrl = ""; RequestMapping clazzMapping = item.getDeclaringClass().getAnnotation(RequestMapping.class); if(null != clazzMapping && !ObjectUtils.isEmpty(clazzMapping.value())){ clazzUrl = clazzMapping.value()[0]; } RequestMapping methodMapping = item.getAnnotation(RequestMapping.class); if(null == methodMapping || ObjectUtils.isEmpty(methodMapping.value())){ return; } String methodUrl = methodMapping.value()[0]; String url = StringUtils.isEmpty(clazzUrl) ? "/" + applicationName + methodUrl : "/" + applicationName + clazzUrl + methodUrl; urlFactory.add(url); }); } }
controller 中 注解使用:
package com.ruijie.demo.api; import com.alibaba.fastjson.JSONObject; import com.ruijie.demo.annotaion.UpperLimitAnnotation; import com.ruijie.demo.entity.remote.RemoteOrganization; import com.ruijie.demo.entity.remote.TycResult; import com.ruijie.demo.service.EeterpriseApiService; import com.ruijie.framework.common.RemoteResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; /** * @ClassName EnterpriseApiController * @Description * @Author WANGQW * @Date 2021/3/18 10:11 **/ @RestController @RequestMapping("/company") public class EnterpriseApiController { @Autowired private EeterpriseApiService eeterpriseApiService; /** * @Author wangqw * @Description 模糊搜索企业信息-列表 * @Date 2021/3/16 17:47 * @Param [keyword] * @return com.ruijie.framework.common.RemoteResult<com.ruijie.demo.domain.remote.TycResult> **/ @UpperLimitAnnotation @RequestMapping(value = "/search",method = RequestMethod.GET) public RemoteResult<TycResult> search(@RequestParam("keyword") String keyword){ return new RemoteResult<>(eeterpriseApiService.search(keyword)); } }
简述:spring InitializingBean 接口的作用:
1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用
2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法。