一个关于Spring AOP的问题

先说一下情况:小弟现正用Spring AOP实现日志记录功能,上面交待Web层,Service层,Dao层都要记录,所以打算在Web层只记一下用户请求的情况,也就是说用户请求哪个Action时,记录一下操作步骤,Service层记录方法调用时的参数及返回值以及异常情况,Dao层上面交待要记录最终SQL(这个小弟现在还不知道怎么实现)。
在此只说一下Web层的日志记录,其它的略过了。我是这样的,先定义一个名为ActionName的annotation,有一个value属性,这个value属性的值就表示该Action方法的功能说明。然后我定义一个Pointcut匹配该annotation。到这里一切都没问题,能正常拦截并代理Action方法,但是前提是必须使用CGLIB!
我记得好像Spring能自动地判断目标方法并决定是使用动态代理还是CGLIB代理实现的。但是我在配置文件中如果不强制使用CGLIB代理则程序运行报错呢?
下面是我的代码:

LogHandler类
package com.delver.bms.log.util;

import java.lang.reflect.Method;
import java.sql.Timestamp;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import com.delver.bms.exception.BusinessException;
import com.delver.bms.log.annotation.ActionName;
import com.delver.bms.log.annotation.TaskScheduling;
import com.delver.bms.log.dao.IAppLogDao;
import com.delver.bms.log.to.AppLog;
import com.delver.bms.util.AutoID32;
import com.delver.bms.util.TenantUtil;
import com.ibm.crl.mmt.datamodel.MTUtilFactory;
import com.ibm.crl.mmt.exception.MTException;

/**

  • 日志记录切面类
  • @author yue.ch
  • */ @Aspect public class LogHandler {

private IAppLogDao appLogDao;

public void setAppLogDao(IAppLogDao appLogDao) {
this.appLogDao = appLogDao;
}

/**

  • 定义切入点,匹配使用@ActionName注解的方法
  • @param actionName @ActionName注解value值(功能解释) */ @Pointcut("@annotation(actionName)") public void annotationMethod(ActionName actionName) { }

/**

  • 定义切入点,匹配使用@TaskScheduling注解的方法
  • @param taskScheduling @TashScheduling注解value值(功能解释) */ @Pointcut("@annotation(taskScheduling)") public void annotationTaskMethod(TaskScheduling taskScheduling) { }

/**

  • 定义切入点,匹配Service层方法 / @Pointcut("execution( com.delver.bms.*.service.*.*(..))") public void serviceMethod() { }

/**

  • 定义切入点,排除UserDetailsServiceImpl类 */ @Pointcut("!within(com.delver.bms.identity.service.UserDetailsServiceImpl)") public void identityServiceMethod(){}

/**

  • 定义切入点,排除日志记录功能模块的Service层方法 / @Pointcut("!execution( com.delver.bms.log.service.*.*(..))") public void logServiceMethod(){}

/**

  • 定义切入点,排除日志记录功能模块的Dao层方法 / @Pointcut("!execution( com.delver.bms.log.dao.*.*(..))") public void logDaoMethod(){}

/**

  • 定义切入点,匹配Dao层方法 / @Pointcut("execution( com.delver.bms.*.dao.*.*(..))") public void daoMethod(){}

/**

  • 系统任务调度方法前置通知(记录任务调度方法功能说明及执行)
  • @param joinPoint
  • @param taskScheduling */ @Around("annotationTaskMethod(taskScheduling)") public void aroundTaskScheduling(ProceedingJoinPoint joinPoint, TaskScheduling taskScheduling) throws Throwable {

System.out.println("ddd");
}

/**

  • Action方法前置通知(记录Action方法功能说明)
  • @param joinPoint
  • @param actionName */ @Before("annotationMethod(actionName)") public void beforeActionLog(JoinPoint joinPoint, ActionName actionName) {

System.out.println("ddd");

}

/**

  • Service方法环绕通知
  • @param joinPoint
  • @return */ @Around("serviceMethod() && identityServiceMethod() && logServiceMethod()") public Object aroundServiceLog(ProceedingJoinPoint joinPoint) throws Throwable {

System.out.println("ddd");
}

/**

  • Dao方法环绕通知(记录SQL)
  • @param jointPoint
  • @return */ @Around("daoMethod() && logDaoMethod()") public Object aroundDaoLog(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("ddd"); }

配置文件:
<?xml version="1.0" encoding="utf-8"?>
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.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">





scope="prototype">




异常信息:
java.lang.NoSuchMethodException: $Proxy51.visit()
at java.lang.Class.getMethod(Unknown Source)
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.getActionMethod(AnnotationValidationInterceptor.java:55)
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:41)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:86)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:123)

下面略。。。
[b]问题补充:[/b]
我希望的是Service层,Dao层用动态代理,web层用CGLIB代理,是不是不能实现?我试过配多个aop:config,但是只要其中有一个配置了proxy-target-class="true",就全都用CGLIB实现了,网上搜了下,貌似确实是这样的。还有,CGLIB代理与动态代理在运行期性能上有区别吗?

2个回答

配置aop:config元素的proxy-target-class="true"当然是对全局切面的配置,
自然是全部都使用cglib,除非你定义多个不同的切面,也就是定义多个aop:config元素,那每一个就对应一种不同的配置了

JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理

*CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。

关于性能方面,这个就得实地测试才知道,一般来说JDK 的动态代理是用反射来实现,
但CGLIB要产生一个子类,性能可能要差些

Spring能自动地判断目标方法并决定是使用动态代理还是CGLIB代理实现的,这个理解是错的

spring是根据你要代理的类来自动决定是使用动态代理还是CGLIB代理,对于要代理的类实现了接口,那就一般使用动态代理,如果要代理的类没实现任何接口,那就用CGLIB代理,

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问