- 浏览: 307014 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
it_like:
请问:x-forwarded-for 是什么时候加入HTTP头 ...
获取当前访问客户端的真实IP地址(含内网地址) -
bluend1004:
mark下~~~谢谢分享喽~~
PL/SQL快捷键设置 -
hastune:
统计一下典型就好了比如取得map的值${user.values ...
EL表达式 EL
[转载]用 AOP 来记录每个方法的执行时间
有时候我们要跟踪方法的执行时间,来观察系统的性能、时间分布。特别是要找出那些十分耗时的操作。如果是在每个方法中起始和结束位置记下时间相减,那是不太现实的,对代码的侵入性太过份,而且在产品环境中又得屏闭那部份代码。
幸好现在有了 AOP,通过配置方式再加上外部辅助代码就能达到我们的要求,正式上线时只需要简单改个配置项拆卸下来即可。
下面介绍三种方式来打印每个方法的执行时间,分别是:
1. Spring 2.0 用 AspectJ 实现 AOP
2. Spring 通用的方法拦截
3. 直接用 AspectJ 实现
1. Spring 2.0 用 AspectJ 实现 AOP
这个实例由五个文件构成,两个配置文件和三个类文件。需要在项目中引用 Spring 2.0 以上版本的相关包,还要日志包。
1) log4j.properties 放在 src 目录下
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%5p] %c{1}.%M(%L) %n%m%n
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%5p] %c{1}.%M(%L) %n%m%n
2) applicationContext.xml 放在 src 目录下
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.spridngframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:config>
<!-- Spring 2.0 可以用 AspectJ 的语法定义 Pointcut,这里拦截 service 包中的所有方法 -->
<aop:advisor id="methodTimeLog" advice-ref="methodTimeAdvice" pointcut="execution(* *..service..*(..))"/>
</aop:config>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloService" class="com.unmi.service.HelloService"/>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.spridngframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:config>
<!-- Spring 2.0 可以用 AspectJ 的语法定义 Pointcut,这里拦截 service 包中的所有方法 -->
<aop:advisor id="methodTimeLog" advice-ref="methodTimeAdvice" pointcut="execution(* *..service..*(..))"/>
</aop:config>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloService" class="com.unmi.service.HelloService"/>
</beans>
3) MethodTimeAdvice.java 记录时间的类
package com.unmi.util;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public class MethodTimeAdvice implements MethodInterceptor {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
/**
* 拦截要执行的目标方法
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = invocation.proceed();
clock.stop(); //计时结束
//方法参数类型,转换成简单类型
Class[] params = invocation.getMethod().getParameterTypes();
String[] simpleParams = new String[params.length];
for (int i = 0; i < params.length; i++) {
simpleParams[i] = params[i].getSimpleName();
}
log.debug("Takes:" + clock.getTime() + " ms ["
+ invocation.getThis().getClass().getName() + "."
+ invocation.getMethod().getName() + "("+StringUtils.join(simpleParams,",")+")] ");
return result;
}
}
package com.unmi.util;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public class MethodTimeAdvice implements MethodInterceptor {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
/**
* 拦截要执行的目标方法
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = invocation.proceed();
clock.stop(); //计时结束
//方法参数类型,转换成简单类型
Class[] params = invocation.getMethod().getParameterTypes();
String[] simpleParams = new String[params.length];
for (int i = 0; i < params.length; i++) {
simpleParams[i] = params[i].getSimpleName();
}
log.debug("Takes:" + clock.getTime() + " ms ["
+ invocation.getThis().getClass().getName() + "."
+ invocation.getMethod().getName() + "("+StringUtils.join(simpleParams,",")+")] ");
return result;
}
}
4) HelloService.java 被拦截的业务类
package com.unmi.service;
/**
* @author Unmi
*/
public class HelloService {
public void sayHello(int id,String name){
try {
Thread.sleep(512);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello "+name+"("+id+")");
}
}
package com.unmi.service;
/**
* @author Unmi
*/
public class HelloService {
public void sayHello(int id,String name){
try {
Thread.sleep(512);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello "+name+"("+id+")");
}
}
5) Main.java 主程序类
package com.unmi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
BeanFactory factory =new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService = (HelloService)factory.getBean("helloService");
helloService.sayHello(1,"Unmi");
}
}
package com.unmi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
BeanFactory factory =new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService = (HelloService)factory.getBean("helloService");
helloService.sayHello(1,"Unmi");
}
}
执行 Main 后输出的结果是:
Hello Unmi(1)
2008-01-18 13:41:25,593 [DEBUG] MethodTimeAdvice.invoke(34)
Takes:516 ms [com.unmi.service.HelloService.sayHello(int,String)]
如果不需要这个功能,只要在 applicationContext.xml 中把 id="methodTimeLog" 和 id="methodTimeAdvice" 配置项注释掉就行了。
2. Spring 通用的方法拦截
Spring 1.x 因为不能用 AspectJ 来对方法进行拦截,要用到 ProxyFactoryBean 使用 AOP,具体操作方法只需要更换以上例子中的 applicationContext.xml 文件就行,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloServiceTarget" class="com.unmi.service.HelloService"/>
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice"/>
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<bean id="helloService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="helloServiceTarget"/>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloServiceTarget" class="com.unmi.service.HelloService"/>
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice"/>
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<bean id="helloService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="helloServiceTarget"/>
</property>
</bean>
</beans>
上面的配置方式需为每个应用方法执行时间记录的 Bean 在外层包一个 ProxyFactoryBean,原来的 Bean 设为一个 Target 实在时麻烦了。
下面用一种应用自动代理的配置方式,指定 BeanNameAutoProxyCreator 的 beanNames 匹配模式即可,如果写成 <value>*Service,*Manager</value>,逗号分隔开,以 Service 或 Manager 结层类的方法都被拦截,这样方便许多。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice" />
<bean id="helloService" class="com.unmi.service.HelloService" />
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice" />
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<!-- 根据 Bean 的名字自动实现代理拦截 -->
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="beanNames">
<list>
<!-- 添加到其中的 Bean 自动就被代理拦截了 -->
<value>*Service</value>
</list>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice" />
<bean id="helloService" class="com.unmi.service.HelloService" />
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice" />
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<!-- 根据 Bean 的名字自动实现代理拦截 -->
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="beanNames">
<list>
<!-- 添加到其中的 Bean 自动就被代理拦截了 -->
<value>*Service</value>
</list>
</property>
</bean>
</beans>
3. 直接用 AspectJ 实现
AspectJ 提供了一套语法来定义切面,Spring 2.0 开始引入了 AspectJ 的部分功能,但如果要用上 AspectJ 更多强大完善的功能,诸如实例构造时,属性被访问时,动态改变类定义,滋生新的属性、方法,更强基于流程的控制时,恐怕非得显式的使用 AspectJ。当然,首先你得去 http://www.eclipse.org/aspectj/ 下载到 AspectJ 或者 AspectJ 的 Eclipse 插件。本人强烈建议使用 AspectJ 的 Eclipse 插件,请下载相应 Eclipse 版本的 AspectJ 插件,Eclipse 3.3 要搭配 AJDT: The AspectJ Development Tools 1.5。不要插件的话就得命令行下 ajc 编译你的方面,不容易习惯的。
AJDT 安装成功后,你可以新建 AspectJ 项目,或者把前面项目转换成 AspectJ 项目。从项目的上下文菜单中可以看到 AspectJ Tools -> Convert to Aspectj Project。然后在包 com.unmi.util 中新建一个方面,包 com.unmi.util 的上下文菜单中 New -> Aspect,输入名字 MethodTimeAdviceRecipe,然后 Finish,这时就会在包 com.unmi.util 中产生一个文件 MethodTimeAdviceRecipe.aj,内容如下:
MethodTimeAdviceRecipe.aj (那么 MethodTimeAdvice.java 就派不上用场了,可删去)
package com.unmi.util;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public aspect MethodTimeAdviceRecipe {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
//拦截所有以 Service 结尾类的方法
pointcut callServicePointCut() : call(* *..*Service.*(..))
&& !within(MethodTimeAdviceRecipe +);
/**
* 在方连接点(业务类方法)周围执行的通知
*/
Object around() : callServicePointCut(){
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = proceed();
clock.stop(); //计时结束
//显示出方法原型及耗时
log.debug("Takes: " + clock.getTime() + " ms ["+thisJoinPoint.getSignature()+"("+
thisJoinPoint.getSourceLocation().getLine()+")]");
return result;
}
}
package com.unmi.util;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public aspect MethodTimeAdviceRecipe {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
//拦截所有以 Service 结尾类的方法
pointcut callServicePointCut() : call(* *..*Service.*(..))
&& !within(MethodTimeAdviceRecipe +);
/**
* 在方连接点(业务类方法)周围执行的通知
*/
Object around() : callServicePointCut(){
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = proceed();
clock.stop(); //计时结束
//显示出方法原型及耗时
log.debug("Takes: " + clock.getTime() + " ms ["+thisJoinPoint.getSignature()+"("+
thisJoinPoint.getSourceLocation().getLine()+")]");
return result;
}
}
再就是因为无需用到 Spring 了,不需要 IOC 了,相应的 Main.java 的内容如下:
Main.java (以前如何创建 HelloService,现在还是怎么做)
package com.unmi;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
HelloService helloService = new HelloService();
helloService.sayHello(1,"Unmi");
}
}
package com.unmi;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
HelloService helloService = new HelloService();
helloService.sayHello(1,"Unmi");
}
}
OK,现在就可以在 Eclipse 中开始运行 Main 类了,对 methodTimeAdviceRecipe.aj 的编译工作由插件 AJDT 自动帮你作了(实质是相匹配的类中插入了代码)。运行 Main 的结果如下:
Hello Unmi(1)
2008-01-21 13:48:16,171 [DEBUG] MethodTimeAdvice.sayHello_aroundBody1$advice(130)
Takes: 515 ms [void com.unmi.service.HelloService.sayHello(int, String)(16)]
用 AspectJ 可以很灵活的定义方面,局限就是对方面的改变须重新编译相关类,而非配置方式。
参考:一个用Spring AOP实现异常处理和记录程序执行时间的实例
FORM:http://www.blogjava.net/Unmi/archive/2008/01/18/165849.html
有时候我们要跟踪方法的执行时间,来观察系统的性能、时间分布。特别是要找出那些十分耗时的操作。如果是在每个方法中起始和结束位置记下时间相减,那是不太现实的,对代码的侵入性太过份,而且在产品环境中又得屏闭那部份代码。
幸好现在有了 AOP,通过配置方式再加上外部辅助代码就能达到我们的要求,正式上线时只需要简单改个配置项拆卸下来即可。
下面介绍三种方式来打印每个方法的执行时间,分别是:
1. Spring 2.0 用 AspectJ 实现 AOP
2. Spring 通用的方法拦截
3. 直接用 AspectJ 实现
1. Spring 2.0 用 AspectJ 实现 AOP
这个实例由五个文件构成,两个配置文件和三个类文件。需要在项目中引用 Spring 2.0 以上版本的相关包,还要日志包。
1) log4j.properties 放在 src 目录下
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%5p] %c{1}.%M(%L) %n%m%n
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%5p] %c{1}.%M(%L) %n%m%n
2) applicationContext.xml 放在 src 目录下
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.spridngframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:config>
<!-- Spring 2.0 可以用 AspectJ 的语法定义 Pointcut,这里拦截 service 包中的所有方法 -->
<aop:advisor id="methodTimeLog" advice-ref="methodTimeAdvice" pointcut="execution(* *..service..*(..))"/>
</aop:config>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloService" class="com.unmi.service.HelloService"/>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.spridngframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:config>
<!-- Spring 2.0 可以用 AspectJ 的语法定义 Pointcut,这里拦截 service 包中的所有方法 -->
<aop:advisor id="methodTimeLog" advice-ref="methodTimeAdvice" pointcut="execution(* *..service..*(..))"/>
</aop:config>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloService" class="com.unmi.service.HelloService"/>
</beans>
3) MethodTimeAdvice.java 记录时间的类
package com.unmi.util;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public class MethodTimeAdvice implements MethodInterceptor {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
/**
* 拦截要执行的目标方法
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = invocation.proceed();
clock.stop(); //计时结束
//方法参数类型,转换成简单类型
Class[] params = invocation.getMethod().getParameterTypes();
String[] simpleParams = new String[params.length];
for (int i = 0; i < params.length; i++) {
simpleParams[i] = params[i].getSimpleName();
}
log.debug("Takes:" + clock.getTime() + " ms ["
+ invocation.getThis().getClass().getName() + "."
+ invocation.getMethod().getName() + "("+StringUtils.join(simpleParams,",")+")] ");
return result;
}
}
package com.unmi.util;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public class MethodTimeAdvice implements MethodInterceptor {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
/**
* 拦截要执行的目标方法
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = invocation.proceed();
clock.stop(); //计时结束
//方法参数类型,转换成简单类型
Class[] params = invocation.getMethod().getParameterTypes();
String[] simpleParams = new String[params.length];
for (int i = 0; i < params.length; i++) {
simpleParams[i] = params[i].getSimpleName();
}
log.debug("Takes:" + clock.getTime() + " ms ["
+ invocation.getThis().getClass().getName() + "."
+ invocation.getMethod().getName() + "("+StringUtils.join(simpleParams,",")+")] ");
return result;
}
}
4) HelloService.java 被拦截的业务类
package com.unmi.service;
/**
* @author Unmi
*/
public class HelloService {
public void sayHello(int id,String name){
try {
Thread.sleep(512);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello "+name+"("+id+")");
}
}
package com.unmi.service;
/**
* @author Unmi
*/
public class HelloService {
public void sayHello(int id,String name){
try {
Thread.sleep(512);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello "+name+"("+id+")");
}
}
5) Main.java 主程序类
package com.unmi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
BeanFactory factory =new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService = (HelloService)factory.getBean("helloService");
helloService.sayHello(1,"Unmi");
}
}
package com.unmi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
BeanFactory factory =new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService = (HelloService)factory.getBean("helloService");
helloService.sayHello(1,"Unmi");
}
}
执行 Main 后输出的结果是:
Hello Unmi(1)
2008-01-18 13:41:25,593 [DEBUG] MethodTimeAdvice.invoke(34)
Takes:516 ms [com.unmi.service.HelloService.sayHello(int,String)]
如果不需要这个功能,只要在 applicationContext.xml 中把 id="methodTimeLog" 和 id="methodTimeAdvice" 配置项注释掉就行了。
2. Spring 通用的方法拦截
Spring 1.x 因为不能用 AspectJ 来对方法进行拦截,要用到 ProxyFactoryBean 使用 AOP,具体操作方法只需要更换以上例子中的 applicationContext.xml 文件就行,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloServiceTarget" class="com.unmi.service.HelloService"/>
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice"/>
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<bean id="helloService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="helloServiceTarget"/>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice"/>
<bean id="helloServiceTarget" class="com.unmi.service.HelloService"/>
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice"/>
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<bean id="helloService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="target">
<ref bean="helloServiceTarget"/>
</property>
</bean>
</beans>
上面的配置方式需为每个应用方法执行时间记录的 Bean 在外层包一个 ProxyFactoryBean,原来的 Bean 设为一个 Target 实在时麻烦了。
下面用一种应用自动代理的配置方式,指定 BeanNameAutoProxyCreator 的 beanNames 匹配模式即可,如果写成 <value>*Service,*Manager</value>,逗号分隔开,以 Service 或 Manager 结层类的方法都被拦截,这样方便许多。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice" />
<bean id="helloService" class="com.unmi.service.HelloService" />
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice" />
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<!-- 根据 Bean 的名字自动实现代理拦截 -->
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="beanNames">
<list>
<!-- 添加到其中的 Bean 自动就被代理拦截了 -->
<value>*Service</value>
</list>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="methodTimeAdvice" class="com.unmi.util.MethodTimeAdvice" />
<bean id="helloService" class="com.unmi.service.HelloService" />
<bean id="methodTimeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="methodTimeAdvice" />
</property>
<!--对指定类的任何方法有效-->
<property name="patterns">
<value>.*.*</value>
</property>
</bean>
<!-- 根据 Bean 的名字自动实现代理拦截 -->
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodTimeAdvisor</value>
</list>
</property>
<property name="beanNames">
<list>
<!-- 添加到其中的 Bean 自动就被代理拦截了 -->
<value>*Service</value>
</list>
</property>
</bean>
</beans>
3. 直接用 AspectJ 实现
AspectJ 提供了一套语法来定义切面,Spring 2.0 开始引入了 AspectJ 的部分功能,但如果要用上 AspectJ 更多强大完善的功能,诸如实例构造时,属性被访问时,动态改变类定义,滋生新的属性、方法,更强基于流程的控制时,恐怕非得显式的使用 AspectJ。当然,首先你得去 http://www.eclipse.org/aspectj/ 下载到 AspectJ 或者 AspectJ 的 Eclipse 插件。本人强烈建议使用 AspectJ 的 Eclipse 插件,请下载相应 Eclipse 版本的 AspectJ 插件,Eclipse 3.3 要搭配 AJDT: The AspectJ Development Tools 1.5。不要插件的话就得命令行下 ajc 编译你的方面,不容易习惯的。
AJDT 安装成功后,你可以新建 AspectJ 项目,或者把前面项目转换成 AspectJ 项目。从项目的上下文菜单中可以看到 AspectJ Tools -> Convert to Aspectj Project。然后在包 com.unmi.util 中新建一个方面,包 com.unmi.util 的上下文菜单中 New -> Aspect,输入名字 MethodTimeAdviceRecipe,然后 Finish,这时就会在包 com.unmi.util 中产生一个文件 MethodTimeAdviceRecipe.aj,内容如下:
MethodTimeAdviceRecipe.aj (那么 MethodTimeAdvice.java 就派不上用场了,可删去)
package com.unmi.util;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public aspect MethodTimeAdviceRecipe {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
//拦截所有以 Service 结尾类的方法
pointcut callServicePointCut() : call(* *..*Service.*(..))
&& !within(MethodTimeAdviceRecipe +);
/**
* 在方连接点(业务类方法)周围执行的通知
*/
Object around() : callServicePointCut(){
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = proceed();
clock.stop(); //计时结束
//显示出方法原型及耗时
log.debug("Takes: " + clock.getTime() + " ms ["+thisJoinPoint.getSignature()+"("+
thisJoinPoint.getSourceLocation().getLine()+")]");
return result;
}
}
package com.unmi.util;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 记录方法的执行时间
* @author Unmi
*/
public aspect MethodTimeAdviceRecipe {
protected final Log log = LogFactory.getLog(MethodTimeAdvice.class);
//拦截所有以 Service 结尾类的方法
pointcut callServicePointCut() : call(* *..*Service.*(..))
&& !within(MethodTimeAdviceRecipe +);
/**
* 在方连接点(业务类方法)周围执行的通知
*/
Object around() : callServicePointCut(){
//用 commons-lang 提供的 StopWatch 计时,Spring 也提供了一个 StopWatch
StopWatch clock = new StopWatch();
clock.start(); //计时开始
Object result = proceed();
clock.stop(); //计时结束
//显示出方法原型及耗时
log.debug("Takes: " + clock.getTime() + " ms ["+thisJoinPoint.getSignature()+"("+
thisJoinPoint.getSourceLocation().getLine()+")]");
return result;
}
}
再就是因为无需用到 Spring 了,不需要 IOC 了,相应的 Main.java 的内容如下:
Main.java (以前如何创建 HelloService,现在还是怎么做)
package com.unmi;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
HelloService helloService = new HelloService();
helloService.sayHello(1,"Unmi");
}
}
package com.unmi;
import com.unmi.service.HelloService;
/**
* 测试主类
* @author Unmi
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
HelloService helloService = new HelloService();
helloService.sayHello(1,"Unmi");
}
}
OK,现在就可以在 Eclipse 中开始运行 Main 类了,对 methodTimeAdviceRecipe.aj 的编译工作由插件 AJDT 自动帮你作了(实质是相匹配的类中插入了代码)。运行 Main 的结果如下:
Hello Unmi(1)
2008-01-21 13:48:16,171 [DEBUG] MethodTimeAdvice.sayHello_aroundBody1$advice(130)
Takes: 515 ms [void com.unmi.service.HelloService.sayHello(int, String)(16)]
用 AspectJ 可以很灵活的定义方面,局限就是对方面的改变须重新编译相关类,而非配置方式。
参考:一个用Spring AOP实现异常处理和记录程序执行时间的实例
FORM:http://www.blogjava.net/Unmi/archive/2008/01/18/165849.html
发表评论
-
web.xml 中的listener、 filter、servlet 加载顺序
2015-08-11 17:00 575转载:http://www.cnblogs.com/Jess ... -
Spring定时器 Quartz类库
2012-05-04 15:54 1405Quartz可以通过两种方式 ... -
Spring 配置log4j(转)
2011-04-15 14:38 11501、在WEB中加入对Spring对log4j的支持: 即在 ... -
spring中装载多个配置文件
2011-01-26 09:15 1514spring中装载多个配置文件的解决方法(2007-08-05 ... -
Spring配置文件
2011-01-26 09:09 1469Spring配置文件总结Spring 首先来看一个标准的S ...
相关推荐
总结来说,利用Spring AOP记录方法执行时间是一种优雅的解决方案,它有助于我们更好地理解和优化系统的性能瓶颈。通过切面和通知,我们可以轻松地插入监控代码,同时保持核心业务代码的清晰和整洁。在实际项目中,...
在实际应用中,我们可以创建一个切面类,比如`LoggingAspect`,其中包含一个`@Before`通知,用于在每个服务方法调用前记录日志。通过配置Spring的AOP配置,我们可以指定哪些类或方法需要被这个切面拦截。这样,无需...
- 根据需求,可以记录方法名、参数、返回值、执行时间、用户信息和请求信息等。 5. **优化与注意事项** - 注意日志级别,根据不同的环境(开发、测试、生产)设置合适的日志级别,避免过多的日志输出影响性能。 ...
例如,我们可以创建一个`LogAttribute`,在方法上应用这个特性,以便在方法执行前后记录日志。下面是一个简单的示例: ```csharp [AttributeUsage(AttributeTargets.Method)] public class LogAttribute : ...
Spring提供了四种主要类型的Advice,每种类型对应于方法执行的不同阶段: 1. **Before Advice**:在目标方法执行之前调用。你可以通过实现`org.springframework.aop.MethodBeforeAdvice`接口来创建Before Advice。...
在本场景下,我们将创建一个切面来记录和统计每个被拦截方法的执行时间。 首先,我们需要引入Spring AOP的依赖。在`pom.xml`文件中添加以下依赖: ```xml <groupId>org.springframework.boot <artifactId>...
2. **日志记录**: 通过拦截方法调用来添加日志记录功能,无需在每个业务方法中添加日志代码。 3. **性能监控**: 可以在方法调用前后记录执行时间,实现方法级别的性能监控。 4. **安全性检查**: 在方法调用前进行...
静态代理需要为每个目标类创建一个代理类,而动态代理则可以在运行时动态创建代理对象,减少了代码量,同时适应性更强,能够处理未知或新增的接口。 6. **代理模式的其他实现**:除了Java的动态代理,还有其他实现...
在这个例子中,`@Before`通知会在`MyService`类的每个方法调用前执行,记录入参;`@AfterReturning`通知则在方法成功返回后记录执行时间。`execution(* com.example.myapp.MyService.*(..))`是切入点表达式,表示...
Spring AOP(面向切面编程)是Spring框架中的一个重要组件,它允许我们在不修改源代码的情况下,通过在程序运行时动态地将代码插入到方法调用中,来实现跨切面的关注点,如日志记录、性能监控、事务管理等。...
例如,我们可以定义一个切面来处理所有数据库操作的事务,这样无需在每个业务方法中显式调用开始和提交事务,只需在切面中配置事务规则即可。 `aop-jar`这个压缩包可能包含了Spring AOP的实现类、接口、以及其他...
- **日志记录**:通过定义一个日志记录切面,可以自动在所有业务方法的开始和结束处记录日志信息,无需在每个方法内部手动编写日志语句。 - **权限验证**:在用户访问敏感资源之前,可以通过一个权限验证切面检查...
这意味着在controller层的每个方法执行前和执行后,对应的日志记录方法都会被调用。 接下来,我们需要在Spring配置中启用AOP代理。在Spring XML配置文件中,添加以下代码: ```xml <aop:aspectj-autoproxy /> ``` ...
- **事务管理**:Spring的AOP可以用来自动处理事务的开始、提交、回滚等操作,无需在每个业务方法中显式处理。 - **日志记录**:在方法调用前后记录日志,方便追踪程序运行情况。 - **性能监控**:在关键方法周围...
`logAround`方法会在每个被拦截的方法前后执行,记录方法名和执行时间。 最后,确保在Spring配置文件(如`applicationContext.xml`)中启用AOP并注册切面: ```xml <aop:aspectj-autoproxy /> ``` 总结起来,...
这个过程称为拦截器链,每个通知相当于一个拦截器,按照顺序执行。 4. 通知类型: - 前置通知(Before):在目标方法执行前运行。 - 后置通知(After):在目标方法执行后运行,无论方法是否正常完成。 - 返回...
3. **异常处理**:通过AOP,我们可以定义全局的异常处理策略,当方法抛出异常时,自动执行相应的通知,如记录错误日志、发送报警邮件或展示友好的用户错误信息。这种方式比在每个可能抛出异常的地方都写异常处理代码...
- 日志记录:在方法调用前后记录日志,无需在每个方法中重复代码。 - 事务管理:处理数据库操作的事务边界,确保数据一致性。 - 权限控制:在访问敏感资源前进行权限检查。 - 性能监控:统计方法执行时间,分析...
通过AOP,我们可以将这些通用的功能抽取出来,形成独立的切面,而不是在每个需要它们的地方重复编写代码。在Spring中,注解使得AOP的使用更加简洁,避免了XML配置的繁琐。本篇文章将详细讲解五种主要的AOP通知注解...
比如,我们可以定义一个全局的日志切面,而无需在每个业务方法中手动添加日志记录。此外,事务管理也是AOP常用于解决的问题,通过定义事务切面,可以确保业务操作在正确性和原子性上得到保障。 总之,AOPalliance、...