TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 - P) I* w( R! W0 x& y
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
, R5 v6 J6 u; k3 B8 B: w
0 n( }; t( c5 t, b0 K* D' r
* O' q3 ]* f: B( @4 b# m 鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
# \! y1 C1 s" f# q( a6 g0 I 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
1 X+ a3 G1 A4 s" G n
1 J) v& P% a2 m3 U& |8 V一、 记录日志并发送邮件通知- a' q4 |' X7 Y8 X
(1)、定义注解:2 {* l; f; Z8 a- q0 c$ b
1.ServiceLog.java(各种参数类型详见上面的资料)5 l H$ Y; M9 Q$ |
- import java.lang.annotation.*;
l* L7 f( V* n- d - /**% ^& z- w) k8 x$ ~
- * 自定义注解 拦截service 9 |1 ^7 P; L. |7 ]' {9 I' ]; R
- * 创建者 张志朋
j+ ]* j) U8 V8 R0 `! h+ |2 J - * 创建时间 2015年6月3日
6 J7 J9 J6 a! \. s6 P( x. L. I% y - *
/ K3 q' i, E/ f6 h - */
" s- ]# K& a! b+ K2 J9 x - @Target({ElementType.PARAMETER, ElementType.METHOD})
# H% d k2 l ^' i& q( s1 W* Z - @Retention(RetentionPolicy.RUNTIME)
* ?3 X& _( M/ ?& C' y - @Documented , f7 c. j. G; f0 ^# ?9 f
- public @interface ServiceLog {
- ?# ~( s# t5 J - String description() default "";) k. o, y7 m! g# v9 b$ r% S8 M
- }
复制代码 2.ControllerLog.java
4 J8 P& R0 Z9 Z; Q- import java.lang.annotation.*; - w! @% I0 S. I# S
- /**
5 K: j/ e& T) V8 p - * 自定义注解 拦截Controller0 b7 J' W; ?. t: B. q: z7 v6 A! T6 ^
- * 创建者 张志朋
7 G' A' c# k) \4 ? - * 创建时间 2015年6月3日
) b. f1 E2 z. f - *9 @6 ? q' _# Z
- */5 d' f7 O3 D) V+ a
- @Target({ElementType.PARAMETER, ElementType.METHOD}) / y" z3 d8 o& S7 @/ A* w
- @Retention(RetentionPolicy.RUNTIME) ; Z3 o2 L2 V+ A8 V v' `
- @Documented2 ^9 m( T6 n' U1 b Y
- public @interface ControllerLog {
6 {. i) g$ j# H$ L& |/ @0 A. a2 J - String description() default "";
9 N- K' ^: L3 O8 D q# L - }
复制代码 (2)、定义切面以及切入点& N2 n! m" ^; u/ I
1.LogAspect.java
2 L" V; B5 t" U8 H$ I- /**. h5 X8 J- _; T+ [7 Z8 v8 P$ o$ g& D) h& N
- * 日志记录AOP$ X1 c4 a6 L1 x- @& E" n
- * 创建者 张志朋
; V( o p' b% q8 ]" w8 c& q' ` - * 创建时间 2015年6月3日! u# _ g1 I: s; v
- *" x7 g$ C! k6 i" ]' u
- */
% G. j$ b1 s3 v, u+ G2 R - @Component) Z2 P, c$ i' A6 X
- @Scope& v* P7 {6 h6 k& s3 |9 \7 x( c
- @Aspect
" C: E* a# e B+ ?; H/ Z# l, b - public class LogAspect {
# ]! f! E t8 g4 f2 a - //Service层切点 用于记录错误日志
3 ^" Z; l7 [1 a - @Pointcut("@annotation(com.web.aop.ServiceLog)")
! H3 \, T9 J0 Z0 I, k - public void serviceAspect() {/ G' {: Z4 k/ O6 d2 c m! B' `
-
/ o" n8 e" h% V! u* e - } K+ Z5 f6 W8 E8 }$ [
- /**
3 p- x0 Y- ?3 k' U8 j6 @/ r/ k - * 异常通知 用于拦截service层记录异常日志 5 Z6 L K! s0 H2 a' C: i% e2 G
- * @Author 张志朋
! [% r, p# j9 c: P! f$ `5 }7 v - * @param joinPoint* B" f$ {' G7 c j0 x, f
- * @param e void; A3 V y d# j8 \9 b
- * @Date 2015年6月3日
5 Z7 h; L" m5 u- g0 @5 g, k - * 更新日志
, m' O l. l8 b8 p4 y$ i% c - * 2015年6月3日 张志朋 首次创建
; M0 i' L, T4 `( c5 L- h( m3 I! T - *4 h& W3 _* C A, G5 x0 T
- *// N& b2 ^' r6 z; }" W
- @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
4 v# Q6 N5 I! X& T+ ^7 i( I% }; r - public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { ' S+ p' k) o* J7 I' J: X0 k
- HttpServletRequest request = ServletActionContext.getRequest();* v! E7 T5 R* k- y% s/ Y
- TeacherEntity user = CommonUtil.getUser();
6 ]: ~' T1 ?. _7 h" H0 {8 } - String ip = AddressUtils.getIpAddr(request);
' O, w) t! z A& a5 n9 w" h& G - try {
' m# u+ H' W& h5 i - String params = "";" |6 d' c% E1 r5 h2 J
- if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {8 T9 h* Q2 q b
- for (int i = 0; i < joinPoint.getArgs().length; i++) {
6 \, W; w& P$ i - params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
# o1 U7 C, d7 n' G( t7 v - }$ |1 c$ }9 q$ D7 r$ W# C
- }
, y7 e0 R6 s1 b0 U5 i - String description = getServiceMthodDescription(joinPoint);//用户操作
5 W& r; C6 k( ^& J% H - String exceptionCode =e.getClass().getName();//异常类型代码
" s* w/ I( {. e - String exceptionDetail = e.getMessage();//异常详细信息
- V M+ _) Z8 @8 v& T ` - String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法$ V4 o' C B- X5 N/ s
- /*==========记录数据库异常日志==========*/
) @: f1 w- e0 g% K; z - Log log = new Log();. a! r; S5 _4 {& O% U* k: r
- log.setDescription(description);& O! }9 r# |2 O) T; Q1 R
- log.setExceptionCode(exceptionCode);0 C. A6 Y4 M5 n* K9 X
- log.setExceptionDetail(exceptionDetail);1 q. e C* T6 g& n' ]
- log.setMethod(method);0 a- U5 M y2 G/ M" O# ]
- log.setType(Constants.LOG_ERROE);// 日志类型: w1 s! P8 C" ~+ G( H3 b
- log.setRequestIp(ip);// 请求IP4 I; R& C7 l# i: ~0 g) }
- log.setParams(params);//请求参数
9 v! o: u/ ]& ~ - if(null!=user){
0 _' S7 A. \. y - log.setCreateUid(user.getUid());//用户ID
/ r. X8 l8 I: ~, C ?8 M |0 z1 O - log.setCreateName(user.getNickname());//用户昵称4 [. n6 i* G6 o6 Q6 H' X! N* g2 F. ?
- }5 H0 ]% E" B7 M5 m4 s* h
- log.setPlatFrom(Constants.SUBJECT_CODE);
, n/ @- y5 A3 Q - /*==========记录数本地异常日志==========*/ 9 N1 a$ U3 k. ~; C# l8 S
- //LogUtil.error(description, e);8 b W: J8 F3 c4 \9 e
- /*==========发送异常日志到邮箱==========*/# t0 J6 f/ F. `0 a9 c
- StringBuffer errorMsg = new StringBuffer();! g; l1 h* p. V6 e& b2 d8 L8 o
- errorMsg.append("异常方法:");$ P' s, D( h% y% _6 M$ {
- errorMsg.append(method);
6 A8 p, r, b }% w - errorMsg.append("</br>");6 _, @/ i9 Z/ Y0 u5 O" O/ g
- errorMsg.append("异常类型代码:");1 `: b7 ~. ]. s* N% P- Q
- errorMsg.append(exceptionCode);, Q6 G# x1 P8 c, W( g
- errorMsg.append("</br>");* w2 e, ?+ p! i5 L, ^1 l
- errorMsg.append("异常详细信息:");) k: ?- B* u4 I% Q
- errorMsg.append(exceptionDetail);. o3 |5 A5 n7 a T" f* `
- errorMsg.append("</br>");9 g" S. E1 M# L4 c
- log.setErrorMsg(errorMsg.toString());
A1 j/ K1 m- x& M - WebServiceMathClient Client = new WebServiceMathClient();& ~9 e9 @+ W9 W. Z! s% \
- Client.sendError(log);4 \) n2 O5 u# n3 n3 ^/ |/ I
- } catch (Exception ex) { Y/ ?/ n* h: X( Y- U# U( Q# c0 ?. f
- e.printStackTrace(); F8 g* p+ G5 t# W' q8 e: M
- }
; S$ W" n" u: `8 O* A! g - }
) p. x, I |; T6 B3 ^0 J - /**' {; h' ?; q2 x
- * 获取注解中对方法的描述信息 用于service层注解 (基于反射)
9 Q! \4 v/ z0 i% s% Z - * @Author 张志朋( p X. y7 O. m/ h+ G
- * @param joinPoint
1 W: J& d& v Y0 V1 G/ g) v1 T - * @return q& A8 A$ @! x" }4 p B0 K
- * @throws Exception String
7 `& N* Y+ c" P% c( c& [ - * @Date 2015年6月3日3 G5 N$ N* y4 Z4 W, `8 b
- * 更新日志1 `9 \8 f" v+ a0 W! x. w! \
- * 2015年6月3日 张志朋 首次创建1 \. z7 v# u' o/ S5 i, f
- *
# D- Z C9 w& J1 J - */
& V i' S( C4 \- A8 Z7 g - @SuppressWarnings("rawtypes")
' M5 {/ ]5 r' e { - public static String getServiceMthodDescription(JoinPoint joinPoint) % w! u- E: x: T7 r9 z5 V, ?% x; v
- throws Exception { ( Y2 B& E% P: @9 H; a9 L
- String targetName = joinPoint.getTarget().getClass().getName(); 7 t, ]: N9 d1 K7 }/ A, `
- String methodName = joinPoint.getSignature().getName();
2 B8 O- R v8 d - Object[] arguments = joinPoint.getArgs();
8 n) E0 Y9 x; m g5 d3 o# v$ Z3 Y - Class targetClass = Class.forName(targetName);
/ k& q+ K/ x& ~; e# f6 }4 ] - Method[] methods = targetClass.getMethods();
5 w3 l: J0 [7 Q/ q# X' A" S4 W3 k' c - String description = ""; 5 [2 J( X" O1 T
- for (Method method : methods) { $ h) m# Y& f; }# g+ S+ [, _
- if (method.getName().equals(methodName)) {
) s/ |, I$ t& B( ? - Class[] clazzs = method.getParameterTypes(); & G* S- m7 z8 s* i# e
- if (clazzs.length == arguments.length) { ; v" k" l. }" T" x- ]& q/ e
- description = method.getAnnotation(ServiceLog. class).description(); ! l. J, l( }& l( \% R; U3 T
- break; + U3 [# j' {0 T" e7 Q
- } + a1 y! z$ ~! _ o5 b, U
- }
5 L2 X1 [. J3 F. n: r5 T% Z- U - } ( h2 |/ l' l/ n- ?
- return description;
5 I7 Y$ J1 S' h; u. A1 c - } % c! ~, L) G% G0 {+ I0 O6 v- L) g
- }
复制代码
7 E: s8 v1 H" |. E2 `这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。. N5 R: k% V% L& w C0 {2 W
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
( h6 Z/ \! T' v8 s/ }) s) c! v' v! B* ~/ @" `. H$ p# `7 |
2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)' q7 K7 D6 T: L% A8 X7 i
- @ServiceLog(description="获取待审试题数量")0 r: f3 E, E& H7 a7 ^, o
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
- u" D" k1 l/ u0 T' W - return quesPerDao.getAuditQuesNum(currentUser);7 V: T0 A& O4 X1 d
- }
复制代码 |- v! T( G5 F3 k6 r0 f5 t$ y, W
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。
# u" [* B, T( ^1 [" q* c# C3 i二、AOP实现权限控制
0 `" q& J) a' \/ Y& S" a" z f% \ 上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
8 p6 J3 N1 `, X7 s- z 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。* ?; C! y( u+ G' d: Q
首先配置文件要引入这样一段配置(看注释说明):8 G: Q2 j0 E, Y
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
: o# l; y& u, U1 T( s - <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码
& g+ P. p+ T8 m0 b8 E7 f4 b(1)、定义注释:
" `. w) f! @7 D+ Y" b4 O: m1.Permission.java
2 P' o$ \) m5 o4 J3 h; g- /**
& ]: o- x* F* i% g7 r W! } - * 自定义权限管理
0 j( |: Z: L& _# P7 |8 b - * 创建者 张志朋* _- V8 ]) W$ ] _! z' r
- * 创建时间 2015年6月30日
( G) N$ u; m$ ?/ ] Q9 I - ** N+ f- p0 l1 O4 e) c; @' e) S
- */9 M7 a, y! W" v" M
- @Target({ElementType.PARAMETER, ElementType.METHOD}) : E- H" ^2 W l, j4 G1 d& O
- @Retention(RetentionPolicy.RUNTIME)
8 h4 _+ x5 Y h" i9 p* i - @Documented( d' l& [( v% k* L; B
- public @interface Permission {2 Q W# n) N$ x
- String name() default ""; //操作行为
. _- t9 G4 P% d7 W* n! ?6 ]% T, Y - int id() default -1;//权限值
) ?2 ]8 x. b& k( r, x! R4 P - }
复制代码 (2)、定义切面以及切入点
8 s7 u1 T0 b2 V1.PsemissionAspect.java
/ A" t% F7 F. p' B+ r3 p. o- /**9 a7 K. ?3 ]+ j- I2 v5 Q
- * 权限管理% X; J& @: U) Y# [; r
- * 创建者 张志朋 i+ I+ o0 ?. r( w% e; u. T- q" E5 `
- * 创建时间 2015年7月3日7 c. g5 G* ^3 a+ f$ @, L
- *- x) V' H/ L% |! L+ c$ x p
- */
5 r! n' O' J- j$ U D - @Component
* v3 N5 S$ Q6 k) P: d4 z - @Scope
9 A) P5 O, ^( T - @Aspect+ K- @+ ~1 m7 p& q- X9 k! z% [) C b) p
- public class PsemissionAspect { ]3 V# Y9 w4 H
-
: Z1 @ i% n% s6 \, k6 {1 ^; G" s - //Controller层切点 用于权限控制
I/ ?7 T- ?7 f* H: r' J7 c8 N4 w( [ - @Pointcut("@annotation(com.acts.web.aop.Permission)") 6 P: Q3 Q% I# `3 m2 V
- public void permissionAspect() {
7 [5 i$ Z& H6 N6 P- N$ u: u - & s @$ t* h; ]0 z( O) M- j: ~
- }1 d* X! p" s3 n( U/ b- |1 R
- /**
: r6 L4 D0 n2 l5 N2 s+ {/ S7 H, @ - * 用于拦截Controller层用户操作权限(环绕通知)( F5 U1 \2 U0 l- o
- * @Author 张志朋
% D% i8 S' H& i( t0 y - * @param joinPoint void
' h% L8 ~ z! N: B* i - * @Date 2015年6月3日
1 I' [+ X! S: F7 X" o* x1 `( g* w - * 更新日志$ e3 ?$ ~* \& C% t! G( X9 I
- * 2015年6月3日 张志朋 首次创建
0 C! x: j; h% P0 ~9 R. p( | - *
, P( q6 [! o4 x5 ] - */
. [3 Q2 r$ O! r7 ]. b5 K - @Around("permissionAspect()") 6 v; V, C9 p6 k( `( \% o- G5 V6 e8 e
- public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { ' ~9 R* A7 g4 U! b+ T8 U1 M
- Object retVal = null;
1 ]6 t1 P7 A& X* Z n - int role = getControllerMethodRole(joinPoint);
1 A, D! F6 f- A: K - TeacherEntity user = CommonUtil.getUser();
: W/ l! h1 r! L& R0 m - if((user.getSpecRole()&role)==role){//没有权限* h4 G0 C# b* e' K. P8 M. \9 _2 h I
- retVal = joinPoint.proceed();$ B9 i) E+ C, J- s+ T$ M
- }else{
$ t" M7 B# Z& K; s/ y7 y! H1 J% F - noAuthorization();& K+ D; }; I$ M k" f2 Y
- }
9 V( x" Z9 e b - return retVal;
# g* } U4 ?# d2 ]7 u4 O! b+ Y - }1 q6 ^4 \" d3 c! }- ]8 _
- /**
# M. y; j) [0 {# l( C; t# P5 ~- l2 G - * 没有权限 实现跳转
* ?" u$ ^% a! x2 z" R6 N - * @Author 张志朋( |, U& }+ {$ q! G, O
- * @throws IOException void+ }5 f) Q0 _3 b
- * @Date 2015年7月3日0 w q1 j, {, { V2 c+ N p
- * 更新日志! y2 ^* E8 O! L" B8 M
- * 2015年7月3日 张志朋 首次创建
. a3 @- Q+ m4 \! M9 g& m& n - *1 ~& Q. f( F% M0 L. w
- */1 ? U9 C: g8 i! I# @* X2 E$ P
- public void noAuthorization() throws IOException{3 s/ Q- F2 ], T# {& y* ^7 u$ I a
- HttpServletRequest request = ServletActionContext.getRequest(); K; U- E! w q' U/ G" Q
- String path = request.getContextPath();
" L; r# Y3 H! Q5 E6 ^7 O/ W9 ~ - HttpServletResponse response = ServletActionContext.getResponse();9 G0 Y& N8 A$ v& c! l% Q
- response.sendRedirect(path+"/pages/noAuthorization.jsp");
$ X+ y/ S1 ^& A9 _% ` - }! h: L' }) X3 g" b% g( r3 C/ E1 i9 O
- /**
$ v% X& t: L5 R9 B" n+ p# ~% b - * 获取注解中对方法的权限值 用于Controller层注解 ( J+ f1 ^7 S+ S @* i
- * @Author 张志朋/ `! j5 }& D! D* L
- * @param joinPoint/ {& t( H- K# p/ V: ?2 P+ ^; G1 ]
- * @return1 f( q" b' {) \6 [* P0 \8 e" c5 l
- * @throws Exception int3 S9 h3 d. g9 P4 B0 v2 b/ W. t
- * @Date 2015年7月3日
% ]/ u l5 z8 ~9 g5 K) k; \ r - * 更新日志 A1 I) w. U- F+ m" }
- * 2015年7月3日 张志朋 首次创建
3 w& }5 X8 Z; m - *' v, Z! g" N. [0 A
- */" Q% l9 {8 u4 J- Q: n
- @SuppressWarnings("rawtypes")
' n4 l5 m8 @- p# ~9 i$ S; f- [: e - public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {
; w7 o1 H1 o4 y$ ?) A - String targetName = joinPoint.getTarget().getClass().getName();
/ ]3 k, p& k/ b M6 S - String methodName = joinPoint.getSignature().getName(); , \8 a2 _. H' g w" ~3 [! Z- T
- Object[] arguments = joinPoint.getArgs();
# I& y' ]3 O/ Z7 s1 m - Class targetClass = Class.forName(targetName); ( W3 E4 c! s; A2 m3 u5 X2 _
- Method[] methods = targetClass.getMethods(); # C5 d. `; P3 F3 w5 v7 ~% R
- int role = -1; & s# G6 p' n' V! k8 }2 y
- for (Method method : methods) {
( S& i. y/ B, Y4 _' V. u7 z - if (method.getName().equals(methodName)) { ' ^& I! C7 n0 L1 R9 t
- Class[] clazzs = method.getParameterTypes();
$ G" i$ G n2 e. ?8 v- H - if (clazzs.length == arguments.length) {
! W# i* I! N0 _9 e - role = method.getAnnotation(Permission. class).id(); ; w( o( R3 D, Y# f3 M. Z
- break;
7 f, u, p8 @9 s! H- q! x7 i1 H! g - }
0 s, ] q1 `4 B4 J9 G! X: c! P- D - } 4 T5 f1 ~) M2 I! z3 @+ e m+ o
- }
! c" N6 l/ Q% t9 N7 r" q# n, V - return role;
9 M8 A4 y3 {* g2 x+ e4 F- B& ? - }
复制代码 ( J [ }) o6 D
2.action层代码实现:4 ]* C+ D3 ^/ \& P$ x# P
- /**
; [( P7 J: |/ V7 B2 U - * 试题审核不通过; O2 `5 J% E- p) v$ D) m6 {7 M. m
- * @Author 张志朋 void" s2 O' I4 t! s' C J
- * @Date 2015年5月4日5 V" b8 K2 V+ V) `( i' G) B
- * 更新日志, I8 o1 f4 {0 o. M5 z) _$ r
- * 2015年5月4日 张志朋 首次创建
" w8 H- b% y7 b$ z6 q5 t - */ c9 J3 U) C6 @. u! L2 W! I
- */
$ R) {4 ]1 j$ o( e5 s! D- s - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
% T; ]; J# m3 s, {. K# m+ e9 }7 k - public void auditQuestions(){
% T! v2 S) F) {4 v3 Z$ n - try {2 V$ ]3 l; E) v u# w% }- D6 v
- //代码实现
2 I6 l2 v1 |- o. y/ b - } catch (Exception e) {
9 I6 B3 q+ v8 \8 x; O - e.printStackTrace();- r: R& W1 e2 T; S8 h9 s
- }9 v* Z) a& k7 I: `# ^
- # @1 m5 ~# n, k6 ]& }
- }
复制代码
1 B6 l g8 T( u, @! h0 o
- j( y& c$ F9 x6 A8 H |
|