springAOP实现的异常日志记录+异常邮件发送+权限控制

       一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。


       鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
       资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
+ l# q+ o8 e' ]2 x% ]# F
% t! x- o! A7 m& `8 q" p2 o
一、 记录日志并发送邮件通知4 y! ~7 [/ L! ?4 ]4 s; M
(1)、定义注解:
1.ServiceLog.java(各种参数类型详见上面的资料)

  1. import java.lang.annotation.*;
  2. /**
  3. * 自定义注解 拦截service  : ~. L- V8 v( t$ \9 ^6 V% g
  4. * 创建者        张志朋+ x7 c  Z# W; b# T- G& W( o: t
  5. * 创建时间        2015年6月3日9 d; P1 X) r6 {6 q# u% h& p6 k6 H
  6. *
  7. */1 j! l9 _+ e/ z' S
  8. @Target({ElementType.PARAMETER, ElementType.METHOD})   
  9. @Retention(RetentionPolicy.RUNTIME)    1 M; d7 P  x$ ^8 x( v( E
  10. @Documented   
  11. public  @interface ServiceLog { 6 D4 w3 z2 y! A1 h
  12.          String description()  default "";
  13. }
复制代码
2.ControllerLog.java ) v7 n! A. q. a
  1. import java.lang.annotation.*; - y6 V9 ~& T" u6 }6 t" D' e% \2 E
  2. /**
  3. * 自定义注解 拦截Controller
  4. * 创建者        张志朋  S; q: G8 h) Y' T
  5. * 创建时间        2015年6月3日0 n- V: l' t( h2 C, Y9 i" [
  6. *
  7. */
  8. @Target({ElementType.PARAMETER, ElementType.METHOD})    % R, ]9 ?( S2 b
  9. @Retention(RetentionPolicy.RUNTIME)   
  10. @Documented+ v% F( p2 \* s( `  _7 c
  11. public @interface ControllerLog {( x2 \5 o$ y/ Y; G0 {, d
  12.         String description()  default ""; 7 k- m. x% \7 n) O
  13. }
复制代码
(2)、定义切面以及切入点1 M1 m4 u! b7 l
1.LogAspect.java
6 w: S& Y. ^6 J
  1. /**
  2. * 日志记录AOP
  3. * 创建者        张志朋
  4. * 创建时间        2015年6月3日! {) e5 l- m* i; O: l# ]
  5. */ g, h) `& i* q- x+ A! o( C* R
  6. */
  7. @Component
  8. @Scope8 j0 @$ R: l% p5 I# a: u$ w4 |
  9. @Aspect
  10. public class LogAspect {/ P. [% F( v% ?- I2 [$ R
  11.         //Service层切点     用于记录错误日志
  12.         @Pointcut("@annotation(com.web.aop.ServiceLog)")  
  13.         public void serviceAspect() {
  14.                   Z' e- i4 d! C; R: v
  15.         }4 {* e) K' j! k2 c
  16.     /**( G8 V$ a  D5 X1 m4 h
  17.      * 异常通知 用于拦截service层记录异常日志  3 C4 I" p- e) X* O$ F, j
  18.      * @Author        张志朋0 u% _  _, P( H) e6 {4 O
  19.      * @param joinPoint
  20.      * @param e  void  ~6 [" q- B, K* C& u$ H: ?( z8 i
  21.      * @Date        2015年6月3日
  22.      * 更新日志
  23.      * 2015年6月3日 张志朋  首次创建  ^9 X/ Z8 g; n6 c/ a! r; H5 J
  24.      *- X# i8 H: u) A0 `3 ]: _3 z
  25.      */
  26.     @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")    # F& ^/ ^& e1 Y. g$ f: E
  27.     public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) { ! n$ ~9 v0 t+ R" Z: c/ C) [
  28.                 HttpServletRequest request = ServletActionContext.getRequest();# [9 ^. \( u% Z/ r1 b
  29.                 TeacherEntity user = CommonUtil.getUser();
  30.                 String ip = AddressUtils.getIpAddr(request);, P5 }1 B0 X. }1 D
  31.                 try {
  32.                         String params = "";' Z+ y6 N) J& ]: A
  33.                         if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
  34.                                 for (int i = 0; i < joinPoint.getArgs().length; i++) {
  35.                                         params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";# n& ~! N. H3 [: S" M
  36.                                 }6 h! K6 z  F$ ?0 H' I2 c) n
  37.                         }
  38.                         String description = getServiceMthodDescription(joinPoint);//用户操作8 d7 r0 {! G4 S; P/ ]! C2 L* m
  39.                         String exceptionCode  =e.getClass().getName();//异常类型代码3 }: p" W3 I1 J
  40.                         String exceptionDetail = e.getMessage();//异常详细信息; l3 u3 R& D" l
  41.                         String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
  42.                         /*==========记录数据库异常日志==========*/  & ^1 }% o# t4 S5 R' `/ \( H8 y& U# R% o
  43.                         Log log = new Log();
  44.                         log.setDescription(description);
  45.                         log.setExceptionCode(exceptionCode);8 S! X8 j! C1 h& y2 s& z
  46.                         log.setExceptionDetail(exceptionDetail);
  47.                         log.setMethod(method);( f) g) b7 ~: {/ j
  48.                         log.setType(Constants.LOG_ERROE);// 日志类型, B0 J' P# p6 T* K/ p. m
  49.                         log.setRequestIp(ip);// 请求IP& o* ?4 e- L, b& A; |6 M
  50.                         log.setParams(params);//请求参数0 h2 D7 s* s0 @7 K9 ?. t
  51.                         if(null!=user){, O/ l6 M7 x$ S6 K# |5 r& s! K7 q
  52.                     log.setCreateUid(user.getUid());//用户ID( z0 L( j$ ~: J" K' J% v
  53.                     log.setCreateName(user.getNickname());//用户昵称
  54.             }" s9 ~: U6 y- G- M' Q' }
  55.                         log.setPlatFrom(Constants.SUBJECT_CODE);
  56.                         /*==========记录数本地异常日志==========*/  
  57.                         //LogUtil.error(description, e);; T8 N! s/ l* Q
  58.                         /*==========发送异常日志到邮箱==========*/
  59.                         StringBuffer errorMsg = new StringBuffer();5 |' L( e# ~+ R% Q' r$ g( U* g
  60.                         errorMsg.append("异常方法:");
  61.                         errorMsg.append(method);
  62.                         errorMsg.append("</br>");& E- N- u; w1 E) M+ Y6 I( L$ [
  63.                         errorMsg.append("异常类型代码:");0 ^$ R5 I/ R8 {
  64.                         errorMsg.append(exceptionCode);7 z. ~; m. H) c; C( I
  65.                         errorMsg.append("</br>");
  66.                         errorMsg.append("异常详细信息:");
  67.                         errorMsg.append(exceptionDetail);- X' n: G0 Z" s" Z( o# v) r$ U7 k
  68.                         errorMsg.append("</br>");
  69.                         log.setErrorMsg(errorMsg.toString());3 |, C- c5 N3 s, ?/ i& m
  70.                         WebServiceMathClient  Client = new WebServiceMathClient();$ u% a& x/ n* s
  71.                         Client.sendError(log);
  72.                 } catch (Exception ex) {* d6 U3 M% Z7 n. W0 S
  73.                         e.printStackTrace();
  74.                 }* c$ B6 q6 \. N: Z3 w5 y
  75.     } 5 \( I% M- y& t) r
  76.      /**
  77.       * 获取注解中对方法的描述信息 用于service层注解  (基于反射)4 |; L( M9 d" `8 _. m! n& X5 ]
  78.       * @Author        张志朋
  79.       * @param joinPoint
  80.       * @return
  81.       * @throws Exception  String
  82.       * @Date        2015年6月3日
  83.       * 更新日志
  84.       * 2015年6月3日 张志朋  首次创建
  85.       *
  86.       */
  87.      @SuppressWarnings("rawtypes")
  88.         public  static String getServiceMthodDescription(JoinPoint joinPoint)   
  89.              throws Exception {    0 W* G  s  D7 D2 b; w. H
  90.         String targetName = joinPoint.getTarget().getClass().getName();    % F8 s+ \: Z3 ?7 \$ ]
  91.         String methodName = joinPoint.getSignature().getName();   
  92.         Object[] arguments = joinPoint.getArgs();    6 v$ V7 n5 K& {) O8 x
  93.         Class targetClass = Class.forName(targetName);    % _: @/ t$ y* q$ N& j
  94.         Method[] methods = targetClass.getMethods();   
  95.         String description = "";   
  96.          for (Method method : methods) {    , k  R# C- s7 v$ L
  97.              if (method.getName().equals(methodName)) {   
  98.                 Class[] clazzs = method.getParameterTypes();   
  99.                  if (clazzs.length == arguments.length) {    2 R# E# k2 G7 I4 c$ X) Y
  100.                     description = method.getAnnotation(ServiceLog. class).description();   
  101.                      break;   
  102.                 }   
  103.             }    - v8 ~( _: X( Z
  104.         }   
  105.          return description;   
  106.     } # I/ ?! V; V' q5 o0 z
  107.     }
复制代码

这里说明一下  serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)")  、也就说明这是一个切入点,springAOP对其进行了封装。
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
* U: G8 b. E7 a
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)
3 v5 C( c! m% H+ J0 w; `) g/ M" i
  1. @ServiceLog(description="获取待审试题数量")/ \( }1 m3 c8 a
  2. public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {7 {( q8 U+ l2 ]2 M5 i/ h1 y
  3.                 return quesPerDao.getAuditQuesNum(currentUser);
  4.         }
复制代码

之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。! q9 r% w- _% |0 H  n3 Z! `
二、AOP实现权限控制
       上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。/ B4 `/ O. K$ L; u* ~
        还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。
首先配置文件要引入这样一段配置(看注释说明):
: j# ]  _7 X: o/ \5 [* ~% A  D$ }% k
  1. <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->! V+ A3 o+ F/ c- n5 b0 U
  2.         <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码
  I1 G0 ]! Z& O1 z
(1)、定义注释:
1.Permission.java

  1. /**/ I) L0 }6 W3 p* q! W/ ?( a8 i
  2. * 自定义权限管理
  3. * 创建者        张志朋
  4. * 创建时间        2015年6月30日( n: [8 B& z- F: D4 K* O' ~
  5. *, {9 `4 ~* P- h) P& Z$ i  e" J# G
  6. */7 M' ]! E4 r7 N* N$ z
  7. @Target({ElementType.PARAMETER, ElementType.METHOD})    ) y8 _  M9 T. X9 Y- J- R
  8. @Retention(RetentionPolicy.RUNTIME)   
  9. @Documented
  10. public @interface Permission {
  11.         String name()  default ""; //操作行为
  12.         int id() default -1;//权限值
  13. }
