TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 8 O* M; _, W F* l0 ^
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。" G9 q& I' A: @% _) @
3 _6 Q0 S! L8 N' C2 y; Y
8 C4 C# g/ ?. Y$ c; g. ` 鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。$ G5 V. u) b n. o# x ^4 a" n. C( o
资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
6 [6 Y& }3 K) I# R/ M
. ^% w# d S+ o% u1 f一、 记录日志并发送邮件通知, p4 O8 d; `5 \! P: |" I' r9 P/ e
(1)、定义注解:
. y! h- r' R2 I+ @! T+ k6 `1.ServiceLog.java(各种参数类型详见上面的资料)% c2 r0 {7 @5 `6 Z
- import java.lang.annotation.*; 5 W7 e3 t4 Y( c1 y# D# G+ y
- /**" Q2 K) k$ d3 o4 G* K
- * 自定义注解 拦截service % D6 q2 h' _- \: P* l4 s
- * 创建者 张志朋
+ Q' L. I4 v. ^4 ~# _ - * 创建时间 2015年6月3日. u# e. m! |( b" y# w) A/ G
- *
; B4 S1 p. H5 j1 \2 V' u8 Q" C - */8 C0 r M/ `3 y3 R
- @Target({ElementType.PARAMETER, ElementType.METHOD}) : Y6 e+ _8 `8 F& T
- @Retention(RetentionPolicy.RUNTIME) - J& D( a) a& Y/ {+ b0 M0 I, E
- @Documented 2 V& ], Q5 \/ y" d8 D" k& Y1 [1 G2 k
- public @interface ServiceLog {
2 C* ~$ ?: r/ `1 ]% q8 e - String description() default "";
" Z5 x6 j# b* T0 Y: ^ - }
复制代码 2.ControllerLog.java' r( A2 Z7 R" I) n4 i2 ?
- import java.lang.annotation.*;
9 W0 k" T( I8 [ - /**' Y4 ?/ O; {0 `: M V6 ^: Y0 \
- * 自定义注解 拦截Controller
& t5 e+ @' {( a% h4 v - * 创建者 张志朋4 ~+ O5 f- P: P% S$ F5 ?
- * 创建时间 2015年6月3日5 t, P% \2 q- B7 g. ?$ i
- *
2 T: Q/ ^( l4 j8 X+ [- m - */
$ z- _ c5 X2 a+ d6 H - @Target({ElementType.PARAMETER, ElementType.METHOD})
& ] R& @+ Y: |7 x* B - @Retention(RetentionPolicy.RUNTIME)
4 _; T, f: z, @. X5 u! z5 d - @Documented# q% }% J A$ ]( b, x8 }& E
- public @interface ControllerLog {
" T- j3 G+ U1 P' \* P - String description() default ""; % @& j3 D1 C4 j5 R
- }
复制代码 (2)、定义切面以及切入点
7 q7 r. c; |: K1 y$ C ~3 ?& J1.LogAspect.java6 ^5 G7 c) R# V& |
- /**
' r- z5 Y8 V7 s$ H* {* Y - * 日志记录AOP
8 \( x+ c, _. f- d% V - * 创建者 张志朋' z1 X2 K* Q% A( D; l
- * 创建时间 2015年6月3日: F" z# G8 P8 g$ N2 i; H( S
- *
. Y6 A6 C8 I) i: D - */
2 D. W7 L: N0 {3 M2 W( S) T - @Component
% K6 Q$ B! E9 G. q# _- E% Q - @Scope: q( l+ q3 S# I; `& H* w
- @Aspect
: z: {# S8 U1 d - public class LogAspect {$ i m1 b) ?, O; H0 P- y5 `7 g# ~
- //Service层切点 用于记录错误日志
/ }6 p8 Y" q- ^" W; i3 V3 a - @Pointcut("@annotation(com.web.aop.ServiceLog)") % }# e9 A! B% ?: E
- public void serviceAspect() {
9 @' h3 _/ W3 |8 U5 H: H/ C/ D - ' |3 Y1 G% m9 r7 K2 D
- }
6 y! ]' n. b2 D& T5 ^ - /**/ B! u( z) r8 f4 q$ W) D# w2 j8 ~
- * 异常通知 用于拦截service层记录异常日志 $ l# Y) _& |6 I* \" H1 I
- * @Author 张志朋
2 H- E6 n( I6 ?3 d8 p - * @param joinPoint7 p: f; Q0 t' s2 a \$ S# v9 f
- * @param e void, @- ?- u+ @; Z. M4 H' m
- * @Date 2015年6月3日
& S8 E! {* m7 J- y2 \4 }$ N - * 更新日志
& \: I' g6 x. N- w - * 2015年6月3日 张志朋 首次创建. n3 Q a# r* T& `9 \
- *
, p8 a& e9 [% H- \) A% i - */0 v, J2 l* c( C
- @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") S! x ^ C/ B0 B2 q0 S
- public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { : }/ w* n" x6 x- _. D4 C
- HttpServletRequest request = ServletActionContext.getRequest();
8 d. M F9 Y' f# [0 W - TeacherEntity user = CommonUtil.getUser();5 O% a* }# m5 B* w9 Y# v: J$ e
- String ip = AddressUtils.getIpAddr(request);
6 {& b4 ?/ {6 S5 t2 g# f - try {
- Y5 P: L3 c- ~. Y' Y& V% H - String params = "";, ]/ }2 b' @+ b+ G$ f0 R3 V) C. _
- if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
! u& B8 x; P+ V6 I3 e - for (int i = 0; i < joinPoint.getArgs().length; i++) {% F) n4 w8 W. e+ M1 }+ p( v
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
; b' m! J- P3 B9 S" N1 d" x - }' V5 i$ b5 T1 B
- }
7 J3 K2 y k/ X; W - String description = getServiceMthodDescription(joinPoint);//用户操作/ d9 C( o9 i: l3 B5 [ R- C0 v1 T
- String exceptionCode =e.getClass().getName();//异常类型代码
- v( S3 G2 V& K4 s; o6 v4 E - String exceptionDetail = e.getMessage();//异常详细信息, O& B& k. w( P8 I& C2 `2 T
- String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法- w! ]& i; Q8 l# a" ]
- /*==========记录数据库异常日志==========*/ - x8 t( J7 s9 w. f
- Log log = new Log();* w( E" R7 \3 b) @- X
- log.setDescription(description);1 b9 k9 R$ p! F! L+ L
- log.setExceptionCode(exceptionCode);7 N. I! z- ?2 E! A2 p
- log.setExceptionDetail(exceptionDetail);
( @8 g; S( i; j c - log.setMethod(method);, V# S$ g. l9 f$ n$ x
- log.setType(Constants.LOG_ERROE);// 日志类型+ A B" I G! J. J! d* \9 O7 Y) v
- log.setRequestIp(ip);// 请求IP
# L4 ^- T4 Z- _5 o6 {4 N0 |4 ? - log.setParams(params);//请求参数, {5 m) E! `# t- X, E1 W- h) ?
- if(null!=user){2 ^- q+ B# ~5 b5 ] P" j8 v; O
- log.setCreateUid(user.getUid());//用户ID. Q, L8 r# K4 s2 O, u
- log.setCreateName(user.getNickname());//用户昵称
. ?/ R2 u9 {: |8 _ - }" e5 J3 `" t! E @
- log.setPlatFrom(Constants.SUBJECT_CODE);
: c- v# [* Z" [9 j( j - /*==========记录数本地异常日志==========*/
, h6 R, J# p5 W" {( O - //LogUtil.error(description, e);
0 s- \7 e$ S( R - /*==========发送异常日志到邮箱==========*/: j* g7 n# c% U% ]! n ?5 R, g% \3 h
- StringBuffer errorMsg = new StringBuffer(); l/ L" i. D/ Y2 ^% @' B7 M
- errorMsg.append("异常方法:");$ @$ E ^5 W8 K: J+ p
- errorMsg.append(method);: d8 h# ~) c: U4 r0 T3 }, h( s# ]
- errorMsg.append("</br>");
" c9 m \6 j2 a$ c: p6 k6 G/ | - errorMsg.append("异常类型代码:");
# d, t3 Y. P5 V( i ~8 `0 U0 t - errorMsg.append(exceptionCode);5 R, l' S1 L v9 M" R
- errorMsg.append("</br>");
1 g* _. J/ [; j$ a - errorMsg.append("异常详细信息:");) a% g4 g: D( D' Z- O7 W
- errorMsg.append(exceptionDetail);
0 y. q: N4 U* N9 b# a4 N* F - errorMsg.append("</br>");4 i' Y* `8 S% D$ ~; v# a' A0 }& N
- log.setErrorMsg(errorMsg.toString());) ~( C* G9 n/ K7 T) a4 J2 H
- WebServiceMathClient Client = new WebServiceMathClient();9 T8 [9 m/ T0 V$ Y6 M. S: E
- Client.sendError(log);
* k4 F7 Z' ?; K& S; X - } catch (Exception ex) {
1 U: s4 n7 j/ E& h! e' r" ~ - e.printStackTrace();
+ G* O5 r, ?/ J2 ~5 B; ?$ }. m - }! w& W! \) b, g3 P) b1 a
- } 2 ~( Y. M- ?- r. t
- /**
5 N, V% s' W$ u2 ~3 p9 a& | - * 获取注解中对方法的描述信息 用于service层注解 (基于反射)
& c/ {9 o! o* _$ D: h - * @Author 张志朋$ K& J4 ~" f1 W' m9 e0 z
- * @param joinPoint
8 \8 P+ j2 W. d/ B8 i9 ]5 u8 \& ~ - * @return5 r& s- Z4 n# ?- z$ f* _5 F' G4 G
- * @throws Exception String9 S& w: O' w& U8 }
- * @Date 2015年6月3日
: a, s! D6 S5 Q) C# u9 s - * 更新日志
6 ~( G$ \( x. B: |* U - * 2015年6月3日 张志朋 首次创建
3 O1 B4 Q; h' j5 l7 t5 ]6 y - * [8 E8 h( _5 R3 F
- */
0 p; u/ {% {/ d1 _" A( Y - @SuppressWarnings("rawtypes")
: J* w8 _; k9 l/ J0 Z( B - public static String getServiceMthodDescription(JoinPoint joinPoint)
9 t7 E4 n6 ]* A) H& j1 r+ j - throws Exception { & y! }% Z9 d: Q4 i; ^7 j) [1 L
- String targetName = joinPoint.getTarget().getClass().getName();
1 `( l% s: K- \5 V. S' X - String methodName = joinPoint.getSignature().getName(); 5 X. K: r5 V; G7 R- U: B7 f
- Object[] arguments = joinPoint.getArgs(); 7 Q& }3 b1 S8 c
- Class targetClass = Class.forName(targetName); 0 O4 o1 ^" D: P
- Method[] methods = targetClass.getMethods();
0 s1 `7 J; e( K! @1 |2 y$ G1 Z) p2 j - String description = ""; 7 m0 E) V' o9 ~. |, Y
- for (Method method : methods) {
2 ?4 y& C! @0 K0 c - if (method.getName().equals(methodName)) {
$ ^: _5 O3 M; ^* N - Class[] clazzs = method.getParameterTypes(); _0 q3 j7 A( _8 \7 H7 B* O% h
- if (clazzs.length == arguments.length) { 3 Z+ u% z+ ^4 v- {
- description = method.getAnnotation(ServiceLog. class).description();
9 `4 G" e j: G; h: h& M0 L - break;
! ^2 o1 P0 G7 S: J; z4 A0 h - }
/ r/ q% A1 H6 h6 B A8 H* s4 X' U - } $ s8 n3 Q& w3 z& P. u2 n& m
- } ) O) v3 i6 e7 Q( j3 K- }9 V
- return description; / }+ W: Q! n4 O: Z
- } # U1 _/ @! R! h& V. ^
- }
复制代码 4 J) F) p: W5 f) O
这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。
$ L+ B N& t$ ydoAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
7 Y- D/ Q6 G( Z7 l- [& h6 ]: {4 ~- \, Z7 B, {1 q I
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)
5 y0 y6 j. _# b ~! J5 w1 Y- @ServiceLog(description="获取待审试题数量")1 i& D: W% \6 W3 w. i. ]# |- U2 E
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
( X- N' Y( L5 y2 H; q7 _1 \0 q b2 M - return quesPerDao.getAuditQuesNum(currentUser);
) u+ c) e+ W5 E. G - }
复制代码
- V! s5 q# z# x: e# }之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。6 S0 t$ D& m# s7 V3 D
二、AOP实现权限控制9 D" ]+ O+ T7 Q1 x# [9 Q+ y- |: D
上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
& }# U# F D, g& ? h% n/ i 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。
$ K5 l" c5 R2 t0 m首先配置文件要引入这样一段配置(看注释说明):$ Z' Z, p9 N/ v4 U2 f0 z
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
# }# `6 |/ Q+ A1 |% V6 y5 I - <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 ; i8 ^) p/ l5 i: G
(1)、定义注释:
/ j. E$ i) S( o$ s8 H/ e& ?7 J1.Permission.java6 b5 K1 j [( z, |: C% I8 [
- /**+ G7 M. t c- h4 v* T% s) Z" m3 @$ ^1 j
- * 自定义权限管理
* D: V0 [7 \; k% e2 H0 R6 b - * 创建者 张志朋0 R; p9 P* H$ Y" X
- * 创建时间 2015年6月30日% O, D! k7 ~& S+ {
- *6 i3 Z r( _' v+ _! }" E
- */! `( ?3 z1 i1 C" X2 i$ q0 P
- @Target({ElementType.PARAMETER, ElementType.METHOD}) & Q4 H+ H z L! d/ s
- @Retention(RetentionPolicy.RUNTIME) ' r6 x! v+ p% {" t2 l7 h1 S
- @Documented
# p" T( u! H B$ f5 {/ W1 L - public @interface Permission {) R8 J& i) @; B- L& v( i3 Y8 k }
- String name() default ""; //操作行为
5 G) X- T; ^% n: D! z/ [$ V- F h - int id() default -1;//权限值 G9 ?/ S1 h- Y1 F0 Z6 m- l1 E
- }
复制代码 (2)、定义切面以及切入点
6 `* u- {0 t7 R- |- |$ g1.PsemissionAspect.java
" K+ R2 `7 ~$ z8 u4 N- /**- w! g2 {. M( [
- * 权限管理
" m& X4 P z* l - * 创建者 张志朋
; n$ V& \0 X2 s; r5 u) c7 | - * 创建时间 2015年7月3日
- e6 {, X4 L) n0 b5 \ - *
/ C/ f; d8 u( E/ w - */) d7 g, o$ g0 z# ~
- @Component0 F: @+ u: C5 Y4 Y1 D s
- @Scope
+ A! K9 y/ Z& X7 |3 S' w0 } - @Aspect
, C8 M- S6 y8 i \! ? - public class PsemissionAspect {, f7 ^5 K1 b+ r
- . S( T3 w# j! n/ z, T
- //Controller层切点 用于权限控制
' b% I3 r( p5 E, K0 B - @Pointcut("@annotation(com.acts.web.aop.Permission)") + X( h/ a# k" Q) b9 p7 g3 H+ g
- public void permissionAspect() {8 P) @) Y* [; u1 ~5 y+ ?7 i
- ; c7 s' V0 v1 f( f8 t& e. F
- }
7 T* ]; p$ q4 C - /**
+ g. L n9 \! G: F - * 用于拦截Controller层用户操作权限(环绕通知)
# G4 j1 K% E! [& r8 u, m: T - * @Author 张志朋
. M6 T# C# U2 G M1 ~- T - * @param joinPoint void8 n0 _& g V% p2 y" ]3 O
- * @Date 2015年6月3日
4 T! _* A5 g) j/ u% N3 e9 z7 {0 E - * 更新日志
/ I: K, j. m7 N - * 2015年6月3日 张志朋 首次创建; ]. M, e6 K1 c, @0 S0 F
- *
/ F/ E! ]& g8 v, f+ ] J# K9 g - */. U* O+ j% o4 g l, _
- @Around("permissionAspect()")
! p5 |9 ^& v5 [ - public Object permission(ProceedingJoinPoint joinPoint)throws Throwable {
+ X+ w2 q! K4 q% a6 H: p - Object retVal = null;& E6 F. v* N" U" P9 h
- int role = getControllerMethodRole(joinPoint);. Y2 N6 u4 C: u1 u
- TeacherEntity user = CommonUtil.getUser();" v- P: H1 N0 C
- if((user.getSpecRole()&role)==role){//没有权限
& A3 P8 \% L0 E* b+ \ - retVal = joinPoint.proceed();
! n1 |/ U) k9 p - }else{
$ K8 ?3 U" s. P" q+ u7 K3 U- r: O/ C - noAuthorization();
$ w5 \ M; a; n' m, } - }
1 y$ q3 a/ |, M - return retVal;
. U' V8 Y% C) E0 b6 I - }) x7 ]6 n5 q6 K G0 ^
- /**
, U$ c7 B% z6 V% v0 i2 H: e* U - * 没有权限 实现跳转* y" ]9 A9 y. ]9 r3 |
- * @Author 张志朋9 _1 U1 W9 g! J1 @" q1 Q
- * @throws IOException void% p2 T/ h% j( f! Z/ m) o( d
- * @Date 2015年7月3日. z- E0 n) A8 `$ T
- * 更新日志( n1 k( M- O, x, p) e
- * 2015年7月3日 张志朋 首次创建
9 N) g( S' T7 G- m( M - *: O& _* r7 ?! i; N) _
- */
% _6 x7 u+ y# ]8 l* X/ u9 d* a - public void noAuthorization() throws IOException{
7 L. k3 e y8 j5 `7 J - HttpServletRequest request = ServletActionContext.getRequest();2 B0 ~$ i8 K8 V: G0 g3 A5 h/ v- }
- String path = request.getContextPath();
* F; }4 t' u0 ], _* `/ z - HttpServletResponse response = ServletActionContext.getResponse();2 d- U2 I8 C: e+ N
- response.sendRedirect(path+"/pages/noAuthorization.jsp");3 I9 p5 V! b5 `; w% V: t- B( k
- }
8 ]7 ?# x5 K9 b. R - /**8 s( r6 G) H8 z3 {+ L: @
- * 获取注解中对方法的权限值 用于Controller层注解
0 \8 X! C& {5 l2 R+ l4 j - * @Author 张志朋
! Q4 p0 Z# o8 ? - * @param joinPoint
# k. z) h, X2 q! R9 a& w3 B7 _ - * @return; P" o& m2 Y* V/ s0 N
- * @throws Exception int5 L. {/ n6 ?" X
- * @Date 2015年7月3日) o H, E. w2 T) _ R6 Z0 H
- * 更新日志
/ O/ @0 }8 t; G4 Y* {/ o - * 2015年7月3日 张志朋 首次创建( n: I$ ~- a5 ~& n. e
- *
2 w b% R+ E* G$ ?! f - */
r; I& C9 L! N' Y F - @SuppressWarnings("rawtypes")
( \- X5 s: {/ E+ U+ u8 m7 W# H - public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {) S3 g* d" s k8 `) b
- String targetName = joinPoint.getTarget().getClass().getName(); 2 u9 H0 e! M' [% @5 V, s
- String methodName = joinPoint.getSignature().getName();
) E1 m4 L' R4 `& x' b1 w/ r - Object[] arguments = joinPoint.getArgs();
+ A5 y w) i$ F) b- J8 _. \7 u5 t2 v - Class targetClass = Class.forName(targetName); ; ]; N1 { C% h, ~/ E+ z
- Method[] methods = targetClass.getMethods(); - C: e1 b5 Z4 C1 ^' Y) _
- int role = -1; 1 T% k0 Q3 k5 G. x6 p" Q. E% m
- for (Method method : methods) { 6 z4 e, I' ]6 J9 |! }
- if (method.getName().equals(methodName)) { 2 ?3 z, D: Q" q* ~$ T" D4 X* r
- Class[] clazzs = method.getParameterTypes(); " F* h; g/ Y r
- if (clazzs.length == arguments.length) {
1 T: n9 l! ]3 z/ x - role = method.getAnnotation(Permission. class).id(); : O# `1 J2 k2 k7 g' c
- break; & B U3 J b$ g6 b
- }
1 K) |8 q1 Z- C5 V2 i - }
! A0 \2 Z: T! S4 g) T; p4 S/ c - } 7 z9 A: R7 i8 Y! U
- return role; , a3 \# [: _8 L% q
- }
复制代码 7 l( k9 v8 m& ], L5 P. h. X
2.action层代码实现:
; h! c$ \6 L% y7 g8 u7 _- /**
' Z- q( C8 v# W/ Y* y P, z - * 试题审核不通过1 G& s/ j! p- Y X4 V7 S
- * @Author 张志朋 void# U+ y; A4 |$ i: H; c
- * @Date 2015年5月4日 Q8 T! {- Q" }/ [+ v
- * 更新日志* @+ Z1 ?8 U" J S. e6 S' S% ?
- * 2015年5月4日 张志朋 首次创建
7 o" }, n, j- J. z - *
6 }! G: m( m! M& a - */
* K& L6 `. [* s5 _8 A% y - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
1 U% {1 S- ^1 [, c* `+ t/ P - public void auditQuestions(){
8 ]4 {; {: I& c. Z! K3 z* V - try {
O: e. M* E$ `# Z - //代码实现
0 u4 o9 {3 w6 X) A# y - } catch (Exception e) {' A. y( J* I6 l: X- x
- e.printStackTrace();7 S9 R0 {% L# C( r7 T
- }
/ v0 r I4 v- k/ V5 p' Y
4 D# d& a2 C9 H- }
复制代码 : b& k$ q, p& s
" r. L5 p& o$ T8 V
|
|