TA的每日心情 | 衰 2021-2-2 11:21 |
|---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。
( W7 K/ X- d! G: T. x8 F( j 一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
4 M5 q1 m: C" z' [; ^
+ p$ N2 t4 ]6 _; s9 t& Q3 p2 u' l8 a
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。9 |; K2 y' y5 z1 R
资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
( O) k4 Q" T# ?- f/ E4 p; a/ u0 S% k
一、 记录日志并发送邮件通知+ I; W; V) ~, A0 v; A# q# M J
(1)、定义注解:
" B/ b# V# f8 i- s, W. n* u1.ServiceLog.java(各种参数类型详见上面的资料); D* m+ f- M* m& Y; p! a3 y& ^
- import java.lang.annotation.*;
* ^* R5 V" k: p) Q- r- o - /**" j- l4 Q6 ]( y: v4 A
- * 自定义注解 拦截service
9 i! I2 l9 Z4 o - * 创建者 张志朋
# J& f: d' f. B( `- d) s# w - * 创建时间 2015年6月3日
( F% C& J' m l8 s# v w, |) y4 X - *
: S4 s, b# P0 t9 C; s0 h0 O - */
: |0 k: r6 Z# D0 h' `& B2 r. l - @Target({ElementType.PARAMETER, ElementType.METHOD}) 2 L: g3 A# q, a! _, B# Z
- @Retention(RetentionPolicy.RUNTIME) , q# E' ?8 M- C& f: o7 H4 W
- @Documented
, q+ g1 ?; ?) d. W - public @interface ServiceLog {
3 y4 x; \" u8 p9 `& u2 {# Z, [( G - String description() default "";6 ]: f: p% A Q0 X
- }
复制代码 2.ControllerLog.java4 G1 z$ U) w* f7 F
- import java.lang.annotation.*;
) | B( r! Q0 w; r% t - /**; N9 U, S N1 C4 Y
- * 自定义注解 拦截Controller
* S- K8 H. }2 Q) Z" F - * 创建者 张志朋3 p# |! B8 h$ q; m& ^
- * 创建时间 2015年6月3日- A4 `% H, Y2 x$ n. h; _
- *
8 f# G' y$ e+ P4 A - */
. d& y8 |) v, s w2 j - @Target({ElementType.PARAMETER, ElementType.METHOD}) 2 p; I6 t. a; V
- @Retention(RetentionPolicy.RUNTIME)
5 S7 Y% f- Z/ {9 P) x: M6 C6 B - @Documented+ G1 [9 o( w, _9 ~7 V# b
- public @interface ControllerLog {( m6 L( r8 l# r b* M
- String description() default "";
. f- w. d; O g( h! a; J% F$ J - }
复制代码 (2)、定义切面以及切入点
W, q! i# c A1.LogAspect.java
5 E5 W. x# \6 ]' @& i- /**' \, |# h6 ^5 J# M: n6 |& }5 s
- * 日志记录AOP
6 W; r. \" S0 P7 Y- c - * 创建者 张志朋- Y- S2 K8 Y" {4 N6 j9 c
- * 创建时间 2015年6月3日
+ d! V) Y Q* \4 | - *
5 T1 r& Q5 f- K - */
5 a; M0 @. X8 c" ~3 ^6 ]7 ] - @Component
& B9 y0 K% u$ ~# K+ s$ f+ K - @Scope; b- i: Y# R, S! W1 ^, S0 m
- @Aspect
: X1 Y3 y9 s1 r3 i$ W: w - public class LogAspect {& N7 Y1 j) B2 N2 H \
- //Service层切点 用于记录错误日志
1 q; _9 l% T& T9 B) b8 g - @Pointcut("@annotation(com.web.aop.ServiceLog)") ' V, Q5 O6 o) Z- X2 s4 t
- public void serviceAspect() {7 P7 I- `1 _; _# ~) ]- v
-
: T3 H% \# F+ ?$ t8 o/ f - }
, p2 r. P9 z; f9 ] - /**5 b1 _# I3 l; z# h b. \
- * 异常通知 用于拦截service层记录异常日志
4 H4 u N1 s5 ~, v1 T+ n/ a6 m0 f - * @Author 张志朋 [" r9 g) ^. b2 n
- * @param joinPoint- i; h# S2 t& m, W; W/ A. L" B
- * @param e void6 r' H- W+ a; Q
- * @Date 2015年6月3日
0 X( x: i7 _+ p2 X - * 更新日志
. x+ S* A* F1 R$ U7 E - * 2015年6月3日 张志朋 首次创建
, \! `9 J# K4 t/ Y& ] - * D; a9 ^6 Q0 ]* j5 U3 ~
- */3 G! \1 D3 W$ _' R* }; n
- @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
8 s! H. R; {; Z1 i/ v n - public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { ) S5 o) r2 Z/ j1 A7 o) J. b
- HttpServletRequest request = ServletActionContext.getRequest();" q% ]- W* A% I: X
- TeacherEntity user = CommonUtil.getUser();9 Z7 W+ ?0 M7 T1 B1 ?. `
- String ip = AddressUtils.getIpAddr(request);
# U- ]4 L9 c9 f o3 o+ E - try {( L1 O3 L6 @. D. D) w, }
- String params = "";
4 _9 m( H: ~9 W/ Z" b. O6 @9 s' j - if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {& K5 i4 v7 K8 i9 U
- for (int i = 0; i < joinPoint.getArgs().length; i++) {# w q+ }! p1 B$ V9 b5 I7 |
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
+ @9 o0 l h; X9 C+ V& w - }
, ^; o9 ?% F* q- [2 I) G# c7 q ] - }" R8 Z, ?- i0 \) P6 R8 e
- String description = getServiceMthodDescription(joinPoint);//用户操作
% N$ `) Y+ ~/ V5 E4 v - String exceptionCode =e.getClass().getName();//异常类型代码
, |( ?' p* Z7 n1 B; Y3 k - String exceptionDetail = e.getMessage();//异常详细信息) }1 y* W, w/ R/ U: {! @" j
- String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
& G) k, i- L0 i" J - /*==========记录数据库异常日志==========*/ 3 T Y/ e) t4 V( k
- Log log = new Log();
# S& a8 j# L2 p6 Q& T - log.setDescription(description);
4 c ]; j# G9 l - log.setExceptionCode(exceptionCode);
( L: H: c7 @0 X* t' b - log.setExceptionDetail(exceptionDetail);$ Q O/ @' t4 y/ M. W y) h
- log.setMethod(method);
4 V# `: s+ O7 q - log.setType(Constants.LOG_ERROE);// 日志类型6 W2 J: `6 {: u0 @! u
- log.setRequestIp(ip);// 请求IP9 _0 \3 q2 J6 j# d. ~ q: M4 x
- log.setParams(params);//请求参数
4 X% B# u# ^. F! E6 k6 b& J6 k1 e - if(null!=user){
; [6 J' }% G3 T- ^8 c3 Y# ~; v6 i - log.setCreateUid(user.getUid());//用户ID
/ b+ y6 g' \* H' D- g - log.setCreateName(user.getNickname());//用户昵称, ` o1 V- w/ R; |1 `, H; T
- }
6 x. a8 e' e7 [, e0 n2 d3 D - log.setPlatFrom(Constants.SUBJECT_CODE);
% b% s* r4 W6 |7 H - /*==========记录数本地异常日志==========*/
& ]/ L" _7 O+ i. {0 M* m - //LogUtil.error(description, e);; m0 _! B" I* _
- /*==========发送异常日志到邮箱==========*/
: b0 W! h! O( p+ [ - StringBuffer errorMsg = new StringBuffer();* o1 u4 o: s! v L/ {
- errorMsg.append("异常方法:");4 s! H2 S+ Z) m. ^
- errorMsg.append(method);$ \8 }7 M% w' V3 T7 M
- errorMsg.append("</br>");
! j( O0 I8 |6 I! l2 d - errorMsg.append("异常类型代码:");
. T9 X6 l! u3 b' \: a3 m' Z0 C - errorMsg.append(exceptionCode);
$ O, f5 C2 u" y" r5 L- K, w# B - errorMsg.append("</br>");9 D( k# h' u# ]6 W+ u$ M
- errorMsg.append("异常详细信息:");
* O9 M& w: a R. r - errorMsg.append(exceptionDetail);/ z) z F4 G# ?2 ]2 w5 |
- errorMsg.append("</br>");) @; K; v. J: T" P6 o8 ~' I+ y
- log.setErrorMsg(errorMsg.toString());
9 f9 ], M4 T X# p7 F - WebServiceMathClient Client = new WebServiceMathClient();( s7 y* A$ k( ]+ g& |% r
- Client.sendError(log);& F3 [- D) s6 x: g1 q
- } catch (Exception ex) {3 G# y5 ~4 X6 m% D9 [0 S( r
- e.printStackTrace();
/ `. C9 P: R# T: K- A+ c8 a - }
. J: n4 Y& N9 q$ s, @% Q - }
/ o4 S5 _) Y. M' @% r - /**) {, \7 }3 c7 T
- * 获取注解中对方法的描述信息 用于service层注解 (基于反射)" u3 C: {$ a: N+ _+ W: O2 I3 H( E
- * @Author 张志朋1 \9 l8 T! n6 S* z
- * @param joinPoint. l: v! C; E6 s* n4 v% |
- * @return
! r J6 ~" T- q* d7 _ - * @throws Exception String
6 ~( U9 i2 A/ s% m - * @Date 2015年6月3日' B X6 h$ p% q. e
- * 更新日志- k" H0 Q y% {; o
- * 2015年6月3日 张志朋 首次创建
6 T G8 H# _' e) y - *
# T$ q2 d1 ^9 ?# q! f4 J: O - */" T- R6 `: J) h1 r
- @SuppressWarnings("rawtypes")
! y9 d2 x6 g2 I' T - public static String getServiceMthodDescription(JoinPoint joinPoint) 7 M! k L4 f$ [1 q) o9 F) e# y+ v
- throws Exception { " b3 ]- w' {) L0 E( c! f9 x
- String targetName = joinPoint.getTarget().getClass().getName(); + N6 L: G, N7 X" t! `$ z' O
- String methodName = joinPoint.getSignature().getName();
. E9 {0 \) a' i - Object[] arguments = joinPoint.getArgs();
8 a a' B. k( G - Class targetClass = Class.forName(targetName);
: a6 E: D* w$ {3 C* ^' t( j - Method[] methods = targetClass.getMethods(); & [7 x5 b: w: z
- String description = "";
, j' `% L5 U, y' B2 U - for (Method method : methods) { . V$ q1 C0 _% s2 L$ k
- if (method.getName().equals(methodName)) {
$ ], h2 h3 U- a* x) e - Class[] clazzs = method.getParameterTypes(); ' Z/ w& A4 L \3 X0 `9 q. ?
- if (clazzs.length == arguments.length) {
0 f# X+ r2 _) a - description = method.getAnnotation(ServiceLog. class).description();
/ [4 O: D1 j+ S# Y. m - break;
+ P9 L ]: C, p* u0 x+ u" ~8 n+ m& S7 g - }
m) d9 Y) C Y* [ - }
: A2 h2 f- w8 a4 Y - }
# F& M" A, H9 ^! ^7 l - return description; $ e& I& W" \ i" U+ I
- }
4 c) C* A: W" _" u1 K& l& A8 Q - }
复制代码
5 n m& k' k ?4 }6 ?这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。8 D8 Q# ^0 V9 m$ f! }
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
N8 k- a+ T: j/ {! N2 {5 y, }; n! g9 Z( B$ {, c* |
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)
0 H$ t$ `' D0 M2 X3 W% @- @ServiceLog(description="获取待审试题数量"); k* C8 {' `7 b$ J t
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {: p$ S: U# r7 D! w# u+ ~# R E$ |
- return quesPerDao.getAuditQuesNum(currentUser);
# U4 B. l. m: |# f - }
复制代码
9 N/ ]& v. ?7 X+ j5 z之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。& A3 T3 Y: d- o; T2 j! c( w
二、AOP实现权限控制
" g5 K0 w' f k T" e2 f+ ] 上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
* f w9 c! e/ Y* e; i 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。
1 Z) i9 m; t; ^# z- g# K; D首先配置文件要引入这样一段配置(看注释说明):3 t3 X. g. C( F
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->" q# ^1 c) j, @. m6 J
- <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 . v3 v' W: F# ~3 v0 [7 O
(1)、定义注释:
+ t) w8 g+ F+ S ^1.Permission.java
" C# ]/ ?+ H- z- R7 g% {- /**
X- ^* k& x6 `& a5 ^ - * 自定义权限管理6 v; l+ I# n# _5 ^: {' t( J
- * 创建者 张志朋( J& u; b, a! G
- * 创建时间 2015年6月30日
% {# i) ?# B9 } - *& D, \/ N# _1 Z: j# e6 i, m: ]5 |
- */
. ?+ o; Z, C: `* q4 m# y2 e - @Target({ElementType.PARAMETER, ElementType.METHOD})
: j0 K+ A- T* m* @ - @Retention(RetentionPolicy.RUNTIME)
4 U4 _7 r. J* I8 j6 _; C, P - @Documented+ M6 S1 U$ x1 k" L
- public @interface Permission {
. [# U6 N' d9 c" R( F1 t6 v - String name() default ""; //操作行为5 K# }. Y4 O4 h6 s% F$ H; b0 j+ j
- int id() default -1;//权限值/ B4 P& @' ]( {2 |: I( o: y, E4 ?; x
- }
复制代码 (2)、定义切面以及切入点
' V8 j" G( v M# E1.PsemissionAspect.java% `+ N' @' h( x7 I) l }5 b$ Z- w) P
- /**; e& q6 E, \5 @1 [- w
- * 权限管理
/ r: G/ X; e0 [. r* d: Q - * 创建者 张志朋6 y) B" J' I1 O. J
- * 创建时间 2015年7月3日( T( R3 W$ }' f: H- ]* u8 G* \0 G0 E2 q
- *
: p. B5 q2 D3 P$ H - */
7 g! E0 M- ?( _ - @Component
( s7 p0 {) d, `, ?. Q" g - @Scope) L- j8 ]9 p9 {- `) U
- @Aspect
- i' ^7 f* O4 A4 ]+ h - public class PsemissionAspect { U& K# Z- w8 x# |; y
-
, T; }" K6 Y ]+ N/ c - //Controller层切点 用于权限控制: Y/ `; [, K- D9 I) c; F' ?
- @Pointcut("@annotation(com.acts.web.aop.Permission)") 0 j6 Y- |" U P! {
- public void permissionAspect() {, w- u) Y$ ~; C# B
-
* H. h3 U& r1 W/ J; e" j - }
5 n3 C! ^! D; M- Q - /**" [- y- H/ T+ q6 }1 i
- * 用于拦截Controller层用户操作权限(环绕通知)0 x6 {* U2 T+ j; L
- * @Author 张志朋
1 Y3 j7 f1 g _+ ] - * @param joinPoint void
$ Z7 o9 e M; E& u8 j - * @Date 2015年6月3日
$ i- z S: u0 I i6 ]# q - * 更新日志
1 B O" S6 T6 c5 Q- ]" D - * 2015年6月3日 张志朋 首次创建( W9 T1 B6 j8 N& U6 r6 L* N$ O% c
- *" [% u, G2 M6 X- D
- */
0 `7 Z, n+ O5 Z' h1 N - @Around("permissionAspect()")
) Y: @1 E! p1 I - public Object permission(ProceedingJoinPoint joinPoint)throws Throwable {
! O/ ]0 C c J }; {, |5 x - Object retVal = null;
2 I# Z- m6 l# d4 R, c" E - int role = getControllerMethodRole(joinPoint);
$ L0 | h9 p8 N - TeacherEntity user = CommonUtil.getUser();
k- ]% ]7 {1 l9 e - if((user.getSpecRole()&role)==role){//没有权限1 Q# l% D) Y+ p, x' ?- m7 S9 A" M
- retVal = joinPoint.proceed();
* H- }% w5 ^+ j6 n7 a - }else{
* l! }( M7 p L1 a: y4 b - noAuthorization();2 i" u9 z+ \" R0 a0 I! n4 M$ @% w
- }" g: f+ j% m/ ?7 q& E
- return retVal;
2 n$ E. i& l: y" J7 [% s% y l( t - }2 y5 ?0 A( o& u8 ?1 J r4 ~7 I+ G
- /**
8 z) n7 F ~) N: c - * 没有权限 实现跳转! `( c+ Q5 I: Z' `
- * @Author 张志朋
2 U8 \3 U9 ?4 ?# D* v - * @throws IOException void
4 C# P9 d4 I9 f" R - * @Date 2015年7月3日
4 q8 f5 O$ m" f7 ^$ [ - * 更新日志
4 O$ d; \' Q* U' \8 ` - * 2015年7月3日 张志朋 首次创建% K5 ? e* J. X0 I
- *
+ P- Y. h: l' D0 U - */
4 w2 a9 s. @7 n- j. a2 F - public void noAuthorization() throws IOException{
6 ?0 h6 i- L" M - HttpServletRequest request = ServletActionContext.getRequest();
! x1 D) b) z' I+ B - String path = request.getContextPath();
+ F/ o7 O2 \% I }& r - HttpServletResponse response = ServletActionContext.getResponse();
& p6 G% Z9 Z2 v - response.sendRedirect(path+"/pages/noAuthorization.jsp");
D3 L( \. H! b: t - }
6 S4 Q3 _ U0 X$ @8 ?& Y - /**
6 f u# S& B0 } - * 获取注解中对方法的权限值 用于Controller层注解
" Z/ L9 |, j* }* w - * @Author 张志朋3 ^, n* @5 N4 U
- * @param joinPoint F" _' n. P$ K0 {* t+ j- K6 u
- * @return
- I& O$ A5 G: T1 U0 _- B - * @throws Exception int0 ^) B7 K" t" W% F. o$ g% |! k* |
- * @Date 2015年7月3日2 f4 {# o3 t9 i# w
- * 更新日志. Y8 X6 c7 G. R5 M! ]; o* P$ x
- * 2015年7月3日 张志朋 首次创建
( J; K: X# k7 ^ - *2 N0 C: s9 U4 M
- */
4 U3 Q- _+ W( w3 w& Q/ \1 k - @SuppressWarnings("rawtypes")# _; m. J4 h1 e4 d/ k; L
- public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {
3 B, |" Y& T* m1 l$ \* v, Q - String targetName = joinPoint.getTarget().getClass().getName(); & L2 R/ g+ Y2 g5 R; _
- String methodName = joinPoint.getSignature().getName(); 5 `6 e Z" f5 @; o4 N
- Object[] arguments = joinPoint.getArgs();
, p2 B7 B7 I0 ?" s0 T4 h - Class targetClass = Class.forName(targetName);
& M, A2 B3 b: @ - Method[] methods = targetClass.getMethods(); ( ~: f1 }- V/ L& o
- int role = -1; / T4 A3 d% y6 T `* {$ T1 H w, [
- for (Method method : methods) {
- I6 M7 T4 W, y4 j9 R - if (method.getName().equals(methodName)) { 7 r* M: w$ A. L
- Class[] clazzs = method.getParameterTypes();
. ^$ }8 n3 ~7 Y - if (clazzs.length == arguments.length) {
" |( {; k" R0 ~9 \* H. ?( D d1 [: L - role = method.getAnnotation(Permission. class).id(); % s1 p9 A( W2 t, w
- break; 2 K. w$ A: K6 J1 r2 w0 ?! l* A4 A
- } % b& h' ~4 [; A+ n; Z
- }
9 U7 v# N0 \0 Y. V" J+ N; J' |( \ - } ' ~; j% N* n% L% N% F5 N
- return role; 5 K! D- j9 O; e9 {2 g7 |4 H
- }
复制代码 % e, n! ~: a) b2 H7 J8 }
2.action层代码实现:2 U. E% O! u/ T, B
- /**, t# @+ w3 }0 P: @/ f* o% O
- * 试题审核不通过$ K& V7 ]$ o! `& ~& f4 T
- * @Author 张志朋 void
4 u2 H! }- V' @ - * @Date 2015年5月4日 W5 u# R3 m( Z" w& X7 p, j
- * 更新日志
" W7 Q! ?$ y2 S - * 2015年5月4日 张志朋 首次创建
5 I U( B: O. h3 Z5 O - *
, m$ X+ \: Y; F. E; r Y! B - */: _& F: r7 n1 c
- @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
! {& S4 f) {# ~3 s; q. g/ j- k - public void auditQuestions(){
' n4 W! z+ M. s5 f - try {
3 I$ w4 M: W! ~4 a+ d f0 X0 l/ U5 Q - //代码实现
# y3 b. S% h1 r1 b/ e! `' p& U - } catch (Exception e) {9 q1 ~9 i# f# f$ k8 r; h
- e.printStackTrace();3 [, n: a$ q3 y3 |5 H% Z
- }' ?6 f/ O! Q. T. O
. T9 d" E4 O3 S6 G% @' D- }
复制代码 1 o- ?6 k( g8 e; V
4 k7 G0 s: N8 ?) A! Z |
|