Last article: SpringBoot dynamic agent | reflection | annotation | AOP optimization code (3) - Annotation

In this article, we will implement the object generated by proxy into the spring container.
First of all, you need to implement two interfaces, beandefinitionregistrypostprocessor and applicationcontextaware. The functions are as follows:
ApplicationContextAware: you can get the ApplicationContext object and then the object in the Spring container
BeanDefinitionRegistryPostProcessor+ FactoryBean: we can inject our customized beans into the spring container

@Slf4j
@Component
public class HandlerBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        /**
         * Get AutoImpl annotated interfaces that need to be implemented by default through dynamic agents
         */
        Set<Class<?>> classes = getAutoImplClasses();
        for (Class<?> clazz : classes) {
            /**
             * Get the generic typeName of the interface inherited from HandlerRouter, and pass it in to dynamicprroxybeanfactory
             * It can be passed in to DynamicProxyBeanFactory to scan the implementation class of typeName, and then implement according to feign and url.
             * Mode classification
             */

            Type[] types = clazz.getGenericInterfaces();
            ParameterizedType type = (ParameterizedType) types[0];
            String typeName = type.getActualTypeArguments()[0].getTypeName();

            /**
             * By injecting FactoryBean into spring container, HandlerInterfaceFactoryBean implements the following functions:
             * 1.Calling dynamic proxy DynamicProxyBeanFactory to provide the default implementation of HandlerRouter sub interface
             * 2.Inject the default implementation of the first step into the spring container
             */

            HandlerRouterAutoImpl handlerRouterAutoImpl = clazz.getAnnotation(HandlerRouterAutoImpl.class);
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            definition.getPropertyValues().add("interfaceClass", clazz);
            definition.getPropertyValues().add("typeName", typeName);
            definition.getPropertyValues().add("context", applicationContext);
            definition.setBeanClass(HandlerInterfaceFactoryBean.class);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            beanDefinitionRegistry.registerBeanDefinition(handlerRouterAutoImpl.name(), definition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        log.info("------------------------>postProcessBeanFactory");
    }

    /**
     * Scan all classes using HandlerRouterAutoImpl by reflection
     * @return
     */
    private Set<Class<?>> getAutoImplClasses() {
        Reflections reflections = new Reflections(
                "io.ubt.iot.devicemanager.impl.handler.*",
                new TypeAnnotationsScanner(),
                new SubTypesScanner()
        );
        return reflections.getTypesAnnotatedWith(HandlerRouterAutoImpl.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        log.info("------------------->setApplicationContext");
    }

    /**
     * Get all bean s of this type through class
     *
     * @param clazz
     * @return
     */
    private Map<String, T> getBeans(Class<T> clazz) {
        return applicationContext.getBeansOfType(clazz);
    }

    private String getYmlProperty(String propery) {
        return applicationContext.getEnvironment().getProperty(propery);
    }
}

HandlerInterfaceFactoryBean creates a default implementation class through a dynamic proxy

@Slf4j
@Data
public class HandlerInterfaceFactoryBean<T> implements FactoryBean<T> {
    private Class<T> interfaceClass;
    private String typeName;
    private ApplicationContext context;
    @Override
    public T getObject() throws Exception {
        Object object = DynamicProxyBeanFactory.newMapperProxy(typeName, context, interfaceClass);
        return (T) object;
    }

    @Override
    public Class<?> getObjectType() {
        return interfaceClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

Final implementation of DynamicProxyBeanFactory

@Slf4j
public class DynamicProxyBeanFactory implements InvocationHandler {

    private String className;
    private ApplicationContext applicationContext;

    private Map<ClientType, Object> clientMap = new HashMap<>(2);

    public DynamicProxyBeanFactory(String className, ApplicationContext applicationContext) {
        this.className = className;
        this.applicationContext = applicationContext;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (clientMap.size() == 0) {
            initClientMap();
        }
       
        Integer env = (Integer) args[0];
        return 1 == env.intValue() ? clientMap.get(ClientType.FEIGN) : clientMap.get(ClientType.URL);
    }

    private void initClientMap() throws ClassNotFoundException {
        //Get all implementation classes of the classStr interface
        Map<String,?> classMap = applicationContext.getBeansOfType(Class.forName(className));
        log.info("DynamicProxyBeanFactory className:{} impl class:{}",className,classMap);

        for (Map.Entry<String,?> entry : classMap.entrySet()) {
            //According to the ApiClientType annotation, the implementation classes are divided into two types: Feign and 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 classStr,ApplicationContext applicationContext,Class<T> mapperInterface) {
        ClassLoader classLoader = mapperInterface.getClassLoader();
        Class<?>[] interfaces = new Class[]{mapperInterface};
        DynamicProxyBeanFactory proxy = new DynamicProxyBeanFactory(classStr,applicationContext);
        return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
    }
}

The above is the annotation to get the target class. The dynamic proxy provides the default implementation and injects it into the core code of the Spring container.

unit testing

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class OptimizationTest {

    @Autowired
    @Qualifier("deviceHandlerRouter")
    private DeviceHandlerRouter deviceHandlerRouter;

    @Test
    public void dispatchApp() {

        DeviceHandler deviceHandlerFeignImpl = deviceHandlerRouter.getHandler(1, null);
        log.info("DeviceHandler-------------->{}",deviceHandlerFeignImpl);

        DeviceHandler deviceHandlerUrlImpl = deviceHandlerRouter.getHandler(2, null);
        log.info("DeviceHandler-------------->{}",deviceHandlerUrlImpl);
    }

   
}

Aop optimization code to be continued

Reference articles https://blog.csdn.net/qq_2059...