Author: Idle fish technology – Dust xiao

Three years ago, we published an article about SWAK, short for Swiss Army Knife, which is a small, flexible and versatile tool. On the server side, the SWAK framework is also a small and flexible technical framework suitable for multiple scenarios. It can be used to solve the serious difficulty of separating platform code and business code coupling. There is a lack of disassembly of interwoven code between businesses and businesses. Previously we applied it to business decoupling in the commodity domain, and now we encountered this problem again in the search glue layer, so we decided to bring SWAK back to shine again.

The application of SWAK in idle fish search

At present, Xianyu search adopts a new end-cloud integrated development mode, and introduces a glue layer to move part of the client’s logic to the server, so as to improve the dynamic capability of the end and side, reduce the number of follow-up version requirements, and speed up the online speed of requirements. In running this new pattern, the code structure for the glue layer was initially designed to be relatively simple, resulting in severe coupling and if-else logic dilatation in the glue layer code. undefined

Based on the business prediction, we reconstructed the glue layer and mainly solved two problems by SWAK:

Due to the increasing number of vertical businesses searched, the existing structure cannot support business customization, so a layer of decoupling is implemented through SWAK, and the logic is arranged according to the different biztypes (business types) to the corresponding pages. This ensures that the page layout logic of different businesses will not affect each other. Small businesses can reuse the page logic of commodity search or customize it themselves.

The increasing variety of cards has led to the expansion of if-else code in the card parser section. Therefore, we introduced the second layer SWAK, and removed the if-else in the card parser part, and changed it to the mode of indexing to the corresponding parser through cardType, so as to avoid the subsequent students getting confused among a bunch of if-else and failing to find the real logic.

undefined

At present, the search glue layer is still in the overall architecture upgrade. Here, I will give you a brief introduction. Later, after the architecture upgrade is completed, I will write an article to introduce the research and development mode of xianyu search end and cloud integration.

Review how SWAK is used

Before explaining the principles of SWAK, let’s briefly review the ways in which SWAK is used to better understand its principles. Let’s first take a look at what problems SWAK solves. For example, when we enter a search, we need to determine different search types and return different page choreography, and we need to go to different branches of code based on that type. If the code is coupled in one place, then the file becomes increasingly difficult to maintain.

If (something1) {if(something1) {doSomething1(); }else if(B version) {doSomething2(); } else if(doSomething3) {doSomething3(); } else if(search user A) {doSomething4(); }else if(user B version) {doSomething5(); }} SWAK is used to solve this problem, we can spread out all the if-else logic, turn it into a TAG, and route through SWAK.

/ * *

Public interface IPage {SearchPage Parse (Object Data); / @swakInterface (desc = “Component parse “) public Interface IPage {SearchPage Parse (Object Data); }

/ * *

•2. Then write the corresponding implementation, which can be many, •/ @Component @swakTag (tags = {ComponentTypeTag.COMMON_SEARCH}) public Class CommonSearchPage Implements IPage { @Override public SearchPage parse(Object data) {

  return null;
Copy the code

}}

/ * *

3. Write the entry of the Swak route

/ @Component public class PageService { @Autowired private IPage iPage;

@SwakSessionAop(tagGroupParserClass = PageTypeParser.class,

  instanceClass = String.class)
Copy the code

public SearchPage getPage(String pageType, Object data) {

  return iPage.parse(data);
Copy the code

}}

/ * *

•/ public Class PageTypeParser implements SwakTagGroupParser {@override public SwakTagGroup parse(String) pageType) {

      // pageType = ComponentTypeTag.COMMON_SEARCH
  return new SwakTagGroup.Builder().buildByTags(pageType);
Copy the code

Although the code is only a few lines, it covers all the core processes in SWAK. The core problem is how to find the corresponding interface implementation in SWAK. We need to divide the registration process and the execution process to answer this question

Picture 2

The registration process

SWAK borrowed a lot of Spring features when it was designed, since most of its server-based applications are based on the Spring framework. Spring related features if you do not understand can be consulted, this side will not be detailed. In the example above, the main purpose of the registration phase is to find the @swakInterface annotated IPage class and hand it over to the Spring container for hosting, so that Spring’s dependency injection capabilities are naturally available when used. In addition, in order to dynamically replace the interface implementation later, we cannot directly register the found class in the Spring container. We need to hook it into a proxy class and return different @swaktag instances in the proxy class according to the situation.

There are a few questions that may arise from these short sentences. Let’s answer them one by one:

How to find a @swakInterface annotated Bean? How to find a @swakInterface annotated Bean

In JAVA we usually use reflection to get custom annotations, so all you need to do here is scan all classes (you can optimize the scan scope here, you can only scan classes in a specific path) and get custom annotations through reflection. Scan library code to write their own logic is of course possible, but the use of open source framework is also a good choice, here we recommend ronmamo reflections library (Github), the implementation principle of the library here is not detailed, use is very simple, directly on the code bar

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

In addition to scanning @swakInterface, we should also scan out @swakTag classes and store them in a map so that we can find a Class using the Tag.

How to switch gears when Spring registers beans

The cut Spring actually has given us are ready, the Spring in the Bean’s registered stage will get container all types of BeanDefinitionRegistryPostProcessor Bean, And call postProcessBeanDefinitionRegistry method, so that we can directly inherit this class and write the corresponding way to Hook the process. In this method, we can create a new BeanDefinition and set the prepared proxy class as BeanClass, so that when we generate the corresponding Bean, we will use our prepared proxy class directly. (The principle here is related to the Registration process of Spring beans, so you can consult the information on your own, and I won’t go into details.)

@Configuration public class ProxyBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
    Set<Class> 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);
    }
}
Copy the code

} How can a proxy class dynamically replace an interface implementation

