TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 , X4 G: n, M8 p$ u* C' O
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。" g: }0 D7 w# U# B* m, \
3 }" i' I" O0 @# ^+ S6 i* {! I5 ^# m! Q0 [
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
; c$ z: Q5 F- M 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html! a8 b8 I; z- q5 Z% K
) C1 } O, J2 {: a+ A1 M一、 记录日志并发送邮件通知
/ D. X l& H& J1 ?* ~( t3 A(1)、定义注解:+ K$ D3 l$ ? n$ ~
1.ServiceLog.java(各种参数类型详见上面的资料)
# _2 ]: \& I/ C& A9 }6 t- J" B- import java.lang.annotation.*;
( ^) S; b% T9 |- `1 Z! D - /**7 p: @% t9 `, Q( ]8 z
- * 自定义注解 拦截service 8 D+ f8 J) B: n. y. Y. d
- * 创建者 张志朋
% D: g# N9 \3 ~8 i- [# y - * 创建时间 2015年6月3日
2 ^6 {( ~8 |) J5 v" _3 I - *4 N# h% T; c* l2 e# F
- */% k. p" o6 o x1 h3 L
- @Target({ElementType.PARAMETER, ElementType.METHOD})
3 R( E9 Z3 u4 y - @Retention(RetentionPolicy.RUNTIME)
$ w# S, X3 l, J- B8 v - @Documented
' n6 |+ u* F8 O+ n$ _" [ - public @interface ServiceLog {
# S: M% ^" L4 e% K% U" s - String description() default "";
1 ^( ? e& `! j/ c, X" {6 s1 _ - }
复制代码 2.ControllerLog.java1 x1 ^, m* k F
- import java.lang.annotation.*; " c" t8 `3 w1 T3 K" G# u* m; Z
- /**9 {( L* B4 l+ E( k. H
- * 自定义注解 拦截Controller
6 v" w, b; w! T4 ~% m - * 创建者 张志朋
& R% e2 V" w3 e - * 创建时间 2015年6月3日* `, U4 o3 c# @/ r& y
- *5 ]& o0 Z) k% D3 [
- */
& h7 {1 o% e& d+ @" v2 I- s - @Target({ElementType.PARAMETER, ElementType.METHOD}) 7 z: q" U; R/ [9 ~% T* L
- @Retention(RetentionPolicy.RUNTIME)
$ K* q+ c- d4 G! k. }* k - @Documented
8 ]# G6 `# G6 A. O' m2 ~ - public @interface ControllerLog {7 u1 `# T( ], \1 L
- String description() default ""; " C/ f. S' K8 H4 [4 M
- }
复制代码 (2)、定义切面以及切入点4 [/ c' S: ~3 u7 `6 l; e6 |: o
1.LogAspect.java
8 {3 a+ V& V' v C+ h7 T9 d- /**
$ _- J- R \' V0 J5 R) o- t - * 日志记录AOP
. D# k+ X7 b' g& m+ b+ W: Y - * 创建者 张志朋/ u) L( X2 k/ o d: J. y
- * 创建时间 2015年6月3日+ i$ s/ |/ p3 d5 u
- *
, p- Z& a; ?- i' Y - */; p0 @' @' e7 _& C/ {
- @Component' P# {7 b- @) M( F6 t% {
- @Scope" L x$ c" d6 x
- @Aspect" v: e/ H( [/ P, [' r5 U8 D! ^% f
- public class LogAspect {4 @0 ~- z5 u' { e
- //Service层切点 用于记录错误日志
0 u) ]) B( A L! ~* u; P6 V - @Pointcut("@annotation(com.web.aop.ServiceLog)")
( `) [0 T7 B* ]8 F0 u7 I! n - public void serviceAspect() {4 ^5 f' Y# |( C/ T
-
* ~3 x1 |% @, @ - }
# e8 D' V9 Y, ]: I - /**
: b7 w) @/ K+ s2 B/ X1 I - * 异常通知 用于拦截service层记录异常日志 ; U Q3 O: N4 r9 t) H* }
- * @Author 张志朋( i8 ^% s; i- o; {. O. @# w
- * @param joinPoint1 C8 h' t6 A- O: m
- * @param e void) q9 Q& A5 H0 `# X
- * @Date 2015年6月3日
8 r2 z, c& {8 l$ J9 n1 i) N - * 更新日志
4 W8 L. c& B# G1 h+ n! J7 I - * 2015年6月3日 张志朋 首次创建
& a$ n, M d* Z: H% ^, ? - *
8 k& h2 ?5 o+ Q6 j - */4 `5 q% Z0 p, d+ Z' @
- @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
9 t& K' C% P8 ]- F( ^, p - public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { , v1 R4 M, A# h
- HttpServletRequest request = ServletActionContext.getRequest();0 s7 {. ?4 m- ?
- TeacherEntity user = CommonUtil.getUser();
5 U3 e: W! v. P a2 |5 B: a. T9 D" z - String ip = AddressUtils.getIpAddr(request);3 P7 x& ]: C% L% H t5 i
- try {+ j% a9 A/ k4 ^1 v) d3 y+ Q
- String params = "";
# n q3 ^) C+ R$ i4 c2 y6 O - if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {2 X" W" {2 q6 h/ q! l! \
- for (int i = 0; i < joinPoint.getArgs().length; i++) {. G r2 s) Z) D7 W6 V6 z* j
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
. [/ G7 ], o1 Y5 L. L - }" P' h3 F8 f1 \ h
- }+ L' U' e. y" `0 C1 g
- String description = getServiceMthodDescription(joinPoint);//用户操作" ~7 H/ O5 Q5 F P/ w ?8 {
- String exceptionCode =e.getClass().getName();//异常类型代码: J% Z. m" Y/ }& n5 M4 ?+ P
- String exceptionDetail = e.getMessage();//异常详细信息
6 N$ t: D1 n" N - String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法9 h6 i! s$ y) F9 B1 H7 s; |* j( _
- /*==========记录数据库异常日志==========*/ , ^6 n A$ N$ s
- Log log = new Log();: _2 S2 N( P# U8 t1 A0 t# M9 `
- log.setDescription(description);. ^/ Y. ~1 h+ `& F
- log.setExceptionCode(exceptionCode);5 w0 \6 R0 [. F$ q$ l
- log.setExceptionDetail(exceptionDetail);
( d! y& I/ Q: | d/ V6 Y: g - log.setMethod(method);
4 \( V. v1 d7 g' d, @+ I$ w - log.setType(Constants.LOG_ERROE);// 日志类型 a* v) @4 G( f
- log.setRequestIp(ip);// 请求IP
/ D8 ?7 j$ i2 }( `' z) w) Z - log.setParams(params);//请求参数
% V/ p. M' }" g) C( R1 q - if(null!=user){8 M* q6 C z# \# ?
- log.setCreateUid(user.getUid());//用户ID
+ _, T, j( F% @' ^! l6 B/ i - log.setCreateName(user.getNickname());//用户昵称, f( i: k% s6 }6 c# [
- }, h6 ~0 G1 x$ M5 O- N
- log.setPlatFrom(Constants.SUBJECT_CODE);
% ]% F7 \5 C, G3 s5 ?1 S - /*==========记录数本地异常日志==========*/
P& T; R) V2 R7 G# i$ l- N! ` - //LogUtil.error(description, e);
9 w3 V( @. d/ Z, ~: ~* }4 C - /*==========发送异常日志到邮箱==========*/
& y4 A, h }5 `; ?! E+ [/ z - StringBuffer errorMsg = new StringBuffer(); W" \$ [* O. u
- errorMsg.append("异常方法:");
' G1 d5 K' |8 i - errorMsg.append(method);
5 c/ l. q H: S - errorMsg.append("</br>");, ?1 p; J$ [/ L! n
- errorMsg.append("异常类型代码:");
$ m# r, x1 V4 c- k/ F- r - errorMsg.append(exceptionCode);
- Y4 C7 J7 X% `- g6 z, L) t - errorMsg.append("</br>");
5 u1 ^' f4 X+ ^' Y+ B* i - errorMsg.append("异常详细信息:");
/ L# W7 y$ p$ v" i1 c4 E! Z - errorMsg.append(exceptionDetail); E. o, A0 z( w. r5 e. n
- errorMsg.append("</br>");/ T+ H. G" h F; A5 O1 F9 m
- log.setErrorMsg(errorMsg.toString());
1 E. W! v' }6 k' P+ n; F - WebServiceMathClient Client = new WebServiceMathClient();
+ h* |, R" K! u( o; Z5 S' V/ _ - Client.sendError(log);% ^' A- |( \5 T8 }/ [
- } catch (Exception ex) {
6 s: p2 w4 R Q* E/ e - e.printStackTrace();, _& I$ M* J* \: ]# B; g; L( a
- }! {* p) p3 e# V( l. u5 c% K
- }
, e# R3 d9 l; J9 | - /**
3 f2 e! T9 Q( J3 R$ I - * 获取注解中对方法的描述信息 用于service层注解 (基于反射)' U4 O, k; c; X; F
- * @Author 张志朋
( k6 A( p0 e% I2 }0 g - * @param joinPoint4 H9 _' F$ O2 m n* e: h0 e, q1 s
- * @return
7 I9 ]6 }/ L" J7 K& {( l - * @throws Exception String
* Y5 x4 A. H9 y/ ]- f/ b - * @Date 2015年6月3日, B. b6 N6 y3 U8 x+ ?
- * 更新日志: @, G( p( X9 j; J
- * 2015年6月3日 张志朋 首次创建
3 G" U% C' ^( E: y0 G - *7 a" W$ u& w \
- */. ]" Z7 @) c& d0 l( m/ ?9 V/ c1 f
- @SuppressWarnings("rawtypes")
( I1 e, w3 v2 y - public static String getServiceMthodDescription(JoinPoint joinPoint)
8 Q4 u" g: w/ V8 m+ {% x - throws Exception { * J7 [5 d6 _4 U* o
- String targetName = joinPoint.getTarget().getClass().getName();
: r! `9 Q0 Z" S: K - String methodName = joinPoint.getSignature().getName();
6 S7 ~4 r9 X- S0 s! u C, Q - Object[] arguments = joinPoint.getArgs(); . t) w( K* l* m' v: q8 o4 S
- Class targetClass = Class.forName(targetName); # J; ]1 Z W0 s K
- Method[] methods = targetClass.getMethods(); 1 e7 D" Z2 g9 S: ]0 h
- String description = "";
: S4 y5 h5 B3 [; b- O - for (Method method : methods) { 0 J+ ~) z g0 j, Y% t, H$ Z
- if (method.getName().equals(methodName)) { , e7 e& x5 U: B; N: `8 O6 [1 k$ I
- Class[] clazzs = method.getParameterTypes();
3 e' G, D& r% [8 n - if (clazzs.length == arguments.length) {
+ r$ A- Z7 _ p. c0 z$ B - description = method.getAnnotation(ServiceLog. class).description();
; r+ @5 ^, I3 h9 J& M - break;
: x4 [8 N& t8 @% P - }
( v' a# N9 f% p' P; O: |' O# t - } 1 M% @# l8 u, O5 y% r
- } ) {! C: i+ M- x! N% M
- return description;
% m2 g: f$ i/ g$ d6 T - }
5 H9 e- ], L! u2 u! M: h - }
复制代码 9 t$ p# A) q2 @# Q, n4 x
这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。$ l& l) Q R- r6 U5 y3 E
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。9 M( ~. x* t- T# E- G
5 K$ T7 C: G4 p, _. ?9 F
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)! C' D- y6 x2 D3 t( c3 a; i
- @ServiceLog(description="获取待审试题数量")0 {$ c# g" M: g& |% Y9 y! O% G
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {6 q" \! \+ i5 V( e
- return quesPerDao.getAuditQuesNum(currentUser);
L; y3 m' S% r1 a" |, w+ c - }
复制代码 ( v4 Q7 d$ R; e0 t# Q2 K3 E( E1 r( [: f
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。- U0 j) Y1 n1 ]5 N- C. a
二、AOP实现权限控制
4 [3 e2 q! a6 B4 v+ L/ D 上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
$ y m, f- m9 N3 o: y7 t 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。9 k- L! T: O8 t+ }$ C$ k
首先配置文件要引入这样一段配置(看注释说明):' e0 ]- R! i% x% O
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->4 H, q. v. ?$ o% k8 a+ \7 K* b
- <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码
9 [$ E, e; m' F(1)、定义注释:: p6 R0 W2 h$ d Y4 S E3 ?
1.Permission.java
8 m/ [/ L; r1 ^8 O4 y* ^2 n1 N- /**1 W) k+ B7 w* e. x* \1 j
- * 自定义权限管理
, c! C. q- g5 Y! ^+ ?( @ - * 创建者 张志朋
$ d" e) }8 B3 O6 u8 J$ s - * 创建时间 2015年6月30日
' V2 B0 y( ?, s; c! t - *
- P5 ] N; P! Q- A6 F - */
# \( {0 D! w! w/ G - @Target({ElementType.PARAMETER, ElementType.METHOD})
( [, }0 [4 }( s% B* |3 I - @Retention(RetentionPolicy.RUNTIME) 2 b1 G# c1 l9 _4 J* |) d' K# P
- @Documented
+ m9 s0 T3 N$ ] q - public @interface Permission {
# N9 h6 b% K3 ^9 [' r& R - String name() default ""; //操作行为
0 p1 v( t1 D* z8 r8 ~, Z0 i - int id() default -1;//权限值
' x( E2 _7 l/ G - }
复制代码 (2)、定义切面以及切入点
: h# K! J5 C8 \' ` f3 l1.PsemissionAspect.java
5 \7 u$ u0 z+ B6 B' B- /**4 g3 s+ G! a$ h) G6 U
- * 权限管理8 F2 {5 f( M [6 ?% s$ X6 u' s
- * 创建者 张志朋
- W1 k9 w7 A$ V7 e' x7 s! R4 I/ Z1 z; M - * 创建时间 2015年7月3日
& @; n0 M* ]$ W8 i5 N4 Z - *# P4 j5 d* b; w% B( P. d/ ^/ r
- */% n9 }0 y6 q2 I. Y
- @Component" [7 ~( C1 c5 h+ Q$ v1 z
- @Scope
, G. U5 p; X5 x4 {; `! q% z - @Aspect8 n2 x" l; C& c/ {; l7 f# Q. s
- public class PsemissionAspect {( O3 T9 K3 r) O# b8 E0 R
- ) y' y- P7 M; L- J0 e- _9 g
- //Controller层切点 用于权限控制
, h6 [2 M0 v7 l+ K+ A: c0 M7 C - @Pointcut("@annotation(com.acts.web.aop.Permission)")
\6 t' Q2 p K9 `0 O3 b" j0 Z7 ] - public void permissionAspect() {
0 o# p, a. c% c$ n - $ v( f9 O' S) {' V7 i9 ~
- }
! G5 d7 P9 }/ i$ X9 O - /*** \2 p! J# r9 x h
- * 用于拦截Controller层用户操作权限(环绕通知)0 [3 v& n+ _" q8 F0 C
- * @Author 张志朋4 U# R; P: D1 u6 H/ ?1 A1 B" S2 w; Q7 B
- * @param joinPoint void F, d1 n& f u2 z" z
- * @Date 2015年6月3日- m4 G0 e# x; E% {; j
- * 更新日志
) u5 V; a0 R, F' j' C$ ?. z - * 2015年6月3日 张志朋 首次创建' M2 Q2 u* t0 y0 f
- *4 E& Y7 c, u( G6 H, c
- */
8 t1 h( L& d: ?( o! [3 m - @Around("permissionAspect()")
! N! ~" _. _( q* e8 z - public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { , R- D' s7 C8 w) k; s
- Object retVal = null;
( q |* o1 z& K* V* @3 @ - int role = getControllerMethodRole(joinPoint);6 O1 h. \& O: n9 q' w
- TeacherEntity user = CommonUtil.getUser();+ ]/ z" A* t! ?% X- f) Y
- if((user.getSpecRole()&role)==role){//没有权限+ [7 i( s5 g: Z# y
- retVal = joinPoint.proceed();
) S' G8 M+ Q' m/ h' q, T - }else{0 K4 S. J! c0 n( A; ?2 t# p2 R8 `) v
- noAuthorization();" z# Q9 B- g2 f' C
- }
/ e1 \+ G6 y3 }. T8 L! q - return retVal;7 r9 k9 M( {8 W/ U& D2 E
- }
7 @6 E+ H2 _ D8 i7 ]4 Q - /**
2 n# O# m8 f9 O2 _! Z5 R$ G. f - * 没有权限 实现跳转
. |0 @% f$ a9 v! x* J% [3 v' g - * @Author 张志朋
7 W/ ~2 J2 G( }9 A+ Y$ j _- D - * @throws IOException void+ z6 s+ {3 V" b" }8 v" g5 {# e# S
- * @Date 2015年7月3日1 j, p* Y7 u+ ^) O; f* O! T& g1 r* F
- * 更新日志0 X+ Q. |0 P$ H: w8 ?
- * 2015年7月3日 张志朋 首次创建
& [7 I- B6 r" Y9 n - *
( }6 {6 l. |5 K8 c' W# `3 q - */* c6 y1 o# o; s0 N9 V: I, v
- public void noAuthorization() throws IOException{
$ V- s+ c; U5 U% Q" c3 m - HttpServletRequest request = ServletActionContext.getRequest();
B6 f( h2 z: y M# D - String path = request.getContextPath();
; H7 [; C+ S4 A2 s/ u" y" ^+ U - HttpServletResponse response = ServletActionContext.getResponse();
1 J/ Z( ?0 l( z$ E - response.sendRedirect(path+"/pages/noAuthorization.jsp");
0 C5 m6 h+ @: k8 n; ?7 M5 C3 k* v - }
3 V5 w; v! _3 U - /**- c/ H0 z5 z7 K/ {
- * 获取注解中对方法的权限值 用于Controller层注解
) Y- N+ }. ^9 ?+ i2 } - * @Author 张志朋" U$ r. s) x) _8 d! N9 R/ D, w
- * @param joinPoint' Q/ y% A9 ^6 Y7 i/ x T
- * @return
5 c$ x |$ j" I* S! I C; F - * @throws Exception int1 I4 g4 {7 k- U C$ q' w% i- X
- * @Date 2015年7月3日' @9 K- ]( j1 J& b) D9 D
- * 更新日志8 e/ _, n( Y! n+ d: Y+ r5 O3 J
- * 2015年7月3日 张志朋 首次创建
2 K1 z# ~ W- w$ ]7 R+ o- T. Y& I - *
: n- r, @" H' ~) k - */
- U' x/ g/ Q+ Z6 T - @SuppressWarnings("rawtypes")
! h+ S% X/ T7 {8 q/ d8 v' t - public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {" U, C i! N/ o. M
- String targetName = joinPoint.getTarget().getClass().getName(); 1 L4 D8 H. H; |8 M
- String methodName = joinPoint.getSignature().getName();
2 f; o. r3 U1 Y. w - Object[] arguments = joinPoint.getArgs();
) ?. O3 X& n. V9 ?6 B - Class targetClass = Class.forName(targetName); $ y: H( L+ q* N; j; ?6 B
- Method[] methods = targetClass.getMethods();
# Q( R2 |* ?$ z% t0 F - int role = -1;
# h( o2 m) u" {9 V# \) a+ Z - for (Method method : methods) {
. _4 ]/ W6 ]( \( T; ^! F: F - if (method.getName().equals(methodName)) {
" V( a5 r9 ?6 ]* y; y - Class[] clazzs = method.getParameterTypes(); 7 W3 \6 i1 u1 O5 @+ g, l$ F' g
- if (clazzs.length == arguments.length) {
1 @" t O- J& E' ~, k) V - role = method.getAnnotation(Permission. class).id(); : _+ s. y) r2 Y$ B5 z' _
- break;
0 @1 y" P$ Z4 u1 i - } m& ~% w+ @$ s2 z8 J% k8 b- y
- } , y, _6 o. q. y6 g8 f; H$ ]" x
- }
2 ~4 K. l1 L/ Z( ]3 L - return role;
/ x' t9 E! B9 a. D" A! i: F8 A# d - }
复制代码 3 f8 U/ f& j2 h$ z, y, S
2.action层代码实现: e q' \; z5 }' U! a0 S) W. m5 `
- /**& g' n6 l D* F3 m% U
- * 试题审核不通过
$ S6 Z% E7 D9 L! [. V - * @Author 张志朋 void
c2 W& U1 m4 B. g - * @Date 2015年5月4日 D, }: x0 \/ H+ Z2 N
- * 更新日志# Y7 G2 S1 u/ `8 l* V2 w3 v( ]# _( H& S' q
- * 2015年5月4日 张志朋 首次创建
8 }1 U, n1 ?" U% n' e# G - *8 I, f: W4 W/ x1 M$ t. B
- */
( N8 v7 ^9 F) P" ~. F - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)1 @6 R1 H; Y& O& J$ p
- public void auditQuestions(){
9 u- u, R* z ^: m* }% ?5 n - try {
a. l! f3 o+ B' U7 f3 D$ L - //代码实现
! f" \$ z3 ?) f1 o n - } catch (Exception e) {* c) T" m* A4 t) k2 N7 Y9 D% E
- e.printStackTrace();% x2 l2 b% q! e& s2 [
- }- g* G+ Q& b! w
- . E5 [* F: b6 i
- }
复制代码
+ F+ B9 o" [& w9 g' w$ o' Z- s
4 S, U+ M/ U* J% [ |
|