AOP 应用 性能
AOP
我的感觉是做些日志什么的比较好,比如在每个controller的api前后搞一下,或者做些metric。今天在spring里用了下AOP并简单的测了一下性能。
使用
业务类
public class DAOImpl {
public int access(int i) {
System.out.println("dao invoked.");
return i+1;
}
}
业务类的bean定义
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:annotation-config />
<bean id="DAO" class="foo.bar.DAOImpl"/>
</beans>
下面通过两种方式来配置使用AOP的功能,目的就是在业务类方法前后增加一些附加的过程。通过AOP方式我们不需要修改目标方法的代码,只需要定义附加的方法即可。
配置文件
定义advisor
public class BeforeAdvisor implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("before: " + method.getName());
}
}
public class AfterAdvisor implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("after: returning.");
}
}
<bean id="afterAd" class="advisers.AfterAdvisor"/>
<bean id="beforeAd" class="advisers.BeforeAdvisor"/>
<aop:config>
<aop:pointcut expression="execution(* foo.bar.DAOImpl.access(..))" id="dataAccess" />
<aop:advisor id="afa" pointcut-ref="dataAccess" advice-ref="afterAd" />
<aop:advisor id="bfa" pointcut-ref="dataAccess" advice-ref="beforeAd" />
</aop:config>
上述配置定义了切点,和相关的advisor,使用如下
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
DAOImpl dao = context.getBean(DAOImpl.class);
dao.access(1);
此时运行的话已经会有增加了附加方法的输出。
AspectJ方式
其实spring内部是依赖AspectJ的。因为只使用spring配置文件,而不使用AspjectJ注解的方式,如果没有添加AspectJ依赖,依然编译无法通过。提供spring配置文件方式的形式可能是考虑到对外接口的统一问题,spring不想直接在使用方式上依赖于第三方的模式。不过AspectJ注解形式显然比spring配置方式方便太多,因而spring又开放了注解配置的方式。这需要将应用了AspectJ注解的类作为一个bean定义,这样spring在读取bean定义时如果发现是个AspectJ定义bean那么就会执行相关的AOP代理生成工作,和读取配置文件其实大同小异。
定义AspectJ类
@Aspect
public class AspectTest {
@Pointcut("execution (* *.access(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("before access");
}
@After("test()")
public void afterTest() {
System.out.println("after access");
}
}
修改一下spring配置文件加入以下内容:
<aop:aspectj-autoproxy/>
<bean id="aspectjBean" class="aspectj.AspectTest"/>
然后再像先前那样运行即可使用。如果没有把前面的相关配置删除的话可以看到有两套advisor方法在作用,说明两种配置是可以共存的。
性能
在除去所有在DAOImpl和advisor中的输出语句后加入测试如下:
long t1 = System.nanoTime();
for (int i=0; i<1000000; i++) {
if (i+1 != dao.access(i)) {
System.out.println("error.");
}
}
long t2 = System.nanoTime();
System.out.println(TimeUnit.NANOSECONDS.toMillis(t2-t1));
在不使用AOP的情况下,调用一百万次时间在3~7ms左右。而在启用了AOP的情况下使用配置文件方式时在300ms左右而使用AspectJ时在700ms+,显然AOP实现造成了巨大的性能开销(不过想想还好,毕竟100w次,不过跟原来不用AOP比真是很挫)。
proxy-target-class
在spring配置文件的aop:config
和aop:aspectj-autoproxy
中都可以指定一个布尔参数proxy-target-class
,当其为true
时一般生成被代理实例的子类,为false
则采用动态代理的形式。
当我们使用AOP时,比如上面的例子在main函数中最后再输出一个dao对象的类名:
System.out.println(dao.getClass().getName());
System.out.println(dao.getClass().getSuperclass().getName());
会得到类似如下输出:
foo.bar.DAOImpl$$EnhancerBySpringCGLIB$$66d38c16
foo.bar.DAOImpl
所以在使用了AOP后在进行一些基于class的操作时要比较小心。AOP实现可以使用CGLIB生成代码产生新类也可以使用JDK动态代理。当像上面的例子那样被代理的类没有实现任何接口时只能使用CGLIB方式通过继承实现。而继承有有些固有的限制是不可避免的,比如不能继承final类(编译时直接报错),advisor不能final方法(编译不报错,但是调用时不会进行AOP动作,只是进行原来的调用)。这就是proxy-target-class
为true
时的效果。当其为false
时且类有实现特定的接口比如
// DAO.java
public interface DAO {
int access(int i);
}
// DAOImpl.java
public class DAOImpl implements DAO {
public final int access(int i) {
//System.out.println("dao invoked.");
return i+1;
}
}
则可以使用JDK代理(是默认情况),当使用JDK代理时再输出本类与子类的名称如下:
com.sun.proxy.$Proxy7
java.lang.reflect.Proxy
同时我们获取bean的方式也要对应的发生变化,不能通过具体类类型获取而需要以接口实现的接口类型获取:
DAO dao = context.getBean(DAO.class);
在这个例子中CGLIB只比JDK Proxy快了5%左右,并没有惊人的提高。
expose-proxy
从被代理的内部调用被AOP修饰的方法并不会应用AOP的相关advisor,比如将上述类接口与类定义修改为:
public interface DAO {
int access(int i);
void access(int i, int j);
}
public class DAOImpl implements DAO {
public int access(int i) {
System.out.println("dao invoked.");
return i+1;
}
public void access(int a, int b) {
}
}
当在access(int)内调用access(int, int)方法时并不会执行AOP的advisor不管是JDK代理还是CGLIB子类。如果需要再内部调用时也应用advisor则需要将参数expose-proxy
设置为true
。同时在方法内部通过以下方式获取含有advisor的对象进行方法调用:
((DAO)AopContext.currentProxy()).access(0, i)
AOP 应用 性能的更多相关文章
- AOP的实现原理
1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前修改字节码或字节码加载后动态创建代理类的字节码,以下是各种实现机制的比较. 类别 ...
- AOP的实现机制--转
原文地址:http://www.iteye.com/topic/1116696 1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前 ...
- NanoProfiler - 适合生产环境的性能监控类库 之 基本功能篇
背景 NanoProfiler是一个EF Learning Labs出品的免费性能监控类库(即将开源).它的思想和使用方式类似于MiniProfiler的.但是,设计理念有较大差异. MiniProf ...
- AOP的实现机制
1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前修改字节码或字节码加载后动态创建代理类的字节码,以下是各种实现机制的比较. 类别 ...
- 依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦(转good)
依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦.所谓横切关注点,即影响应用多处的功能,这些功能各个应用模块都需要,但又不是其主要关注点,常见 ...
- spring-petclinic性能调优实战(转)
1.spring-petclinic介绍 spring-petclinic是spring官方做的一个宠物商店,结合了spring和其他一些框架的最佳实践. 架构如下: 1)前端 Thymeleaf做H ...
- 转:AOP与JAVA动态代理
原文链接:AOP与JAVA动态代理 1.AOP的各种实现 AOP就是面向切面编程,我们可以从以下几个层面来实现AOP 在编译期修改源代码 在运行期字节码加载前修改字节码 在运行期字节码加载后动态创建代 ...
- AOP与JAVA动态代理
1.AOP的各种实现 AOP就是面向切面编程,我们可以从以下几个层面来实现AOP 在编译期修改源代码 在运行期字节码加载前修改字节码 在运行期字节码加载后动态创建代理类的字节码 2.AOP各种实现机制 ...
- AOP的核心:代理与织入
分为两步: 1.动态生成代理类: 2.织入: 2.6 织入(Weaving) 织入是将增强添加到目标的具体连接点上的过程 . AOP 织入方式: 方式 实现 应用编译期织入 特殊的 Java 编译器. ...
随机推荐
- 08_python_文件操作
一.初始文件操作 打开⽂件的⽅式: r, w, a, r+, w+, a+, rb, wb, ab, r+b, w+b, a+b 默认使⽤的是r(只读)模式 f = open("少妇嫩模.t ...
- VS2013 生成事件,删除不必要的DLL
解决方案中有一个 Project 是 Windows Service,用来从消息队列中取出事件,发送通知电邮: UI是一个MVC网站,两个Project都引用了同一个类库,这个类库引用了第三方的生成P ...
- Smart/400开发上手3: 练习实践
练习题 在2006年1月1日之前入职且在职的营销员,给予年资补贴2000元: 符合以上条件的,再按以下标准一次性发放职级补贴: 职级代码 简称 补偿金额 A1 AD 6000 B1 SBM 5000 ...
- 在MVC3中使用富文本编辑器:KindEditor的配置及上传图片
现在比较常用的富文本编辑挺多的,如ueditor.fckeditor.kingeditor等,本文主要介绍一下KindEditor的配置与使用. 先去官网http://www.kindsoft.net ...
- Windows server2012 IIs 8 自定义日志记录
问题: 通过CDN加速的网站,记录日志时无法追踪源IP,日志的IP都为CDN节点ip. 分析: 1.在解析记录header时,CDN实际会把源IP以其它header的形式回传,如网宿为[Cdn-Src ...
- vue中请求本地的json数据
为什么要请求本地的数据?模拟后台的请求数据,验证页面的逻辑是否存在问题,抛开后台提前开发等. 常用的说来有:jq的方式 约等于 axios的方式,vuex状态管理的方式 个人认为最好用的就是jq的方式 ...
- ADO.NET 4.5中的异步与流特性
.NET 4.5为仍在选择直接与DataReader系列类打交道的.NET开发人员带来了一些新的异步与流特性支持.SqlDataReader允许开发人员在减少一些便利性的基础上获得更好的性能.例如,该 ...
- Sharding-jdbc(一)分库分表理解
1.什么是分库分表 所谓的分库分表就是数据的分片(Sharding). 2.为什么需要分库分表 因为随着公司的业务越来越大,对于现成单机单个应用瓶颈问题,对数据持久化硬盘如何进行扩容. 可以从4个方面 ...
- 只用一招,让你Maven依赖下载速度快如闪电
一.背景 众所周知,Maven对于依赖的管理让我们程序员感觉爽的不要不要的,但是由于这货是国外出的,所以在我们从中央仓库下载依赖的时候,速度如蜗牛一般,让人不能忍,并且这也是大多数程序员都会遇到的问题 ...
- Nginx缓存配置之手动清除缓存
访问我的博客 前言 前文介绍了利用 nginx 的 nginx_ngx_cache_purge 模块来实现缓存功能,并设置了缓存时间为一天. 但是如果前端修改了页面,比如首页,由于 Nginx 缓存的 ...