CallKeLinServiceProxyConfiguration.java 3.23 KB
package com.zteits.charge.config;


import com.zteits.charge.service.spi.CallBusinessInfoSpi;
import com.zteits.charge.service.spi.CallStationInfoSpi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 调用科林接口代理类,实现加密和签名
 *
 * <p>⚠️ 这里**故意删掉了**老代码上的 {@code @ComponentScan}(无参数版),它不是「少了个配置」,
 * 而是合并单 Jar 时的一个坑,详见 AGENTS.md 坑 14:
 *
 * <p>无参数 {@code @ComponentScan} 扫的是**本类所在包** {@code com.zteits.charge.config}。
 * 合并单 Jar 后,这个包名同时被**封闭 Jar**({@code zteits-parking-charge-spring-boot-starter},
 * 源码不可改)占用,于是它会把那个 Jar 里的 {@code ChargeConfig} 一起扫进来注册成
 * {@code @Configuration};而 {@code ChargeConfig} 自己又带
 * {@code @ComponentScan("com.zteits.charge")},会再触发一整轮**不受启动类过滤器约束**的扫描,
 * 最终抛出 {@code BeanDefinitionOverrideException}({@code @Bean("chargeService")} 撞
 * {@code @Component ChargeService})。
 *
 * <p>删掉它是**零损失**的:启动类 {@code ParkingServerApplication} 的
 * {@code @ComponentScan} 已经包含 {@code com.zteits.charge},本包下的
 * {@code KeLinOkHttpHelper} 等组件本来就会被扫到(实测启动日志里它们由启动类那一轮扫描注册,
 * 后一轮重复扫描对已注册的同源 Bean 是空操作)。老工程 7 个 JVM 里
 * {@code ChargeApplication} 同样扫 {@code com.zteits.charge},所以这一行在老工程里也是冗余的。
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class CallKeLinServiceProxyConfiguration {
    private Lock lock = new ReentrantLock();
    final KeLinOkHttpHelper keLinOkHttpHelper;

    @Bean
    @ConditionalOnMissingBean
    public CallStationInfoSpi getStationService(){
        return (CallStationInfoSpi) Proxy.newProxyInstance(
            CallKeLinServiceProxyConfiguration.class.getClassLoader(),
            new Class[] { CallStationInfoSpi.class },
            new ChargingInvocationHandler()
        );
    }

    @Bean
    @ConditionalOnMissingBean
    CallBusinessInfoSpi getBusinessService(){
        return (CallBusinessInfoSpi) Proxy.newProxyInstance(
            CallKeLinServiceProxyConfiguration.class.getClassLoader(),
            new Class[] { CallBusinessInfoSpi.class },
            new ChargingInvocationHandler()
        );
    }

    @RequiredArgsConstructor
    public class ChargingInvocationHandler implements InvocationHandler{

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ApiName apiName = method.getAnnotation(ApiName.class);
            return keLinOkHttpHelper.post(apiName.value(),(String)args[0], args[1], method.getReturnType());
        }
    }
}