Python里有非常方便的Function Decorator语法糖,使用起来非常简便。如果想在Java里做类似的事情,可以使用Dynamic Proxy API来实现。
看看这个小例子。有一组Web服务接口,需要访问数据库得到所需的数据。比如,
1 2 3 4 5 6 |
Analyzer analyzer = new Analyzer(...); ... // 使用了Spark Framework Spark.get("/review_request_per_month_last_half_year", (req, res) -> { return analyzer.getRequestStatisticOfLastSixMonth(); }); |
代码里的Analyzer类会去数据库找到相应的数据。
为了提高访问的效率,加入memcached,在真正的数据库访问之前,先去查看有没有相应的Cache,代码会变成类似的样子,
1 2 3 4 5 6 |
String result = memcachedClient.get("review_request_per_month_last_half_year"); if (result == null) { result = review_request_per_month_last_half_year; memcachedClient.set("review_request_per_month_last_half_year", result); } return result; |
如果每个服务接口都要做同样的事,可想而知,会有非常多的重复代码。这时就可以利用Dynamic Proxy API来大大的优化代码实现,非常灵活。
Dynamic Proxy API只能针对接口,所以要为原来Analyzer类写一个接口类,如:
1 2 3 4 5 6 7 8 9 10 11 12 |
interface IAnalyzer { String getRequestStatisticOfLastSixMonth(); } class Analyzer implements IAnalyzer { @Override public String getRequestStatisticOfLastSixMonth() { // .... } } |
然后我们的代理实现类要继承自InvocationHandler,并通过Proxy.newProxyInstance生成真正的代理。看下面的实现,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class CachedAnalyzerProxy implements InvocationHandler { private static final Logger LOGGER = LogManager.getLogger(CachedAnalyzerProxy.class); private IAnalyzer analyzer; private final MemcachedUtil cached = new MemcachedUtil("localhost", "11211"); public CachedAnalyzerProxy(IAnalyzer analyzer) { // 保留Analyzer实现 this.analyzer = analyzer; } public static IAnalyzer bind(IAnalyzer analyzer) { // 生成真正的动态代理 return (IAnalyzer) Proxy.newProxyInstance( analyzer.getClass().getClassLoader(), new Class[] { IAnalyzer.class }, new CachedAnalyzerProxy(analyzer)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里的Cache Key简单的使用的函数名 LOGGER.debug("invoking " + method.getName()); String result = cached.get(method.getName()); if (result == null) { LOGGER.debug("cache is not hit."); // 利用保留的Analyzer引用,调用原来的函数实现 result = (String) method.invoke(analyzer, args); cached.set(method.getName(), result); } else { LOGGER.debug("cache is hit"); } return result; } } |
有了动态代理,原来Web服务接口的实现就非常简洁了。
1 2 3 4 5 |
IAnalyzer analyzer = CachedAnalyzerProxy.bind(new Analyzer()); Spark.get("/review_request_per_month_last_half_year", (req, res) -> { return analyzer.getRequestStatisticOfLastSixMonth(); }); |
Java自带的Dynamic Proxy API虽然有些限制,但使用起来还是非常简单的。听说还有更强大的CGLib,有机会现研究一下。😄