I. Background of the problem

The business group of the blogger is currently doing an architecture upgrade. With the introduction of the unified basic engineering module, one of the business packages with the chatroom function [implemented using Websocket] cannot be started when the other business modules are all up and running normally.

An error is as follows

Caused by: javax.websocket.DeploymentException: Cannot deploy POJO class [com.xxxx.service.impl.ChatWebSocketImpl$$EnhancerBySpringCGLIB$$7792c1b8] as it is not annotated with @ServerEndpoint
	at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java: 245).at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java: 228).at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoint(ServerEndpointExporter.java:156)
	... 11 common frames omitted
Copy the code

Look for problems

My first reaction to this was, look, is there something wrong with WebSocket? How can it not work when other applications are fine?

And immediately tell my smart little brain to work. Well, it’s a Google bite.

On the Internet search to the following answer blog.csdn.net/qq_43644023…

Websocket classes marked with @serverEndpoint cannot be proxied by AOP.

Heart happy, immediately commad+shift+F global @Aspect, and then so surprise, so surprise, nothing.

I thought my way of opening had changed, then wondered if my dependency was in conflict, and used Maven-Helper to see that there was no conflict.

Then I also introduced Websocket in a demo project, and then introduced the basic engineering of the new architecture, startup, demo project directly started…

I just froze. Then I hit the breakpoint here according to the stack error!

Discover the business module where the annotation is null, and the demo project annotation is valued.

Three. Positioning problems

Later I wrote an AOP aspect in the demo project and defined the pointcut in the WebSocket service class, but it also failed to start. Boy, did you get your head around here. We don’t have AOP in the project, and we don’t allow external frameworks to introduce aspects to generate proxy classes?

You can use the/meta-INF /spring.factories file to implement the automatic configuration of external frameworks.

To find the proxy generation path for our webSocket configuration class, we need to refer to the life cycle of beans in Spring.

The Spring lifecycle is divided into four main parts

Instantiate -> Attribute assignment -> Initialize -> Destroy

Generate the AOP agent in the initialization phase BeanPostProcessor postProcessAfterInitialization AnnotationAwareAspectJAutoProxyCreator in the post processor

The following code

/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean ! = null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) ! = bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }Copy the code

Enter the key method wrapIfNecessary

* Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors ! = DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }Copy the code

According to the comments, find the method of generating the proxy

reateProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))
Copy the code

This is where the break point is. The trick here is that if the break point is directly here, it will cause all classes to be cut to enter the break point. Idea can set a conditional breakpoint

Found this tripartite aop proxy class org. Springframework. Cloud. The sleuth. Instrument. The scheduling. TraceSchedulingAspect

The pointcut inside is defined as follows

@Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..) )") public Object traceBackgroundThread(final ProceedingJoinPoint PJP) throws Throwable {Copy the code

It is found that all classes and methods annotated by Spring’s scheduled task annotation will be cut by the aspect.

Following this idea, I searched for this annotation in the business implementation class 1 corresponding to WebSocket and found the following code

@Scheduled(cron = "*/20 * * * * ?" Public void run(){// ignore business logic}Copy the code

Solve the problem

I looked at the Traceschedling Aspect aspect class and found that it was marked ** @deprecated ** and would be Deprecated in later versions. Then click the class found it will be TraceSchedulingAutoConfiguration is loaded into the automatic configuration class, and the automatic configuration class are marked * * * * @ Deprecated.

The following code

@Deprecated @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint") @ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true) @ConditionalOnBean(Tracing.class) @AutoConfigureAfter(TraceAutoConfiguration.class) @EnableConfigurationProperties(SleuthSchedulingProperties.class) public class TraceSchedulingAutoConfiguration { @Bean public TraceSchedulingAspect traceSchedulingAspect(Tracer tracer, SleuthSchedulingProperties sleuthSchedulingProperties) { return new TraceSchedulingAspect(tracer, Pattern.compile(sleuthSchedulingProperties.getSkipPattern())); }}Copy the code

See this @ ConditionalOnProperty (value = “spring. Sleuth. Scheduled. Enabled”, matchIfMissing = true), and everybody thought is very clear, I added a spring configuration in the configuration file. The sleuth. Scheduled. Enabled = false, the application can launch a success.

In live.

In fact, daily business development is not too much will encounter similar to this framework conflict problems, but in the face of such problems, reasonable use of Google resources and their own source code knowledge ability to locate bugs is still very important.

Contact me

Nailing: louyanfeng25

WeChat: baiyan_lou