Backend Development 13 min read

Applying SWAK Framework in Xianyu Search: Architecture and Implementation

The article explains how Xianyu’s search service refactors tightly‑coupled, if‑else‑heavy code by applying the lightweight SWAK framework—defining @SwakInterface and @SwakTag annotations, registering proxy beans, using AOP and CGLIB proxies to route business logic via request‑derived tag groups, achieving clean, modular routing.

Xianyu Technology
Xianyu Technology
Xianyu Technology
Applying SWAK Framework in Xianyu Search: Architecture and Implementation

SWAK (Swiss Army Knife) is a lightweight, flexible framework used in Xianyu's backend to decouple platform and business code, especially in the search glue layer.

The search glue layer originally suffered from tight coupling and extensive if‑else logic. By applying SWAK, two main problems were addressed: (1) decoupling business types via bizType tags, allowing independent page composition; (2) replacing card parser if‑else chains with tag‑based routing using cardType.

SWAK works by defining interfaces annotated with @SwakInterface and multiple implementations annotated with @SwakTag . At runtime, a tag group is built from request parameters and used to select the appropriate implementation.

if(搜商品) { if(搜商品A版本) { doSomething1(); } else if(搜商品B版本) { doSomething2(); } } else if(搜会玩) { doSomething3(); } else if(搜用户) { if(搜用户A版本) { doSomething4(); } else if(搜用户B版本) { doSomething5(); } }

Core implementation steps:

1. Registration process : Scan classes for @SwakInterface and @SwakTag using a library such as Reflections, then register a proxy bean for each interface in the Spring container.

public Set > getSwakInterface() { Reflections reflections = new Reflections(new ConfigurationBuilder() .addUrls(ClasspathHelper.forPackage(this.packagePath)) .setScanners(new TypeAnnotationsScanner(), new SubTypesScanner()); return reflections.getTypesAnnotatedWith(SwakInterface.class); }

2. Proxy bean registration creates a RootBeanDefinition whose bean class is SwakInterfaceProxyFactoryBean , storing the original interface class as a property.

@Configuration public class ProxyBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { Set typesAnnotatedWith = getSwakInterface(); for (Class superClass : typesAnnotatedWith) { if (!superClass.isInterface()) { continue; } RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(SwakInterfaceProxyFactoryBean.class); beanDefinition.getPropertyValues().addPropertyValue("swakInterfaceClass", superClass); String beanName = superClass.getName(); beanDefinition.setPrimary(true); beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition); } } }

3. Dynamic proxy creation in SwakInterfaceProxyFactoryBean uses CGLIB Enhancer to generate a proxy that delegates method calls to the selected implementation.

public class SwakInterfaceProxyFactoryBean implements FactoryBean { @Override public Object getObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.clazz); enhancer.setCallback(new SwakInterfaceProxy()); return enhancer.create(); } }

4. AOP interception is performed by SwakSessionInterceptor , which extracts the tag group before the annotated method executes and stores it in SwakSessionHolder .

@Component @Aspect public class SwakSessionInterceptor { @Pointcut("@annotation(com.taobao.idle.swak.core.aop.SwakSessionAop)") public void sessionAop() {} @Around("sessionAop() && @annotation(swakSessionAop)") public Object execute(ProceedingJoinPoint jointPoint, SwakSessionAop swakSessionAop) throws Throwable { Class instanceClass = swakSessionAop.instanceClass(); Object sessionInstance = null; for (Object object : jointPoint.getArgs()) { if (instanceClass.isAssignableFrom(object.getClass())) { sessionInstance = object; } } Class parserClass = swakSessionAop.tagGroupParserClass(); SwakTagGroupParser parser = (SwakTagGroupParser) parserClass.newInstance(); SwakTagGroup tagGroup = parser.parse(sessionInstance); SwakSessionHolder.hold(tagGroup); Object result = jointPoint.proceed(); SwakSessionHolder.clear(); return result; } }

5. Method interception in SwakInterfaceProxy retrieves the tag group, obtains the implementation instance for each tag, and invokes the original method.

public class SwakInterfaceProxy implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { SwakTagGroup tagGroup = SwakSessionHolder.getTagGroup(); Object retResult = null; for (String tag : tagGroup.getTags()) { Object impl = getInvokeInstance(tag); retResult = method.invoke(impl, args); } return retResult; } }

The complete flow results in clean, tag‑driven routing of business logic without hard‑coded if‑else branches.

Conclusion : The article demonstrates how SWAK can be applied to refactor tightly coupled code in Xianyu's search service, providing a reusable pattern for backend developers.

backendjavaaopspringDynamicProxySWAK
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.