复制代码
(2)、定义切面以及切入点
1.PsemissionAspect.java
, X0 h4 T, c% R# u* u6 E& r" a
  1. /**
  2. * 权限管理
  3. * 创建者        张志朋
  4. * 创建时间        2015年7月3日+ w; @. ]$ l/ _) X/ f
  5. *4 e( B" q' }  B1 C* }( _; y
  6. */! V; M; x: ]9 X# J; V1 N& c" A2 Q
  7. @Component* s" l. ?' U( r( n# x
  8. @Scope% U( P, l! q+ [1 f
  9. @Aspect, q* ~8 `$ G+ `/ N7 M& w
  10. public class PsemissionAspect {
  11.         " E& c, n; x  G
  12.     //Controller层切点 用于权限控制. G$ L# x% l- a9 ^) o5 ^
  13.         @Pointcut("@annotation(com.acts.web.aop.Permission)")  
  14.         public void permissionAspect() {
  15.                
  16.         }! k  G' ?, J4 J5 k
  17.         /**
  18.          * 用于拦截Controller层用户操作权限(环绕通知)
  19.          * @Author        张志朋
  20.          * @param joinPoint  void3 r# p: b" b$ n3 Z, U
  21.          * @Date        2015年6月3日/ b/ C* y+ ^6 \. z' \
  22.          * 更新日志
  23.          * 2015年6月3日 张志朋  首次创建! o6 d- C3 V: k/ J6 H, ~
  24.          ** j! O( p+ a6 e& ?
  25.          */, K! V. a, w( D/ G
  26.     @Around("permissionAspect()")   
  27.     public  Object permission(ProceedingJoinPoint  joinPoint)throws Throwable {  
  28.         Object retVal = null;+ Z" m9 g" Z# ~3 K' I/ p( |. j
  29.                 int role  =  getControllerMethodRole(joinPoint);8 d% J# e  o9 s% m2 K" C) X7 E  }5 o7 N
  30.                 TeacherEntity user = CommonUtil.getUser();
  31.                 if((user.getSpecRole()&role)==role){//没有权限8 K! v4 t$ u1 n9 q, n3 D
  32.                         retVal  = joinPoint.proceed();# \+ Z  {' Z/ H/ x8 A8 |4 r( q
  33.                 }else{
  34.                         noAuthorization();1 g4 F7 d& _: r
  35.                 }
  36.             return retVal;3 a+ q7 g- Z0 r, S& z& C- X
  37.     }: ?$ _1 T2 Q. V& u7 p# R/ ~2 j
  38.     /**
  39.      * 没有权限 实现跳转4 d7 n' q. c* h5 i0 H& Z3 j& {. m
  40.      * @Author        张志朋
  41.      * @throws IOException  void
  42.      * @Date        2015年7月3日
  43.      * 更新日志% p2 f, w# [) S, J6 F/ {
  44.      * 2015年7月3日 张志朋  首次创建$ v3 H) E- V4 {; S! H9 Q9 `
  45.      *, R* K; p) X4 K& ^, ^
  46.      */) ~% Q- M- Y* J$ H: x$ v
  47.     public void noAuthorization() throws IOException{
  48.             HttpServletRequest request = ServletActionContext.getRequest();
  49.                 String path =  request.getContextPath();
  50.                 HttpServletResponse response  = ServletActionContext.getResponse();0 h- n9 \4 P- n% G8 _7 ~
  51.                 response.sendRedirect(path+"/pages/noAuthorization.jsp");# v7 O# |# p; a9 B
  52.     }+ e: h' w9 b9 r: G
  53.     /**- D" C9 P; ~$ x$ k1 y  ^
  54.      * 获取注解中对方法的权限值 用于Controller层注解  & B" T  K! |' @, b; f$ ]
  55.      * @Author        张志朋' C3 ^) ?" r. {/ T
  56.      * @param joinPoint- G1 M4 T0 O2 h, }2 k; S! ]7 N
  57.      * @return2 T; ?7 t. Z; f) T7 g  B) G
  58.      * @throws Exception  int
  59.      * @Date        2015年7月3日# d3 v. j9 |6 h# Y- f
  60.      * 更新日志) x; M. l9 z( s* I  Z; V3 w( b; h
  61.      * 2015年7月3日 张志朋  首次创建6 C1 J8 ~7 `; I
  62.      *
  63.      */  g* c8 _5 h' f
  64.     @SuppressWarnings("rawtypes")
  65.         public  static int getControllerMethodRole(JoinPoint joinPoint)  throws Exception {/ Z5 }$ A' O4 a! c# l
  66.         String targetName = joinPoint.getTarget().getClass().getName();    : r. D2 X) ~" t7 j: i% L
  67.         String methodName = joinPoint.getSignature().getName();    ( ]1 ?! v4 ?% b. s/ W/ o! P
  68.         Object[] arguments = joinPoint.getArgs();   
  69.         Class targetClass = Class.forName(targetName);    9 _- m; s* s6 {9 r+ R  O2 [8 Y
  70.         Method[] methods = targetClass.getMethods();    6 w0 q$ D% A4 U+ l
  71.         int role = -1;   
  72.          for (Method method : methods) {    " B+ C7 I. \2 m5 i) w  R
  73.              if (method.getName().equals(methodName)) {    & N) M. w  }4 u: j* p  v
  74.                 Class[] clazzs = method.getParameterTypes();   
  75.                  if (clazzs.length == arguments.length) {   
  76.                          role = method.getAnnotation(Permission. class).id();   
  77.                      break;   
  78.                 }   
  79.             }   
  80.         }    7 _* H- x2 b1 H
  81.          return role;   
  82.     }
复制代码
0 D, F; Y& O% c/ v) p
2.action层代码实现:
  1. /**
  2.          * 试题审核不通过
  3.          * @Author        张志朋  void* u/ t3 p  Y/ N8 r7 z: e) P
  4.          * @Date        2015年5月4日1 H9 N! ^2 T( ^
  5.          * 更新日志
  6.          * 2015年5月4日 张志朋  首次创建
  7.          *
  8.          *// C: H: Y, x( b' J
  9.         @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)$ C- I" p0 R) S, I! ]
  10.         public void auditQuestions(){. o; ?# b6 Y# E% o; z4 r! x! u
  11.                 try {
  12.                      //代码实现3 ^9 a2 U3 R' b
  13.                 } catch (Exception e) {8 q7 {2 Z! Z/ K# R
  14.                      e.printStackTrace();
  15.                 }

  16.         }
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP是一个强大的框架,可以帮助我们实现各种切面,其中包括日志记录。下面是实现日志记录的步骤: 1. 添加Spring AOP依赖 在Maven或Gradle中添加Spring AOP依赖。 2. 创建日志切面 创建一个用于记录日志的切面。这个切面可以拦截所有需要记录日志的方法。在这个切面中,我们需要使用@Aspect注解来声明这是一个切面,并使用@Pointcut注解来定义哪些方法需要被拦截。 ```java @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.demo.service.*.*(..))") public void serviceMethods() {} @Around("serviceMethods()") public Object logServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法名,参数列表等信息 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); // 记录日志 System.out.println("Method " + methodName + " is called with args " + Arrays.toString(args)); // 执行方法 Object result = joinPoint.proceed(); // 记录返回值 System.out.println("Method " + methodName + " returns " + result); return result; } } ``` 在上面的代码中,我们使用了@Around注解来定义一个环绕通知,它会在拦截的方法执行前后执行。在方法执行前,我们记录了该方法的名称和参数列表,然后在方法执行后记录了该方法的返回值。 3. 配置AOP 在Spring的配置文件中配置AOP。首先,我们需要启用AOP: ```xml <aop:aspectj-autoproxy/> ``` 然后,我们需要将创建的日志切面添加到AOP中: ```xml <bean id="loggingAspect" class="com.example.demo.aspect.LoggingAspect"/> <aop:config> <aop:aspect ref="loggingAspect"> <aop:pointcut id="serviceMethods" expression="execution(* com.example.demo.service.*.*(..))"/> <aop:around method="logServiceMethods" pointcut-ref="serviceMethods"/> </aop:aspect> </aop:config> ``` 在上面的代码中,我们将创建的日志切面声明为一个bean,并将其添加到AOP中。我们还定义了一个切入点,并将其与日志切面的方法进行关联。 4. 测试 现在,我们可以测试我们的日志记录功能了。在我们的业务逻辑中,所有匹配切入点的方法都会被拦截,并记录它们的输入和输出。我们可以在控制台中看到这些日志信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值