上一篇SpringBoot 动态代理|反射|注解|AOP 优化代码(二)-反射spring
咱们实现了经过反射完善找到目标类,而后经过动态代理提供默认实现,本篇咱们将使用自定义注解来继续优化。segmentfault
1.建立枚举 ClientType,用来标明Handler的实现方式api
public enum ClientType { FEIGN,URL }
2.建立注解ApiClient,用来标明Handler的实现方式app
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ApiClient { ClientType type(); }
3.建立HandlerRouterAutoImpl注解,来标记该HandlerRouter是否经过代理提供默认实现ide
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface HandlerRouterAutoImpl { /** * 在spring容器中对应的名称 * @return */ String name(); }
4.DeviceHandlerRouter添加注解,以动态代理提供默认实现优化
@HandlerRouterAutoImpl(name = "deviceHandlerRouter") public interface DeviceHandlerRouter extends HandlerRouter<DeviceHandler> { }
5.DeviceHandlerFeignImpl、DeviceHandlerUrlImpl 添加注解标明具体的实现方式this
@ApiClient(type = ClientType.FEIGN) @Component @Slf4j public class DeviceHandlerFeignImpl implements DeviceHandler { @Autowired private DeviceFeignClient deviceFeignClient; @Override public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) { RestResult restResult = deviceFeignClient.create(remoteAddDeviceParam); ... } @Override public void remoteDeleteBatch(Integer envValue, List<String> snsList) { RestResult restResult = deviceFeignClient.deleteBySnList(snsList); ... } }
@ApiClient(type = ClientType.URL) @Component @Slf4j public class DeviceHandlerUrlImpl implements DeviceHandler { @Override public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) { String url = getAddUrlByEnvValue(envValue); String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false); RestResult restResult = JSON.parseObject(response, RestResult.class); ... } @Override public void remoteDeleteBatch(Integer envValue, List<String> snsList) { String url = getDelUrlByEnvValue(envValue); String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false); RestResult restResult = JSON.parseObject(response, RestResult.class); ... } }
6.经过注解扫描目标类url
扫描HandlerRouterAutoImpl注解的类,经过动态代理提供默认的实现代理
/** * 经过反射扫描出全部使用注解HandlerRouterAutoImpl的类 * @return */ private Set<Class<?>> getAutoImplClasses() { Reflections reflections = new Reflections( "io.ubt.iot.devicemanager.impl.handler.*", new TypeAnnotationsScanner(), new SubTypesScanner() ); return reflections.getTypesAnnotatedWith(HandlerRouterAutoImpl.class); }
动态代理类中,获取业务接口的实现类,并获取ApiClient注解,而后分类,保存到Map中。以在调用getHandler方式时根据传入的环境值,返回不一样实现方式的实例。rest
@Slf4j public class DynamicProxyBeanFactory implements InvocationHandler { private String className; private Map<ClientType, Object> clientMap = new HashMap<>(2); public DynamicProxyBeanFactory(String className) { this.className = className; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取过一次后再也不获取 if (clientMap.size() == 0) { initClientMap(); } //若是传入的参数是1,就返回经过Feign方式实现的类 (该逻辑只是用来举例) Integer env = (Integer) args[0]; return 1 == env.intValue() ? clientMap.get(ClientType.FEIGN) : clientMap.get(ClientType.URL); } private void initClientMap() throws ClassNotFoundException { //获取classStr 接口的全部实现类 Map<String,?> classMap =SpringUtil.getBeansOfType(Class.forName(className)); log.info("DynamicProxyBeanFactory className:{} impl class:{}",className,classMap); for (Map.Entry<String,?> entry : classMap.entrySet()) { //根据ApiClientType注解将实现类分为Feign和Url两种类型 ApiClient apiClient = entry.getValue().getClass().getAnnotation(ApiClient.class); if (apiClient == null) { continue; } clientMap.put(apiClient.type(), entry.getValue()); } log.info("DynamicProxyBeanFactory clientMap:{}",clientMap); } public static <T> T newMapperProxy(String typeName,Class<T> mapperInterface) { ClassLoader classLoader = mapperInterface.getClassLoader(); Class<?>[] interfaces = new Class[]{mapperInterface}; DynamicProxyBeanFactory proxy = new DynamicProxyBeanFactory(typeName); return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); } }
以上咱们经过注解、动态代理、反射就实现了经过注解,找到须要提供默认实现的HandlerRouter子类,并经过动态代理提供默认实现。
还有一个问题:经过代理生成的对象,该怎么管理,咱们并不想经过代码,手动管理。若是能把动态代理生成的对象交给spring容器管理,其它代码直接自动注入就能够了。