From 6ab2a886dcbf4025420326e99ea487b053135492 Mon Sep 17 00:00:00 2001 From: kuroky Date: Sun, 4 Dec 2022 18:20:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=20Byte=20Buddy=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20Spring=20WebMVC=20Controller=20=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage-1/src/biz-project/biz-web/pom.xml | 6 ++ ...tAnnotationByteBuddyBeanPostProcessor.java | 77 +++++++++++++++++++ .../LoggingBeanPostProcessor.java | 35 +++++++++ .../web/interceptor/LoggingInterceptor.java | 47 +++++++++++ .../UserRegistrationController.java | 2 + .../InMemoryUserRegistrationService.java | 9 ++- .../src/main/resources/application.properties | 4 +- 7 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/AbstractAnnotationByteBuddyBeanPostProcessor.java create mode 100644 stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/LoggingBeanPostProcessor.java create mode 100644 stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/interceptor/LoggingInterceptor.java diff --git a/stage-1/src/biz-project/biz-web/pom.xml b/stage-1/src/biz-project/biz-web/pom.xml index a5f30e1..b1d5a1c 100644 --- a/stage-1/src/biz-project/biz-web/pom.xml +++ b/stage-1/src/biz-project/biz-web/pom.xml @@ -77,6 +77,12 @@ spring-boot-starter-test test + + + net.bytebuddy + byte-buddy + + diff --git a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/AbstractAnnotationByteBuddyBeanPostProcessor.java b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/AbstractAnnotationByteBuddyBeanPostProcessor.java new file mode 100644 index 0000000..0204125 --- /dev/null +++ b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/AbstractAnnotationByteBuddyBeanPostProcessor.java @@ -0,0 +1,77 @@ +package com.acme.biz.web.configuration; + +import net.bytebuddy.dynamic.DynamicType; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * 基于{@link net.bytebuddy.ByteBuddy}实现的注解拦截 + * + * @author 韩超 + * @since 1.0.0 + */ +public abstract class AbstractAnnotationByteBuddyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, + BeanClassLoaderAware, BeanFactoryAware { + + private ClassLoader classLoader; + private AutowireCapableBeanFactory autowireCapableBeanFactory; + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.autowireCapableBeanFactory = (AutowireCapableBeanFactory) beanFactory; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanType, String beanName) throws BeansException { + if (hasLogAnnotatedMethod(beanType, getAnnotationClass())) { + Class dynamicType = doIntercept(beanType).load(this.classLoader) + .getLoaded(); + try { + return this.autowireCapableBeanFactory.createBean(dynamicType); + } catch (Exception e) { + throw new BeanCreationException(e.getMessage()); + } + } + + return null; + } + + protected abstract DynamicType.Unloaded doIntercept(Class beanType); + + protected abstract Class getAnnotationClass(); + + protected ClassLoader getClassLoader() { + return classLoader; + } + + protected AutowireCapableBeanFactory getAutowireCapableBeanFactory() { + return autowireCapableBeanFactory; + } + + /** + * 是否存在被指定注解标记的方法 + * @param beanClass 被代理的bean class + * @param annotationClass 注解 + * @return 存在与否 + */ + public boolean hasLogAnnotatedMethod(Class beanClass, Class annotationClass) { + Method[] methods = beanClass.getDeclaredMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(annotationClass)) + return true; + } + return false; + } +} diff --git a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/LoggingBeanPostProcessor.java b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/LoggingBeanPostProcessor.java new file mode 100644 index 0000000..6810982 --- /dev/null +++ b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/configuration/LoggingBeanPostProcessor.java @@ -0,0 +1,35 @@ +package com.acme.biz.web.configuration; + +import com.acme.biz.web.interceptor.LoggingInterceptor; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.matcher.ElementMatchers; +import org.springframework.context.annotation.Configuration; + +/** + * TODO + * + * @author 韩超 + * @since 1.0.0 + */ +@Configuration +public class LoggingBeanPostProcessor extends AbstractAnnotationByteBuddyBeanPostProcessor { + + + private final LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); + + @Override + protected DynamicType.Unloaded doIntercept(Class beanType) { + return new ByteBuddy() + .subclass(beanType) + .method(ElementMatchers.isAnnotatedWith(LoggingInterceptor.Log.class)) + .intercept(MethodDelegation.to(this.loggingInterceptor)) + .make(); + } + + @Override + protected Class getAnnotationClass() { + return LoggingInterceptor.Log.class; + } +} diff --git a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/interceptor/LoggingInterceptor.java b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/interceptor/LoggingInterceptor.java new file mode 100644 index 0000000..98b88ce --- /dev/null +++ b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/interceptor/LoggingInterceptor.java @@ -0,0 +1,47 @@ +package com.acme.biz.web.interceptor; + +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * TODO + * + * @author 韩超 + * @since 1.0.0 + */ +public class LoggingInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class); + + @RuntimeType + public Object doLog(@Origin Method method, @SuperCall Callable callable) { + // intercept any method of any signature + logger.info("before invoke ..."); + long start = System.currentTimeMillis(); + try { + return callable.call(); + } catch (Exception e) { + logger.error("error occupied", e); + throw new RuntimeException(e); + } finally { + System.out.println(method + "invoke finished, it took " + (System.currentTimeMillis() - start)); + } + } + + @Target({ElementType.TYPE, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Log { + + } + +} diff --git a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/mvc/controller/UserRegistrationController.java b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/mvc/controller/UserRegistrationController.java index b271c0e..beecc9f 100644 --- a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/mvc/controller/UserRegistrationController.java +++ b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/mvc/controller/UserRegistrationController.java @@ -18,6 +18,7 @@ import com.acme.biz.api.interfaces.UserRegistrationService; import com.acme.biz.api.model.User; +import com.acme.biz.web.interceptor.LoggingInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -36,6 +37,7 @@ public class UserRegistrationController implements UserRegistrationService { @Override @ResponseBody + @LoggingInterceptor.Log public Boolean registerUser(User user) { return userRegistrationService.registerUser(user); } diff --git a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/service/InMemoryUserRegistrationService.java b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/service/InMemoryUserRegistrationService.java index b3daa10..ca45023 100644 --- a/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/service/InMemoryUserRegistrationService.java +++ b/stage-1/src/biz-project/biz-web/src/main/java/com/acme/biz/web/service/InMemoryUserRegistrationService.java @@ -19,6 +19,7 @@ import com.acme.biz.api.exception.UserException; import com.acme.biz.api.interfaces.UserRegistrationService; import com.acme.biz.api.model.User; +import com.acme.biz.web.interceptor.LoggingInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -47,12 +48,16 @@ public class InMemoryUserRegistrationService implements UserRegistrationService private Map usersCache = new ConcurrentHashMap<>(); - @Autowired - private Tracer tracer; + private final Tracer tracer; + + public InMemoryUserRegistrationService(Tracer tracer) { + this.tracer = tracer; + } @Autowired private CurrentTraceContext currentTraceContext; + @LoggingInterceptor.Log @Override public Boolean registerUser(User user) throws UserException { Long id = user.getId(); diff --git a/stage-1/src/biz-project/biz-web/src/main/resources/application.properties b/stage-1/src/biz-project/biz-web/src/main/resources/application.properties index db760c0..5c7b385 100644 --- a/stage-1/src/biz-project/biz-web/src/main/resources/application.properties +++ b/stage-1/src/biz-project/biz-web/src/main/resources/application.properties @@ -10,7 +10,7 @@ management.endpoint.env.post.enabled = true management.endpoints.web.basePath = /management # Metrics Prometheus Pushgateway \u5BFC\u51FA\u6FC0\u6D3B -management.metrics.export.prometheus.pushgateway.enabled = true +management.metrics.export.prometheus.pushgateway.enabled = false management.metrics.export.prometheus.pushgateway.baseUrl = http://127.0.0.1:9091 management.metrics.export.prometheus.pushgateway.pushRate = 10s management.metrics.export.prometheus.pushgateway.job = ${spring.application.name}-metrics-push-job @@ -20,7 +20,7 @@ eureka.client.serviceUrl.defaultZone = http://127.0.0.1:12345/eureka eureka.client.instanceInfoReplicationIntervalSeconds = 10 # Eureka Instance \u914D\u7F6E -eureka.instance.metadataMap.prometheus.scrape = true +eureka.instance.metadataMap.prometheus.scrape = false eureka.instance.metadataMap.prometheus.path = ${management.endpoints.web.basePath:/actuator}/prometheus eureka.instance.metadataMap.prometheus.port = ${management.server.port:${server.port}}