`

关于JAVA注解的一个梦想--使用注解实现缓存切面

    博客分类:
  • JDK
阅读更多
最近在做一个项目的时候,用到了业务自已的缓存,发现有很多这样的代码:
public List<User> findUsers(String userName){
Object result = getFromCache(createKey(userName));
if(result!=null){
     return result;
}
.....
result = userDAO.findData(userName);
putToCache(createKey(userName));
return result;
}

这几行是非常死的东西,除了key之外,到处都是一样,于是想到用SPRING的AOP注解来寻求解决方法,同时看了一些有关注解的文章,发现很难在AOP中取得所需要的KEY,因为生成KEY的方法基本都不一样,目标方法参数的个数和未知的类型也导致难以统一生成KEY。自定义注解也不能直接实现在方法的前后进行拦截,下面是我没有实现的想法:
private String createKey(String userName){
       return "user_List_Cache"+userName;
}

//注解的第一个参数是上面的方法,第二参数调用那个方法所需要的参数
//注解现在没有传递方法,也没法传递运行时的值--据我所知
@cache(keyMthod=createKey,keyPara=userName);
public List<User> findUsers(String userName){
//注解帮我在方法的前后进行调用注解类里的方法,我在注解类里写那死代码,
//这样这里就只需要写查询的逻辑,从数据库返回结果就行
......
return userDAO.findData(userName);
}


我的梦想:java注解类可以有方法实现,当上面的findUsers被执行时,注解类的某个方法会在方法执行前被调用,另一个方法会在之后被调用,且能方便的从当前运行环境中取得所需的对象和值。

提两个问题

1、我最上面的那段代码各位有什么更好的实现吗?请赐教
2、JAVA其实要支持这种注解也不难,为什么注解不做更强大一些,还是本来就可以,是我没发现?

补充一点,感谢很多朋友发表自已的意见,但有不少的朋友忽略我不能违背的一些条件:
1、使用SPRING的AOP意见,请考虑在类类内部调用时如何处理。
2、createKey方法的名字和参数不是固定的,每个业务类都可能不一样,而且有可能一个类有多个这样的方法。



欢迎讨论,但不要人身攻击......

###############10年以后更新#############2021-01-15 20:35
没想到,10年以后我真的还在写代码,偶然想起来当年有这样的想法还没有实现,今天这个实现方案,应该早在当年就具备外部条件了,奈何没有发现,今天偶然想起,果然只用了半天实现了,当年可能不具备的条件是,实际运行时可能取不到被拦截方法的真实参数名,在JDK1.8以前,可能只能取到arg0,arg1...这种,但不是必须的,有则更好而已
@LocalCache(keyPaths = {"system","bizType"})
public Object queryConfigs(String system, String bizType) {
       // 这里只管查数据库或者调远程读接口,随意。。。
       return queryDB()....
}

主要思想这里说下:
1、基于LocalCache注解先实现一个AOP切面;
2、切面执行时,可以取出注解的配置,这里这个配置就是被拦截方法的参数名,然后根据名称,取得拦截方法的运行时参数值,拼装成key,然后去取缓存,
3、如果取到,则返回,不再调用被拦截方法,如果取不到,则调用被拦截方法,并且把结果放入缓存。
分享到:
评论
25 楼 ych19850810 2009-11-01  
是啊 两者有可比性吗?
24 楼 lgdlgd 2009-10-26  
呵呵,我是想JDK下一版能直接支持AOP注解,在进行方法内联时直接嵌入注解类中定义的拦截方法...
23 楼 whaosoft 2009-10-26  
aspjectJ 挺不错,比自己写注解强
22 楼 mxswl 2009-10-26  
楼主也就是想用动态代理+Annotation为某个方法调用做增强吧?

至于调用效率,直接调用和反射调用的效率差距肯定是有的.

直接调用的话.很多信息,比如调哪一个虚函数,方法参数类型等在编译的时候就已经确定了.
而使用 动态代理+Annotation 则把确定这些信息的逻辑移到了运行期(比如需要根据方法名搜索出对应的Method实例),效率自然会变低.


21 楼 ray_linn 2009-10-25  
一粒蛋 写道
你这个需要,可能用动态代理比 annotation 更容易 ……

继续跑题 …… 一次定义完所有 find_xxxs :(可能对于没接触过 ruby 的有点不好懂)
def method_missing(method, *params)
  if method.to_s =~ /find_(\w+)s/
    cache[params] ||= const_get($1.capitalize).find(*params) # params 可多个
  else
    raise 'method not found'
  end
end


或者
def self.cached *methods
  @old_m ||= {}
  methods.each {|m|
    @old_m[m] = method(m)
    define_method(m){|params|
      cache[params] ||= old_m[params]
    }
  }
end
# 现在可以方便的用一行定义哪些要 cache,哪些不要 cache:
cached :find_users, :find_roles


这东西不就是等于反射么....付出效率而已。
20 楼 srdrm 2009-10-25  
我觉得这种用aspjectJ最好,当然运行时不能再改了,能避免从内部调用不取cache的问题,性能还好
19 楼 condeywadl 2009-10-25  
那个注解特性也算是一个难得的好改进了
18 楼 rainv 2009-10-25  
如果只有一个类只有一个这样的方法的话可以用
模板方法来处理可以吧。

父类

public List<T> find(Object... params){
       beforeFind();
       doFind();
       afterFind();
}
//子类实现即可
public List<T> doFind(Object... params){}

17 楼 lgdlgd 2009-10-25  
wxq276 写道
我在前几天写程序是遇到了这样一种情况,JVM内存不足的异常,我用了大概五个线程读取一个40M左右的文件时报的错,在网上查了一下,说JVM默认的内存空间是64M,俺对这个JVM也是不太了解,哪位仁兄给推荐一本专门针对JVM的书看看也好啊

英文的话可以直接到sun网站看,中文:深入java虚拟机第二版,这个我才看了几章,东西比较老,不过有不少的东西还是有用的,翻译还算可以吧,文中把父类叫双亲,好久才缓过神来。中文更新的没找到。
16 楼 lgdlgd 2009-10-24  
sword.cai 写道
引用

凤舞凰扬 说得不错,我就是生成KEY的方法不确定,方法名和所需要的参数都不是确定的,所以才想将方法名和参数传到注解中...
sword.cai 说的意思是不同的方法要用不能的代理来拦截吧,这样不太好,如果为每一个差异都做一个这样的AOP,还不如直接在本方法里面写了,对吧?


上面没有说清楚,这里不是为每个dao做一个aop,应该 是为每个dao生成策略做一个aop,

可以通过ProceedingJoinPoint pjp.getTarget()不同来做key,这个target对为个dao应该肯定不同吧,而key生成策略就是根据传过来的username以及Target的通过何种方式处理了。这个方式有几个,就有几个advice

sword.cai说的办法还是比较可行的,可以通过给所有需要缓存操作的业务类定义一个统一的生成KEY的接口,参数是可变数组,如createKey(Object ...),这样在AOP中将所在参数传回给target.createKey()方法即可,但为什么我说比较可行呢,因为我考虑到不是所有的调用都来自于类外部,有一些可能来自于类内部方法的直接调用,这时AOP感觉不到了,这是其一;还有,有些业务类比较复杂,内部需要那几种缓存的KEY,这时代码可能就是首先一堆的if else 的类型判断了,感觉有些乱。
15 楼 sword.cai 2009-10-24  
引用

凤舞凰扬 说得不错,我就是生成KEY的方法不确定,方法名和所需要的参数都不是确定的,所以才想将方法名和参数传到注解中...
sword.cai 说的意思是不同的方法要用不能的代理来拦截吧,这样不太好,如果为每一个差异都做一个这样的AOP,还不如直接在本方法里面写了,对吧?


上面没有说清楚,这里不是为每个dao做一个aop,应该 是为每个dao生成策略做一个aop,

可以通过ProceedingJoinPoint pjp.getTarget()不同来做key,这个target对为个dao应该肯定不同吧,而key生成策略就是根据传过来的username以及Target的通过何种方式处理了。这个方式有几个,就有几个advice
14 楼 lgdlgd 2009-10-24  
sword.cai 写道
private String createKey(String userName){  
       return "user_List_Cache"+userName;  
}  
  
//注解的第一个参数是上面的方法,第二参数调用那个方法所需要的参数  
//注解现在没有传递方法,也没法传递运行时的值--据我所知  
@cache(keyMthod=createKey,keyPara=userName);  
public List<User> findUsers(String userName){  
//注解帮我在方法的前后进行调用注解类里的方法,我在注解类里写那死代码,  
//这样这里就只需要写查询的逻辑,从数据库返回结果就行  
......  
return userDAO.findData(userName);  
}  

这个在spring 2.5里是可以实现的
简单实现:(英文不乍样,一些类名,及方法名拼写可能有错,相当于伪码吧)

先根据createKey方法不同,定义N个不同的annotation, 如:cacheAnnotation1, cacheAnnotation2....

新建N个aspect
@aspect
@component
class Advice{
@Resource *** cache  
@around("@annotation(anno) && args(username)")
public Object daoAroundAspect(ProceedJoinPoint pjp, String username, cacheAnnotation1 anno){
          //这里根据 anno不同,以及username生成一个key
        object = cache.get(key)
        if(object == null) object = pjp.proceed();
         
        if(object != null) cache.save(object);
        return object;
  }
}


凤舞凰扬 说得不错,我就是生成KEY的方法不确定,方法名和所需要的参数都不是确定的,所以才想将方法名和参数传到注解中...
sword.cai 说的意思是不同的方法要用不能的代理来拦截吧,这样不太好,如果为每一个差异都做一个这样的AOP,还不如直接在本方法里面写了,对吧?
13 楼 sword.cai 2009-10-24  
private String createKey(String userName){  
       return "user_List_Cache"+userName;  
}  
  
//注解的第一个参数是上面的方法,第二参数调用那个方法所需要的参数  
//注解现在没有传递方法,也没法传递运行时的值--据我所知  
@cache(keyMthod=createKey,keyPara=userName);  
public List<User> findUsers(String userName){  
//注解帮我在方法的前后进行调用注解类里的方法,我在注解类里写那死代码,  
//这样这里就只需要写查询的逻辑,从数据库返回结果就行  
......  
return userDAO.findData(userName);  
}  

这个在spring 2.5里是可以实现的
简单实现:(英文不乍样,一些类名,及方法名拼写可能有错,相当于伪码吧)

先根据createKey方法不同,定义N个不同的annotation, 如:cacheAnnotation1, cacheAnnotation2....

新建N个aspect
@aspect
@component
class Advice{
@Resource *** cache  
@around("@annotation(anno) && args(username)")
public Object daoAroundAspect(ProceedJoinPoint pjp, String username, cacheAnnotation1 anno){
          //这里根据 anno不同,以及username生成一个key
        object = cache.get(key)
        if(object == null) object = pjp.proceed();
         
        if(object != null) cache.save(object);
        return object;
  }
}

12 楼 wxq276 2009-10-24  
我在前几天写程序是遇到了这样一种情况,JVM内存不足的异常,我用了大概五个线程读取一个40M左右的文件时报的错,在网上查了一下,说JVM默认的内存空间是64M,俺对这个JVM也是不太了解,哪位仁兄给推荐一本专门针对JVM的书看看也好啊
11 楼 murainwood 2009-10-24  
楼主这个梦想,是可以在Spring里用AspectJ来实现滴。
而且基本上是无侵入的
10 楼 凤舞凰扬 2009-10-24  
引用
这几行是非常死的东西,除了key之外,到处都是一样,于是想到用SPRING的AOP注解来寻求解决方法,同时看了一些有关注解的文章,发现很难在 AOP中取得所需要的KEY,因为生成KEY的方法基本都不一样,目标方法参数的个数和未知的类型也导致难以统一生成KEY。自定义注解也不能直接实现在方法的前后进行拦截

   其实楼主的这个想法蛮不错的,不过有几个必须的前提,第一是生成key的行为描述(也就是方法)是已知的,包括参数个数、类型以及更重要的数据源(也就是每个参数的实际值是如何获取)等。如果上述问题解决(比如通过静态的键值生成方法或者当前服务类实现某个特殊的接口,并且参数可以识别,比如是对象类型加上对象id,每个对象有自己唯一识别的id等),那么完全可以结合annotation以及Aop在加上一点反射的应用,可以实现楼上的想法的。
9 楼 treblesoftware 2009-10-24  
撤哪去了。好好的JAVA主题,非要用个

def 

end
8 楼 lgdlgd 2009-10-24  
一粒蛋 写道
感觉…… 你已经打定主意了,并且认为自己的写法最好,发出来只是想寻求应声附和的……

其实我是想看到一些深入理JVM的人讲解一下,如果JDK要支持我这种做法,会不会很有难度,在方法连接时会不会有性能问题等,因为本人对JVM理解很浅薄。。。
7 楼 一粒蛋 2009-10-24  
感觉…… 你已经打定主意了,并且认为自己的写法最好,发出来只是想寻求应声附和的……
6 楼 lgdlgd 2009-10-24  
一粒蛋 写道
因为出现的次数多,所以能省很多行。
老实说如果一共只省一两行,你改成 annotation 就是蛋疼。(啊!)

另外,万一写完发现更长更耗时间更不好改呢?……
我觉得批量替换更 KISS ……

/*xyz821*/Object result = getFromCache(key);  
if(result!=null){  
  return result;  
}


注:添上的注释 /*xyz821*/ 内容自定,作用是可以使批量替换不会误杀 …… 非常 stupid,不过很有效 ……

另外一个思路就是 AOP getFromCache(),(有时还能让编译器自动把 234 行的判断优化掉)。
不过我不喜欢 AOP,不想告诉你。

你建议我批量替换以前的代码?疯了...呵呵
AOP,这个我也用多了,呵呵,感觉你还不是很理解我想要的实现思路,始终说不到点子上。

相关推荐

    Spring基于注解的缓存配置--web应用实例

    在本实例中,我们将深入探讨如何在Spring框架中利用注解来实现缓存配置,特别是在Web应用程序中的实际应用。Spring Cache是一个强大的功能,它允许我们高效地管理应用程序中的数据,减少不必要的数据库查询,提高...

    Java 使用注解拼接SQL语句

    "Java使用注解拼接SQL语句"是一个常见的实践,它使得动态构建SQL查询变得更为简洁和可维护。这种技术通常与ORM(对象关系映射)框架如MyBatis或Hibernate结合使用,但也可以通过自定义处理逻辑实现。 1. **自定义...

    自定义注解实现缓存机制

    创建一个类,比如`CacheAspect`,并使用`@Aspect`和`@Component`注解标记为切面组件。在这个切面中,我们将处理`@CacheResult`注解的方法调用,实现缓存逻辑: ```java import org.aspectj.lang....

    aop切面注解-限流与防止重复提交-2.docx

    AOP 切面注解 - 限流与防止重复提交是基于 AOP 编程范式实现的,使用了 FrequentlyRepetition 注解和 FrequentlyRepetitionAspect 切面。该实现可以防止重复提交和限流,提高系统的安全性和性能。

    spring4基于java注解事例

    在本教程中,我们将深入探讨如何使用Spring 4框架,特别是其基于Java注解的配置方式,来构建一个高效、可维护的系统。Spring 4是Java企业级应用开发的首选框架,它提供了广泛的功能,包括依赖注入、AOP(面向切面...

    SpringBoot使用注解实现 Redis 数据库的切换.zip

    在现代的Web应用程序开发中,SpringBoot框架因其简化配置、快速启动和强大的功能而...在实际开发中,还可以结合Spring的其他特性,如AOP(面向切面编程)来实现更复杂的逻辑,比如在特定的请求条件下自动切换数据库。

    spring整合redis做缓存用注解实现的完整例子

    Redis是一种高性能的键值数据存储系统,常被用于缓存、消息队列等多种场景,而Spring框架则为Java开发者提供了强大的依赖注入和面向切面编程功能,使得整合Redis变得轻松便捷。 首先,我们需要在项目中引入Spring ...

    Java自定义注解实现Redis自动缓存的方法

    Java自定义注解实现Redis自动缓存的方法是指在Java应用程序中使用自定义注解来实现Redis自动缓存的功能。该方法可以在实际开发中节省重复劳动,提高开发效率。 首先,需要创建一个自定义注解@RedisCache,该注解将...

    Spring 与Ehcache实现基于方法的缓存

    在IT行业中,Spring框架是Java领域最广泛应用的轻量级框架之一,它为开发者提供了强大的依赖注入(DI)和面向切面编程(AOP)功能。Ehcache则是一款广泛使用的开源缓存解决方案,用于提高应用程序性能,减少数据库...

    springboot实现消息转换、切面AOP和Redis缓存

    在SpringBoot中,你可以使用`@Aspect`注解定义一个切面,`@Before`, `@After`, `@Around`, `@AfterReturning`, `@AfterThrowing`定义不同的通知类型。 下面是一个简单的日志记录切面示例: ```java @Aspect @...

    SpringBoot AOP各种注解、自定义注解、鉴权使用案例(免费下载)

    SpringBoot AOP,即面向切面编程,是Spring框架中的一个重要特性,用于实现代码的横切关注点,如日志记录、事务管理、权限验证等。AOP通过使用代理模式,将这些关注点与核心业务逻辑分离,使得代码更加模块化,更...

    java注解annotation.rar

    5. AOP(面向切面编程):如Spring的`@Aspect`、`@Before`、`@After`等用于定义切面和通知。 总结,Java注解是一种强大的工具,它增强了代码的可读性,简化了配置,并且通过元数据提供了额外的信息层,使得开发过程...

    java项目开发实战--使用ssm框架开发众筹网站.rar

    1. **Spring框架**:Spring是一个开源的应用框架,提供IOC(Inversion of Control)容器来管理对象的生命周期和依赖关系,以及AOP(Aspect-Oriented Programming)用于实现切面编程,增强代码的可维护性和解耦性。...

    深入体验Java+Web开发内幕-高级特性-高清扫描版

    - **Spring框架**:涵盖AOP(面向切面编程)、IoC(控制反转)、事务管理等功能,是Java企业级应用的基石。 - **MyBatis**或Hibernate:持久层框架,简化数据库操作,提供对象关系映射功能。 - **Maven或Gradle**...

    JavaEE spring半自动实现AOP代理

    - 使用`@Aspect`注解来声明一个切面类,切面类中定义了`@Before`、`@After`、`@Around`、`@AfterReturning`和`@AfterThrowing`等通知方法,它们分别代表前置、后置、环绕、返回后和异常后执行的逻辑。 - 使用`@...

    使用spring aop对web 应用数据进行memcached缓存

    - 创建自定义切面,实现缓存逻辑,如使用`@Around`注解编写拦截器。 - 在需要缓存的方法上添加`@Cacheable`等注解。 5. **注意事项**: - 缓存一致性问题:当多个节点同时修改同一数据时,需要确保最终一致性。 ...

    【Java核心知识面试】-阿里Java面试集锦.zip

    - AOP(面向切面编程):讲解切面、通知、代理等概念,以及在Spring中的实现。 9. **数据库** - SQL语言:掌握基本的查询、增删改操作,以及高级特性如索引、事务、存储过程。 - JDBC:了解数据库连接、执行SQL...

    深入体验Java+Web开发内幕-高级特性.pdf 高级的 张孝祥 的第二本好书源码

    - **Spring框架**:提供依赖注入、AOP(面向切面编程)、事务管理等功能,是Java企业级应用的基石。 - **Spring MVC**:Spring的Web MVC模块,用于构建基于HTTP的应用,提供RESTful API设计。 - **Struts2或...

    spring注解方式实现aop

    在Spring中,我们可以通过创建一个类并使用`@Aspect`注解来定义切面。例如: ```java @Aspect public class LoggingAspect { // ... } ``` 2. **编写通知(Advice)** 通知是在特定连接点上执行的代码,例如...

    java web高级编程--英文高清版

    根据提供的文件信息,我们可以推断出这是一本关于Java Web应用程序的专业书籍,作者为Nicholas S. Williams,并由John Wiley & Sons, Inc.出版。本书的全称是《Professional Java® for Web Applications》。虽然...

Global site tag (gtag.js) - Google Analytics