In the previous step, we have prepared a SwakInterfaceProxyFactoryBean as proxy class registered to BeanDefinitionMap, but actually SwakInterfaceProxyFactoryBean technically is not a proxy class, As the name suggests, it’s a FactoryBean, which is a class in Spring for creating more complex beans. In the getObject() method of this class, we really use dynamic proxy to create the corresponding object, creating the corresponding object.

In the choice of dynamic proxy, we use CGLIb to implement dynamic proxy, because the built-in dynamic proxy mechanism in JDK can only proxy classes that implement interfaces, while CGLIb can provide proxy for classes that do not implement interfaces and can provide better performance. There are a lot of information about CGLIB on the Internet, so I won’t go into the details here. Set a CallBack in the Enhancer, in the proxy class method is called, will CallBack we set in SwakInterfaceProxy. Intercept () method to intercept. The intercept() method will be covered in more detail in the following execution. Let’s look at this part of the code first

public class SwakInterfaceProxyFactoryBean implements FactoryBean { @Override public Object getObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this); this.clazz = clazz; Enhancer.setcallback (new SwakInterfaceProxy()) enhancer.setCallback(new SwakInterfaceProxy()) enhancer.setCallback(new SwakInterfaceProxy()) enhancer.setCallback(new SwakInterfaceProxy()); // Return the proxy object. The returned object starts as a proxy class that encapsulates the “entity class” and is an instance of the implementation class. return enhancer.create(); }} Execute procedure

SwakTagGroupParser parses member variable IPage IPage to CommonSearchPage before the @swaksessionAOP tag’s method body is executed. Calling ipage.parse() in the body of this method then calls the CommonSearchPage.parse() method directly.

Is also a short two sentences, then some children may ask:

How do we insert the code we parsed before the method body of the @swaksessionAOP tag executes? How do we “assign” the iPage variable after resolving the corresponding implementation class? How do we insert the code before the method

Spring’s AOP is a DYNAMIC jVM-based proxy implemented through CGlib, and provides a nice layer of encapsulation. We can use the @around annotation to execute our code one level ahead of the method. We first parse the tagGroup using the SwakTagGroupParser parser and save the parsed tagGroup. Then we can call JointPoint.proceed () to continue with the method body. The iPage used in the method body will then be used in the corresponding implementation.

Some of you may have a question, but I saved the tagGroup here. Does this later cause iPage to use the corresponding implementation? We will describe this in the next question.

@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) {/ / according to the requirements of the type to get into the parameters of the Parser Class instanceClass = SwakSessionAop. InstanceClass (); Object sessionInstance; for (Object object : args) { if (instanceClass.isAssignableFrom(object.getClass())) { sessionInstance = object; }} / / by Parser parsed out corresponding tagGroup Class parserClass = swakSessionAop. TagGroupParserClass (); SwakTagGroupParser swakTagGroupParser = (SwakTagGroupParser)(parserClass.newInstance()); SwakTagGroup tagGroup = swakTagGroupParser.parse(sessionInstance); Try {//SwakSessionHolder is where the tagGroup is stored. Swaksessionholger.hold (tagGroup) can be implemented at will; Object object = jointPoint.proceed(); return object; } finally { SwakSessionHolder.clear(); }}Copy the code

} how to “assign “iPage variable

First OF all, I need to explain why I keep putting “assign” in quotes, because this part isn’t really assigning to iPage, but the effect is the same. Remember that we made the @swakInterface annotated class a dynamic proxy when registering, so the object corresponding to iPage will call the intercept() method mentioned earlier before calling the method. In this method, We can use the saved tagGroup to find the SwakTag we want to invoke. We can use the SwakTag to find the corresponding implementation class instance. Finally, we can invoke the example using the method.invoke() method.

To invoke a Method instance, invoke an object instance. The first parameter of the invoke object is the object instance on which to call the Method. Otherwise, an error will be reported. public class SwakInterfaceProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] parameters, MethodProxy methodProxy) throws Throwable { String interfaceName = clazz.getName(); SwakTagGroup tagGroup = SwakSessionHolder.getTagGroup();

List<String> tags = taggroup.gettags (); // Tags = taggroup.gettags (); Object retResult = null; Try {// Execute for (String tag: Tags) {// you can get an instance of the implementation Class based on the TAG. // You can get an instance of the implementation Class based on the TAG. Object tagImplInstance = getInvokeInstance(tag); retResult = method.invoke(tagImplInstance, parameters); } return retResult; } catch (Throwable t) { throw t; }}Copy the code

} At this point, the complete process of invoking a method using SWAK is complete.

conclusion

In this paper, the principle of SWAK is mainly expounded, and some key code implementations are attached. In order to reduce the understanding cost and length of some codes involved in this paper, some reduction is made to a certain extent, and direct copy is avoided by all means. SWAK open source preparation is still a long way off and may not be available for a short time, but you can refer to this article for your own implementation. If the article is sent out, you have questions, we will continue to write the corresponding article according to your questions.