我有一个总体设计和体系结构问题。在查看了设计模式(装饰器,命令链等)之后,我仍然不清楚。
要求: 我想同时记录方法的输入和输出。
到目前为止,我所有的方法如下:
public SomeReturnType someMethod1( SomeParameter someParameter ) {
LOGGER.info( "{someMethod1}[START someMethod1 compute]" );
doSomeComputationFor(someParameter);
SomeReturnType result = getSomeResult();
LOGGER.info( "{someMethod1}[END someMethod1 compute]" );
return result;
}
public SomeOtherReturnType someMethod2( SomeOtherParameter someOtherParameter ) {
LOGGER.info( "{someMethod2}[START someMethod2 compute]" );
maybeDoSomeDBOperation(someOtherParameter);
SomeOtherReturnType result = getSomeOtherResult();
LOGGER.info( "{someMethod2}[END someMethod2 compute]" );
return result;
}
(在日志记录下方,我还使用一些KPI库发送一些时间戳,例如StatsD)
所有结构实际上都是以下形式:
public Foo method( Bar bar ) {
//1 do some logging as the method starts, and also send some KPI
LOGGER.info( "{method}[START method compute]" );
//2 do some computation and actual business logic
//3 do some logging the confirm the mothods has reached the end + send some other KPI like time elapsed
LOGGER.info( "{method}[END method compute]" );
return result;
}
现在,看着我的项目,我有大约290个遵循完全相同结构的方法。
有解决这个问题的聪明方法吗?也许有一个不错的模式?一些注释?方面?我愿意提出建议,因为我相信肯定有比当前解决方案更好的解决方案。
答案 0 :(得分:3)
可能的解决方案是使用依赖项注入器+注释。
这里有一个示例,说明如何在JavaSE应用程序中使用Weld来实现所需的功能。
您需要添加此依赖项:
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>3.1.0.Final</version>
</dependency>
然后创建一个注释,它将用于指向您要记录的那些方法。
package org.loggable;
import javax.interceptor.InterceptorBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
创建拦截器
package org.loggable;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import java.util.logging.Logger;
@Loggable
@Interceptor
public class LoggableInterceptor {
@AroundInvoke
public Object logMethod(InvocationContext context) throws Exception {
Logger logger = Logger.getLogger(context.getTarget().getClass().getSimpleName());
logger.info("Starting method: " + context.getMethod().getName());
Object response = context.proceed();
logger.info("Finished method: " + context.getMethod().getName());
return response;
}
}
如您所见,@AroundInvoke
允许我们控制输入方法和退出时间。
我们必须通知Weld,有一个新的拦截器,我们可以通过在META-INF文件夹中添加beans.xml
来实现。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<interceptors>
<class>org.loggable.LoggableInterceptor</class>
</interceptors>
</beans>
最后,我们需要通过Weld来调用我们的实体,因为它负责创建和执行拦截器。
package org.loggable;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import java.io.IOException;
import java.util.logging.Logger;
public class Main {
public static void main(String... args) throws IOException {
SeContainer seContainer = SeContainerInitializer.newInstance()
.initialize();
Main main = seContainer.select(Main.class).get();
main.loggableMethod();
seContainer.close();
}
@Loggable
public void loggableMethod() {
Logger.getLogger(Main.class.getSimpleName()).info("Inside method.");
}
}
您将得到类似的输出
[2019-04-06 11:07:20] [INFO ] Starting method: loggableMethod
[2019-04-06 11:07:20] [INFO ] Inside method.
[2019-04-06 11:07:20] [INFO ] Finished method: loggableMethod
这是需要的项目结构。
注意:如果您在JavaEE项目中,则与Weld创建相关的所有操作都由容器管理。