TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 + @+ k" a2 b- o% d4 H3 S. v
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
# i1 q2 h1 k% O1 _% Y% t
& F) d H( e" c' F5 I7 Q# ~3 S; K' w2 c$ H# b
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
! ?8 Y1 N8 C# b; l 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html1 s% O1 l, q. O' q$ Q
, _* J. Y% o. r一、 记录日志并发送邮件通知
1 g6 T/ {. l7 j5 {" f(1)、定义注解:
! r. Q5 j+ I# q/ W$ C1.ServiceLog.java(各种参数类型详见上面的资料) m7 \5 y/ E/ d0 r" @& q. o$ w
- import java.lang.annotation.*;
! j2 ]; H( O w# v$ s/ R - /**( c0 W& Y; t; q. A( R, `0 Q: {
- * 自定义注解 拦截service
( N9 ~$ C. E D3 T: _* M% c - * 创建者 张志朋
) T8 y. y( D9 w3 d* | - * 创建时间 2015年6月3日
4 c1 [5 l: N: P$ S, ^7 j - *
) \2 P0 C% G e' K - */
4 a% ~- c% ^7 r3 I4 @) \1 M - @Target({ElementType.PARAMETER, ElementType.METHOD}) ( [: H* t3 a9 W6 g8 [" n
- @Retention(RetentionPolicy.RUNTIME) " |0 y, |2 h" D% l$ i+ ]
- @Documented 2 X# u8 z% u! z! R
- public @interface ServiceLog { ) ?4 j& K8 y0 X' |3 \& G$ m
- String description() default "";
# b I$ {- g: R - }
复制代码 2.ControllerLog.java
1 q+ D5 g5 j5 h% D# h# C- import java.lang.annotation.*;
+ b4 T# P: ?) O$ g& X5 [' K' \ - /**
$ G# A9 h3 u2 @) y3 ]; V - * 自定义注解 拦截Controller7 c* `+ Y, H) B, {! ?
- * 创建者 张志朋7 B8 W: |, r( a) e* w
- * 创建时间 2015年6月3日- y9 ]2 \! p3 a- l
- ** o* \: e; R( J7 H) J I
- */5 p2 Q+ f0 f" Y
- @Target({ElementType.PARAMETER, ElementType.METHOD})
, w5 i) J: d1 t - @Retention(RetentionPolicy.RUNTIME) " w' D( {3 f! M$ s3 s/ }) r; u
- @Documented
! C- I4 {. z" ]/ Y# Y# b! S7 _ - public @interface ControllerLog {% ^7 d& r# q& B( R
- String description() default ""; ! r, L# m2 x; n8 s
- }
复制代码 (2)、定义切面以及切入点6 p1 P! R. t" S; c6 W, ~# |! Y
1.LogAspect.java) m9 r- C+ E' C1 v& S9 y" P6 n
- /**' h8 I" H" b! k' T" W$ S4 V
- * 日志记录AOP
) M1 i, A! H4 v4 O a0 i4 P8 ? - * 创建者 张志朋( [$ A# Y( r# ^, ~1 m# x
- * 创建时间 2015年6月3日; ~% y. L0 a- b6 P x6 G
- *
* ^! ^& O7 B+ z }% E& w3 e) d - */% S, t* F( f( @2 u
- @Component* w) n7 u, r/ d7 l6 K) R4 U5 O
- @Scope! |$ y6 e" t1 C2 ?
- @Aspect# p4 A( T' J+ Q+ y8 q8 O$ k+ S
- public class LogAspect {3 n! b5 |2 q: G6 K3 J$ {3 g8 B$ C
- //Service层切点 用于记录错误日志4 f3 e! O5 F7 i$ F" a- A- [4 J
- @Pointcut("@annotation(com.web.aop.ServiceLog)")
: N' E* m* v6 i# m( E& I3 l - public void serviceAspect() {
& N- q9 s. Y I( S5 M$ C - $ E, C+ z" F* J9 N9 I, N
- }5 S1 ^, Z- L' C, k* B3 b
- /**1 G' D- |5 L i! L# d; A
- * 异常通知 用于拦截service层记录异常日志
" O' Y2 U- ~8 R J) B - * @Author 张志朋# ?$ a7 o, {8 l. A q9 ~
- * @param joinPoint& ^8 e0 M0 H4 ~% u s
- * @param e void
, x4 o+ V% W: M5 `' ] - * @Date 2015年6月3日! b* v! i9 U7 \ a* f" N
- * 更新日志
3 G3 U$ t3 Z. S" _9 [! n - * 2015年6月3日 张志朋 首次创建
' W' R4 }; s: V4 ]+ _9 l3 z0 d - *1 }* O! S* o8 [! {; N4 Z% C
- */
H( T' h* K3 ?4 k - @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
! M {1 f4 t( |2 Q9 J4 h( G% d - public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
$ O @/ I6 B: {# [; o: w. k( g+ E, o9 r - HttpServletRequest request = ServletActionContext.getRequest();
7 _3 D$ d% E& ]0 [3 ]) K - TeacherEntity user = CommonUtil.getUser();. J0 q/ X2 D) o9 L8 b% w! Z
- String ip = AddressUtils.getIpAddr(request);
* z( M. T. v' T: v( a/ R - try {
+ _2 ~4 b$ X6 f. | - String params = "";3 b. O, P' u. Q7 n0 `: r
- if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {5 T5 y( H a8 n8 U" G
- for (int i = 0; i < joinPoint.getArgs().length; i++) {7 R* ]( o4 Q8 F) t6 f! n! I$ m) [
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";' y8 Y/ y$ U6 f* w3 E5 ~
- }
/ d- \. [7 W* [# I8 F - } m1 ?" ~) @0 }, F- z) q! e
- String description = getServiceMthodDescription(joinPoint);//用户操作5 K1 h' ?/ E# i
- String exceptionCode =e.getClass().getName();//异常类型代码
4 G0 j ?" J! h; v1 s& o# t - String exceptionDetail = e.getMessage();//异常详细信息
' ?, ~6 T, j( K& N/ y( G; j - String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
+ q% d8 Z. j+ z% I - /*==========记录数据库异常日志==========*/
& g% l* T5 Z: M8 @9 ~; c7 d% l! X - Log log = new Log();- B" U5 z; N! _( C/ B+ |5 z
- log.setDescription(description);3 P' T0 y2 V3 R) P$ N4 q
- log.setExceptionCode(exceptionCode);4 g# i$ A5 ]9 P
- log.setExceptionDetail(exceptionDetail);5 a9 ]1 n. t* `4 }
- log.setMethod(method);
0 [! [3 w7 |" f4 E6 S - log.setType(Constants.LOG_ERROE);// 日志类型7 c9 x/ _# W# N+ K3 n. v$ P
- log.setRequestIp(ip);// 请求IP
! y% K* W; ? u' e1 f - log.setParams(params);//请求参数; L: R% z: F6 k& a4 T1 J, A
- if(null!=user){
8 x# Q! C Y7 h/ ` - log.setCreateUid(user.getUid());//用户ID; g2 F g8 t$ f* i1 ~5 e0 Q8 y
- log.setCreateName(user.getNickname());//用户昵称3 g e; B! @" m! R- r
- }- u2 B* f2 l- E8 ~+ y: Y5 v3 L0 R
- log.setPlatFrom(Constants.SUBJECT_CODE);
8 m% ]. R& A' R% Y/ p1 { - /*==========记录数本地异常日志==========*/ 6 p3 B1 W: M/ J* ~
- //LogUtil.error(description, e);
/ \- s; P9 r5 k3 E% \9 F! i - /*==========发送异常日志到邮箱==========*/: K2 q. p* r1 ]5 L5 Y/ e m
- StringBuffer errorMsg = new StringBuffer();
% E+ I, b# i9 d3 v - errorMsg.append("异常方法:");% D; ~- o+ T' H' S; }
- errorMsg.append(method);- ]' j) X' G, e! B) [3 I. g
- errorMsg.append("</br>");
6 C" j6 d" j& `% n! S t' r - errorMsg.append("异常类型代码:");
# X2 d% _* m& U) @0 G* ` - errorMsg.append(exceptionCode);
; z4 R! u' J( k, Z4 x - errorMsg.append("</br>");
) L7 P9 I9 F- N* O9 p4 l5 n* o - errorMsg.append("异常详细信息:");
( n! Q B) |* n0 [8 \$ N; }# ^ - errorMsg.append(exceptionDetail);
, L( ]+ B) \, P0 I; e H8 N+ p - errorMsg.append("</br>");" O& x; U# t- Q
- log.setErrorMsg(errorMsg.toString());& f0 \+ _5 d, q) Z- z. T" h
- WebServiceMathClient Client = new WebServiceMathClient();8 C: h+ U. E; g$ M
- Client.sendError(log);; W; X4 S; J" o" w$ f2 P& {
- } catch (Exception ex) {
5 m! n) G. S2 B) l9 j0 J' K - e.printStackTrace();
6 b6 F" U2 D& t# j- i - }6 T5 G, h7 o* p! d, z8 N# ?
- } , e" A0 X \+ w' W/ G
- /**' y7 D" i8 i3 a4 [
- * 获取注解中对方法的描述信息 用于service层注解 (基于反射)
) E/ M4 {6 u) k8 Y. I# P6 V) H" e - * @Author 张志朋9 |4 I5 Q* s% g# p4 B$ U7 A
- * @param joinPoint6 K6 P" ]% C/ b$ f' X
- * @return
+ n/ |$ Q+ V; I! J - * @throws Exception String+ O: P1 Z& m/ P' t
- * @Date 2015年6月3日- T- |8 z8 c1 v) r% f$ `' r# c8 U
- * 更新日志
3 f V- k7 x1 n1 c! `4 `: a5 l - * 2015年6月3日 张志朋 首次创建& O# c( {% F9 J/ d$ `& v5 u
- *; Y% B+ a, h$ O1 K4 z& h+ ?+ ?
- */
_8 z1 o4 l# |% N* b - @SuppressWarnings("rawtypes")- I. D$ Q* p5 y8 Z
- public static String getServiceMthodDescription(JoinPoint joinPoint)
/ q6 q1 u- E8 \. C! f - throws Exception {
, U. T7 [* T( k2 G& | - String targetName = joinPoint.getTarget().getClass().getName(); 9 i( S+ N) L/ |- m+ o
- String methodName = joinPoint.getSignature().getName(); * Q4 h) j: g# u! W
- Object[] arguments = joinPoint.getArgs(); ! \3 i; v8 d: x+ c' `
- Class targetClass = Class.forName(targetName); 3 \. h/ K' W" u2 i: V
- Method[] methods = targetClass.getMethods(); " h; q. x" C# r5 J
- String description = ""; $ k! t: q% a6 ^
- for (Method method : methods) { 7 d( M# z6 t- W! v
- if (method.getName().equals(methodName)) {
% V2 t" C* ~. z$ w, J( U( Y9 t - Class[] clazzs = method.getParameterTypes();
- w1 b6 e. Z4 z5 _+ p - if (clazzs.length == arguments.length) { 0 n; c0 o. D9 Z" Z
- description = method.getAnnotation(ServiceLog. class).description();
$ N2 m. q. |; ~, B - break; ) n+ o8 [$ K) f: }0 i( i
- } 0 {3 N2 B8 t& A2 ]9 e; W: R
- } 1 P7 C1 v' q& ~1 l8 Q
- } + f2 y# m. F" O2 I, T
- return description;
9 w; e* a! L" a/ \; d% E - }
0 m0 F, L: R5 b x9 I - }
复制代码
( q* y& J6 k5 m这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。2 v3 A: Z+ N0 U4 C
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。+ {) i* q5 E, B4 a" @9 `
$ s2 v- {8 b$ l
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)3 j. U% Q' Q o6 i+ q P0 n
- @ServiceLog(description="获取待审试题数量")2 ]1 h2 @5 R, P6 t3 G$ I- ?
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
7 m9 V0 H1 i) z. j9 u5 W6 T% @ - return quesPerDao.getAuditQuesNum(currentUser);
0 y. l1 X- J. D4 ^ m" t5 n& H3 b" E. e - }
复制代码 ; Z. c" Q7 \7 _4 M) h
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。
6 r3 T8 `5 t* f( F8 ~3 O! k二、AOP实现权限控制( \/ B0 T) X) d5 q9 T/ m& s- X3 K
上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
. T, e" d' u; r6 }) }. l! r 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。
_" M3 V' W& X" G0 l i首先配置文件要引入这样一段配置(看注释说明):
# r' Z. V: D: j0 q' W* E. b- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->1 x: k% a$ B8 _1 W( O9 _
- <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 6 X' U+ I" e; M7 C" ?- ]
(1)、定义注释:' p: d* ?' m( i3 z7 T
1.Permission.java
9 e; n: W0 B- h5 d- /**2 M6 e x5 F$ f G" x( Y/ ]
- * 自定义权限管理$ v! s2 U+ W6 a# f
- * 创建者 张志朋
& h! d* A' q3 j: x* R+ L0 }/ s - * 创建时间 2015年6月30日
9 V" d/ G" Z2 Y1 A4 z - *
- d6 G3 m7 d" m - */4 p k) U' l/ M. F& n3 N+ \
- @Target({ElementType.PARAMETER, ElementType.METHOD})
6 r( ?4 _% _6 Q4 M' n - @Retention(RetentionPolicy.RUNTIME) 4 ? M( \0 P- A: u( ^1 U5 K
- @Documented
' w- O7 q+ [6 J: e' V# Z - public @interface Permission {: y2 T' e4 W" A; ~5 u
- String name() default ""; //操作行为
) X) r4 W2 y7 f- i - int id() default -1;//权限值
6 h: |# k6 S: U. l( n0 Z' U. G - }
复制代码 (2)、定义切面以及切入点% n5 z6 L0 U. t( t1 T
1.PsemissionAspect.java8 K- U, E, z) L0 r1 @+ v
- /*** U6 F4 {9 n a* ~ F( j0 Y
- * 权限管理
9 n* ]9 f% |7 z1 z Z4 H - * 创建者 张志朋
2 ?, A# V4 f8 H1 Q0 i - * 创建时间 2015年7月3日6 ?. F! d0 x2 ?# A
- *4 i1 Y; d1 P$ y! p! G9 O
- */
' k; Z' C7 ~* u4 F% f+ f - @Component: Z: {- n& T1 }, t3 q- c
- @Scope+ k p5 ?4 ^7 K* K7 h# e
- @Aspect
* _ @6 ?2 G. M$ ] - public class PsemissionAspect {. d+ S" c) k$ y) x
- , b2 G: n7 {; R& y! p
- //Controller层切点 用于权限控制
+ r2 i3 G }% a J T- \" S$ U - @Pointcut("@annotation(com.acts.web.aop.Permission)")
) a f1 E. W% M5 `3 D( Q3 t; h1 M - public void permissionAspect() { x _7 d9 b. `' H
-
/ K& j. G2 C2 d! ^1 u6 C. m0 ` - }
/ r& c" e* q* P6 V5 u5 a - /**
# {* N4 l1 |/ f, M4 p R$ u - * 用于拦截Controller层用户操作权限(环绕通知): [) p. H* H' U
- * @Author 张志朋
) m! p) k* q% u- \ - * @param joinPoint void
' `3 W B' `8 B2 e: M2 P1 H% f - * @Date 2015年6月3日2 W4 }5 S9 z. }+ X
- * 更新日志
0 [0 I! [- B. s1 b" X - * 2015年6月3日 张志朋 首次创建
/ b" s% g% f# {1 S3 B$ T! n - *. O1 \" u8 N2 t: y, d$ U' v8 {
- */5 `' N9 Y# Y6 c- u) |4 X$ F# u6 T& b
- @Around("permissionAspect()")
* {. C. A# x" ^0 F* T9 ? - public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { 5 N3 V) C* F# w2 K% N
- Object retVal = null;7 ^, J6 J! b |5 v0 h3 ?
- int role = getControllerMethodRole(joinPoint);4 z) h9 m; t ?0 U
- TeacherEntity user = CommonUtil.getUser();
7 N% Q& y: j2 Y' E - if((user.getSpecRole()&role)==role){//没有权限
3 Y" k! {3 v* S5 w - retVal = joinPoint.proceed();( Q( Z4 C, @* P
- }else{8 F5 O1 i8 S. [
- noAuthorization();$ ~. T. b+ W4 Z) K! v h6 w
- }
, n7 j: c* e# d3 d. ^ - return retVal;8 d3 q3 Q5 i5 ^7 F# |
- }- o3 h4 e6 `/ r9 a/ \( @
- /**
8 d i, t3 v2 q j- ~2 d) L - * 没有权限 实现跳转7 m' N* d- K; m! |$ x, r; q
- * @Author 张志朋
' }; a+ `& l4 B: K- W3 g - * @throws IOException void
6 l4 g$ i9 P: U- {& z- Q - * @Date 2015年7月3日
/ _, H0 y# z5 Q' Z$ ?6 h. u - * 更新日志* Z) p0 n, `' v: m1 r" A
- * 2015年7月3日 张志朋 首次创建: w( x. B( X. F- S
- *; {0 j" n* l8 r# _! f
- */4 Y- `. \7 Q+ ?5 q0 v
- public void noAuthorization() throws IOException{
4 Q: ?0 t+ Q; Z& d& J; V% U - HttpServletRequest request = ServletActionContext.getRequest();& K' U! c8 U U3 ]& u) g
- String path = request.getContextPath();& y( Z. |# i( }( D4 s
- HttpServletResponse response = ServletActionContext.getResponse();
$ n5 j$ r% x- o7 A - response.sendRedirect(path+"/pages/noAuthorization.jsp");0 E6 O) i. O) T: F( D
- }
6 E, q$ B' V a' G, O7 W - /**0 Y# N. {- Q5 c( b# n% }
- * 获取注解中对方法的权限值 用于Controller层注解 . r3 s% p9 [- d. A, E9 S
- * @Author 张志朋
" D3 N0 }: L: \- u6 r* \9 q/ S6 Z6 N - * @param joinPoint2 E. n& d. m& Y7 t
- * @return
4 h2 b& p, _0 K% _$ y, S8 K* b - * @throws Exception int
9 d- [+ |8 V* A! E* G4 R* ]9 E - * @Date 2015年7月3日
5 c) e* O1 v6 b- ^' D0 R2 D - * 更新日志* T- ]* H/ _8 f2 P
- * 2015年7月3日 张志朋 首次创建9 l4 N9 T( r( N" k! h3 R
- *3 v( U* Y- |5 y% v9 U$ O' n. ^
- */0 e+ E/ c) O9 V; J0 S N
- @SuppressWarnings("rawtypes")
) N) Z8 C, G/ M( s, v - public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {+ x& N7 n7 T: M
- String targetName = joinPoint.getTarget().getClass().getName();
3 s# z E4 I5 h( e! H$ H* @ - String methodName = joinPoint.getSignature().getName();
0 m5 P7 P+ E3 L- M0 f$ k! \3 h - Object[] arguments = joinPoint.getArgs(); 3 U# ~# l8 }+ ^( ^0 d6 B) P/ v3 c
- Class targetClass = Class.forName(targetName); p9 \2 d3 O* R
- Method[] methods = targetClass.getMethods(); 1 D9 t+ p. L8 R8 W
- int role = -1; 7 \/ W! l' T0 [9 T( z1 ~
- for (Method method : methods) { 6 z! E4 l7 X0 y$ [. ?# r
- if (method.getName().equals(methodName)) { / a( Q# ^) ~% D; i# h# c2 T
- Class[] clazzs = method.getParameterTypes(); ! F$ a9 }3 a8 S5 F! c
- if (clazzs.length == arguments.length) {
! G" ?, ?$ v. p5 g# U% y - role = method.getAnnotation(Permission. class).id(); - ^) Z- J% j* w6 r# {& z/ Y6 b2 J
- break;
" e7 K5 Y6 }& G( L+ P3 a! s - } 2 s' d3 h2 l& A; D/ P
- } 4 b: s( ^1 l' z% L0 k
- } ' ?% P% x+ Y8 \% s* R. n( \
- return role; 4 {& Q8 x" Q+ Z0 T2 ^- U
- }
复制代码
" J& q, c7 q. L) `4 m! w2.action层代码实现:
[ s7 [& A- k* V; x a6 f- /**
3 E( \+ a# T2 _' j5 I! T4 d! g - * 试题审核不通过7 J" o, F9 R2 K! q) _8 r; a9 ~
- * @Author 张志朋 void
" R; R* h9 E. W0 ^ - * @Date 2015年5月4日
2 d. O( U6 \6 I; r8 Y! n - * 更新日志/ p* {& S. f1 I. h
- * 2015年5月4日 张志朋 首次创建, x, _! O: j& |! O
- *
* J% f+ W3 ~, l- W+ Y: Q - */
/ T2 R5 x% Q7 B2 n$ r0 f2 Y0 E - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
: x2 ^% F% P& O' \- v1 U3 I. Q - public void auditQuestions(){" f' p9 c- N: g* Q" `& Z
- try {& C9 ]- z8 ]2 b" W" l8 u* `' S
- //代码实现
4 \1 W9 h! c' }# Z n& r9 e - } catch (Exception e) {+ b# P) c ^' v
- e.printStackTrace();2 H+ j+ c, H9 j0 j% {1 s7 G" M% C
- }2 C9 D$ o8 g8 _
- ( Q* h8 K2 `2 G5 X0 ~0 s3 x
- }
复制代码
5 l) e, g# Q: f& E1 d1 a- H2 k; G. z
|
|