`

关于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,这个我也用多了,呵呵,感觉你还不是很理解我想要的实现思路,始终说不到点子上。

相关推荐

    nutz使用手册

    Nutz 可以做什么? * Dao -- 针对 JDBC 的薄封装,事务模板,无缓存 * Ioc -- JSON 风格的配置文件,声明时切片支持 ...* Aop -- 轻便快速的切面编程支持 * Plugin -- 轻便的插件机制 * Resource -- 资源扫描

    nutz-1.b.52.zip

    如果一个 Web 应用,你在 WEB-INF/lib 下只 需要放置一个 nutz.jar 就够了 当然你要使用连接池,数据库驱动等功能,还需要自行添置 jar 包。 -------------Nutz 为谁而设计? 如果你觉得 Hibernate 控制比较繁琐,...

    springboot注解+aop实现接口限流

    springboot框架中使用自定义注解,配合切面实现接口限流,增加ip黑名单功能,可实现ip+账号+接口进黑名单,也可以实现账号+ip进黑名单及禁用账号; 可以学会自定义注解使用、自定义响应码枚举及使用、自定义异常类及...

    容灾项目从入门到实战视频.rar

    │ 104-在切面中获取自定义注解-1.mp4 │ 105-完善通知类-1.mp4 │ 106-druid监控中心的配置-1.mp4 │ 11-逻辑语句-1.mp4 │ 12-计算属性-1.mp4 │ 13-使用class绑定样式-1.mp4 │ 14-使用style绑定样式-1.mp4 │ 15...

    JAVA单例模式源码-goaop-laravel-bridge:Go的集成桥接器!AOP框架和Laravel

    是一个特殊的类,它结合了切入点和通知,每个切入点被定义为一个注解,每个通知是这个切面内部的一个方法。 您可以在不同的来源阅读更多关于 AOP 的信息,有关于 Java 语言的好文章,它们也可以应用于 PHP,因为它是...

    商品秒杀系统(限时抢购系统)

    切面使用 设计模式使用 事物、回滚使用 docker、nginx使用 图片服务器OSS使用 stram、lambda使用 多线程、线程池使用 定时任务使用 短信验证、邮件服务使用 JWT验证TOKEN令牌使用 雪花算法分布式算法使用 拦截器、...

    springboot部署系统(自动化部署+缓存管理+业务降级+应用监控).zip

    Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供...

    SpringBoot集成常用开发中间件,分库分表,缓存,消息队列,定时器,权限管理等组件.zip

    Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供...

    Oauth2-SSO:使用Oauth2实现的SSO单点登录登出,模拟微信QQ授权码模式授权资源访问

    每个系统实现切面aspect注解添加到控制器上面,每个请求根据当前系统的用户名去纠正Redis里面有没有存入相同的用户名,存入的话说明当前用户在别的系统退出了,那么清除当前访问项目的局部会话cookie。 将JWT方式的...

    Spring.3.x企业应用开发实战(完整版).part2

    7.10.2 使用LTW织入一个切面 7.10.3 在Tomcat下的配置 7.10.4 在其他Web应用服务器下的配置 7.11 小结 第3篇 数据访问 第8章 Spring对DAO的支持 8.1 Spring的DAO理念 8.2 统一的异常体系 8.2.1 Spring的DAO异常体系...

    Spring3.x企业应用开发实战(完整版) part1

    7.10.2 使用LTW织入一个切面 7.10.3 在Tomcat下的配置 7.10.4 在其他Web应用服务器下的配置 7.11 小结 第3篇 数据访问 第8章 Spring对DAO的支持 8.1 Spring的DAO理念 8.2 统一的异常体系 8.2.1 Spring的DAO异常体系...

    oxygen:一个轻量级Java框架,包含ioc,aop,config,cache,job,Jdbc,web等

    氧 轻量级Java框架 介绍 一个轻量级Java框架 氧核 ...可使用注解Aspect或直接实现Interceptor编写切面 部分工具类 氧气瓶 小巧简单的jdbc实现,纯jdk实现,无第三方jar 支持多数据源 基于sql进行cru

    SpringBoot2 基础教程,日志配置,数据源配置,事务管理等

    配置AOP切面编程,解决日志记录业务。基于转账案例,演示事务管理操作。基于Cache注解模式,管理Redis缓存。基于Yml配置的方式,实现文件上传逻辑。配置MongoDB数据库,实现增删改查操作。配置Actuator组件,实现系统监控...

    商品秒杀系统(限时抢购系统),包含商城展示与后台管理(毕业设计)

    mybatis-plus逆向生成 其中融入短信通知(榛子云)、邮件通知(boot-starter-mail)、短信验证(kaptcha)、一二级缓存(JetCache)、跨域登录(jjwt)、数据库连接(druid),还包括自定义注解、切面编程等相关东西...

    spring chm文档

    6.3.1. 声明一个切面 6.3.2. 声明一个切入点 6.3.3. 声明通知 6.3.4. 引入 6.3.5. 切面实例化模型 6.3.6. Advisors 6.3.7. 例子 6.4. AOP声明风格的选择 6.4.1. Spring AOP还是完全用AspectJ? 6.4.2. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.3.1. 切入点指定者的支持 6.2.3.2. 合并切入点表达式 6.2.3.3. 共享常见的切入点(pointcut)定义 6.2.3.4. 示例 6.2.4. 声明通知 6.2.4.1. 前置通知...

    Spring in Action(第二版 中文高清版).part2

    7.6.1 创建一个安全切面 7.6.2 使用元数据保护方法 7.7 小结 第8章 Spring和基于POJO的远程服务 8.1 Spring远程调用概览 8.2 与RMI一起工作 8.2.1 连接RMI服务 8.2.2 输出RMI服务 8.3 使用Hessian和Burlap...

    Spring in Action(第二版 中文高清版).part1

    7.6.1 创建一个安全切面 7.6.2 使用元数据保护方法 7.7 小结 第8章 Spring和基于POJO的远程服务 8.1 Spring远程调用概览 8.2 与RMI一起工作 8.2.1 连接RMI服务 8.2.2 输出RMI服务 8.3 使用Hessian和Burlap...

    Spring框架:深入解析、心得分享与实战应用

    通过依赖注入(DI)和面向切面编程(AOP)的核心特性,Spring帮助开发者实现业务逻辑与底层技术的解耦,从而简化了企业级应用的开发过程。 在Spring框架中,开发者可以通过配置文件或注解的方式,定义和管理应用...

    Spring in Action(第2版)中文版

    7.6.1创建一个安全切面 7.6.2使用元数据保护方法 7.7小结 第8章spring和基于pojo的远程服务 8.1spring远程调用概览 8.2与rmi一起工作 8.2.1连接rmi服务 8.2.2输出rmi服务 8.3使用hessian和burlap的远程调用 ...

Global site tag (gtag.js) - Google Analytics