注释嵌套注释
从Java 5开始,Java中出现了注释。 我想做一个自己的注释,只是为了看看需要什么。 但是,我发现它们只是接口。
有擦
接口后面没有牙。 必须执行一些代码。 我认为这是橡胶行之有效的方法,我真的找到了解决方法。
首先,我需要一个目标
我选择了一个最近的热门话题:缓存。 我不想实现JSR 109(JCache),但也不想做典型的“ Hello World”。 我选择实现两个注释,一个注释不带任何参数,另一个注释不带参数。 我还需要一个缓存提供程序。 如果我要这样做的话,还可以将真正的缓存库加入其中。 它还遵循我的设计理念,即使用产品/库来达成目标,而不是将所有内容都进行家庭纺。 经过仔细考虑,我选择了hazelcast作为我的缓存引擎。 它是市场上最快的,而且是免费的。
更多决定
在选择了目标之后,我仍然需要找出如何在它们后面扎牙的方法。 经过一番挖掘,我发现了两种方法:
反射
几乎每次使用反射时,我都会为编写这么笨拙的代码感到遗憾。 另外,要按照我想要的方式进行操作,我必须创建自己的框架。 听起来两个注解的工作量很大。
面向方面的编程(AOP)
这非常适合我想做的事。 AOP致力于将样板代码减少到一个地方。 这将很方便并且与缓存紧密结合,因为缓存可分为以下步骤:
- 检查此情况是否之前已完成。
- 如果是这样的话:
- 检索存储的结果
- 如果不:
- 运行功能
- 存储结果
- 返回结果
也许这过于简单,但总的来说是正确的。 就像所有事物一样,细节决定成败。
同时,回到AOP牧场
虽然我知道AOP是适合我的地方,但我对此并不了解。 我发现Spring有一个AOP库,而众所周知的库是AspectJ。 AspectJ对我不熟悉,需要运行时引擎才能工作。 我对Spring更加熟悉,所以选择了它。 在研究Spring的AOP时,我发现我必须深入研究AspectJ的注释,因此无论如何我还是以某种形式或方式被AspectJ所困。
新概念,新词汇
编写方面不像编写对象。 它们是对象,但并非如此,因此当然需要一组新的术语。 我使用的是Spring AOP文档中的内容
我确实需要阅读几次该页面才能理解所讲的内容。 强烈建议您执行相同的操作,否则其余帖子听起来像胡言乱语。
切入点的构成和建议
切入点设计很容易,因为我只对带有注释的方法感兴趣。 它需要的建议是周围的建议,因为如果已经进行了匹配的调用,我就需要能够避免调用该方法。
最后的代码
Maven Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darylmathison</groupId>
<artifactId>annotation-implementation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>4.2.4.RELEASE</spring.version>
</properties>
<description>
This project is an example of how to implement an annotation via Spring AOP.
</description>
<scm>
<url>https://github.com/darylmathison/annotation-implementation-example.git</url>
<connection>scm:git:https://github.com/darylmathison/annotation-implementation-example.git</connection>
<developerConnection>scm:git:git@github.com:darylmathison/annotation-implementation-example.git</developerConnection>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/darylmathison/annotation-implementation-example/issues</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.7</version>
<reportSets>
<reportSet>
<reports>
<report>dependencies</report>
<report>index</report>
<report>project-team</report>
<report>issue-tracking</report>
<report>scm</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.18.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<reportSets>
<reportSet>
<reports>
<report>javadoc</report>
<report>test-javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.5</version>
<configuration>
<linkJavadoc>true</linkJavadoc>
</configuration>
<reportSets>
<reportSet>
<reports>
<report>jxr</report>
<report>test-jxr</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-changelog-plugin</artifactId>
<version>2.3</version>
<configuration>
<type>range</type>
<range>90</range>
</configuration>
</plugin>
</plugins>
</reporting>
</project>
注释
快取
缓存注释的可爱名称,对吗?
package com.darylmathison.ai.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Daryl on 2/19/2016.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CacheMe {
}
CacheMeNow
package com.darylmathison.ai.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Daryl on 2/19/2016.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CacheMeNow {
String key();
}
弹簧配置
我决定使用基于Java的配置,而不是像通常为了改变速度而使用的XML。 EnableAspectJAutoProxy批注是使Spring AOP开始工作的关键。 我一直在我旁边,直到我读到有关这颗小宝石的这篇文章。 有时,这是一天中最容易燃烧的事情。
AppConfig
package com.darylmathison.ai.config;
import com.darylmathison.ai.cache.CacheAspect;
import com.darylmathison.ai.service.FibonacciService;
import com.darylmathison.ai.service.FibonacciServiceImpl;
import com.hazelcast.config.Config;
import com.hazelcast.config.EvictionPolicy;
import com.hazelcast.config.MapConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Daryl on 2/20/2016.
*/
@Configuration
@ComponentScan(basePackages = "com.darylmathison.ai")
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public Map<String, Object> cache() {
Config config = new Config();
MapConfig mapConfig = new MapConfig();
mapConfig.setEvictionPercentage(50);
mapConfig.setEvictionPolicy(EvictionPolicy.LFU);
mapConfig.setTimeToLiveSeconds(300);
Map<String, MapConfig> mapConfigMap = new HashMap<>();
mapConfigMap.put("cache", mapConfig);
config.setMapConfigs(mapConfigMap);
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
return instance.getMap("cache");
}
@Bean
public FibonacciService fibonacci() {
return new FibonacciServiceImpl();
}
@Bean
public CacheAspect cacheAspect() {
return new CacheAspect();
}
}
服务编号
基于经典Spring的设计需要服务吗? 由于Spring使用代理来实现其AOP,因此强烈建议为带注释的类定义一个接口以实现。
斐波那契服务
package com.darylmathison.ai.service;
/**
* Created by Daryl on 2/20/2016.
*/
public interface FibonacciService {
long calculate(int rounds);
long calculateWithKey(int rounds);
}
FibonacciServiceImpl
package com.darylmathison.ai.service;
import com.darylmathison.ai.annotation.CacheMe;
import com.darylmathison.ai.annotation.CacheMeNow;
/**
* Created by Daryl on 2/20/2016.
*/
public class FibonacciServiceImpl implements FibonacciService {
@Override
@CacheMe
public long calculate(int rounds) {
return sharedCalculate(rounds);
}
@Override
@CacheMeNow(key = "now")
public long calculateWithKey(int rounds) {
return sharedCalculate(rounds);
}
private static long sharedCalculate(int rounds) {
long[] lastTwo = new long[] {1, 1};
for(int i = 0; i < rounds; i++) {
long last = lastTwo[1];
lastTwo[1] = lastTwo[0] + lastTwo[1];
lastTwo[0] = last;
}
return lastTwo[1];
}
}
AOP的东西
这是注释实现的核心。 其他所有内容都可以用来支持后续的工作。
系统存档
根据Spring文档,集中化切入点定义是一个好主意。
package com.darylmathison.ai.cache;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
/**
* Created by Daryl on 2/20/2016.
*/
@Aspect
public class SystemArch {
@Pointcut("@annotation(com.darylmathison.ai.annotation.CacheMe)")
public void cacheMeCut() {
}
@Pointcut("@annotation(com.darylmathison.ai.annotation.CacheMeNow)")
public void cacheMeNowCut() {
}
}
缓存方面
周围注释使用切入点类的完整方法名称来定义建议的内容。 CacheMeNow批注的建议包括一个额外条件,因此可以定义批注,以便可以读取关键参数。 测试代码中揭示了CacheMeNow中的一个设计错误。
package com.darylmathison.ai.cache;
import com.darylmathison.ai.annotation.CacheMeNow;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
/**
* Created by Daryl on 2/20/2016.
*/
@Aspect
public class CacheAspect {
@Autowired
private Map<String, Object> cache;
@Around("com.darylmathison.ai.cache.SystemArch.cacheMeCut()")
public Object simpleCache(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
StringBuffer keyBuffer = new StringBuffer();
for(Object o: proceedingJoinPoint.getArgs()) {
keyBuffer.append(o.hashCode());
}
String key = keyBuffer.toString();
Object ret = cache.get(key);
if(ret == null) {
ret = proceedingJoinPoint.proceed();
cache.put(key, ret);
}
return ret;
}
@Around("com.darylmathison.ai.cache.SystemArch.cacheMeNowCut() && @annotation(cacheMeNow)")
public Object simpleCacheWithParam(ProceedingJoinPoint proceedingJoinPoint, CacheMeNow cacheMeNow) throws Throwable {
Object ret = cache.get(cacheMeNow.key());
if(ret == null) {
ret = proceedingJoinPoint.proceed();
cache.put(cacheMeNow.key(), ret);
}
return ret;
}
}
测试代码
显示注释确实引起缓存的驱动程序代码。
斐波那契检验
package com.darylmathison.ai.service;
import com.darylmathison.ai.config.AppConfig;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Created by Daryl on 2/20/2016.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
public class FibonacciTest {
private static final int ROUNDS = 12;
private static final long ANSWER = 377;
@Autowired
private FibonacciService fibonacci;
@org.junit.Test
public void testCalculate() throws Exception {
long start = System.currentTimeMillis();
Assert.assertEquals(ANSWER, fibonacci.calculate(ROUNDS));
long middle = System.currentTimeMillis();
Assert.assertEquals(ANSWER, fibonacci.calculate(ROUNDS));
long end = System.currentTimeMillis();
Assert.assertTrue((end - middle) < (middle - start));
}
@org.junit.Test
public void testCalculateWithKey() throws Exception {
Assert.assertEquals(ANSWER, fibonacci.calculateWithKey(ROUNDS));
// This test should not pass
Assert.assertEquals(ANSWER, fibonacci.calculateWithKey(13));
}
}
结论
注释不必很难实现。 使用AOP编程,我可以用很少的代码来实现两个注释。
翻译自: https://www.javacodegeeks.com/2016/03/diy-annotations-3.html
注释嵌套注释