TA的每日心情 | 衰 2021-2-2 11:21 |
|---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 % Z5 f4 c3 {1 d( K2 b
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
1 m' g* C0 `6 ~6 _% {* j9 s' z5 y* }7 \6 G& V+ W0 t
) r& T, a/ a" e8 A" d" \5 b 鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
, F* Y; H% w$ T0 E- L% m 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html! n* B. W n. ]* C0 s9 o
( Z. C8 I! u3 f# u2 \* C: {* g
一、 记录日志并发送邮件通知+ S: @( ~. {7 ?' W3 a# ^
(1)、定义注解:' `+ U" q9 {5 O# K- s. E7 O
1.ServiceLog.java(各种参数类型详见上面的资料)
1 H0 u, J3 B; y- import java.lang.annotation.*; ! Z% o. V1 i9 G& U
- /**4 a; x! r3 a4 I: O3 b/ l* {$ x
- * 自定义注解 拦截service $ p$ K: M/ g5 X T. p: w# k6 ]
- * 创建者 张志朋
( r- B- w& f3 p7 N, E - * 创建时间 2015年6月3日. [* k$ a( u1 \7 j: F- b& B) i6 G
- *! s, z$ ^6 {6 r
- */5 D" V6 m: a; m0 Q; i2 k0 R
- @Target({ElementType.PARAMETER, ElementType.METHOD})
7 t4 \1 W. p9 f* X - @Retention(RetentionPolicy.RUNTIME)
# Q1 a8 p$ ^9 m- L. O - @Documented 2 [* k0 U" a( z% N% K2 k7 [# h' D
- public @interface ServiceLog {
3 }& ` O& Y9 ~7 C - String description() default "";, Q: s# d3 L2 F6 g8 Y7 y! j
- }
复制代码 2.ControllerLog.java
$ A/ w9 o; u) y. X5 @1 O8 G' K- import java.lang.annotation.*; " H% f- |8 o# Q, G& m- Q% ]
- /*** p, ]/ _8 M# Z2 d% x9 I4 }
- * 自定义注解 拦截Controller
) |1 b# V. s1 h% O - * 创建者 张志朋2 N5 @' {+ b! u4 B& J5 `
- * 创建时间 2015年6月3日
# h6 t2 N8 p9 ] - *
+ w; C/ p# Y: z, O/ L5 e6 x4 E1 x - */9 F" h$ ?) p W ?
- @Target({ElementType.PARAMETER, ElementType.METHOD})
* R h/ G# \/ k# w3 C7 J9 V - @Retention(RetentionPolicy.RUNTIME) 3 y# R! x; D t5 B0 T% R* [
- @Documented/ ~$ e8 H0 L( U
- public @interface ControllerLog {
- G. n3 q: P U1 M$ m" C0 z - String description() default ""; 7 E1 k4 x9 M) k" Y9 l5 f+ s4 r* v$ v
- }
复制代码 (2)、定义切面以及切入点
) r! V+ I' k$ S: e1.LogAspect.java3 R9 q* Q, p7 `. t- O7 {
- /**% A, J2 [/ G! G$ S2 `8 ~( D8 C: [
- * 日志记录AOP" Y% n, \/ {8 D. i2 O
- * 创建者 张志朋# d1 z( _) p8 L! X2 Q J
- * 创建时间 2015年6月3日
$ O/ D8 o) R) Z6 x" f - *: ]2 S' P9 d' [4 F+ B; D4 p
- */
/ a1 U+ `/ Y& K9 ^& A$ u - @Component
1 j j0 H1 E, X( ~. } - @Scope
. y5 b. y7 k3 X) u - @Aspect
# T, R1 b: g; _7 q, J - public class LogAspect {
0 f) \$ m# r& b B4 q3 N8 t& s - //Service层切点 用于记录错误日志
2 R. m$ _9 V9 g. e$ _: T& G - @Pointcut("@annotation(com.web.aop.ServiceLog)")
! L1 K) e; Z- x! b0 w1 W3 G - public void serviceAspect() {( ^* K- i- D! w
- / z/ x9 S8 u) p" C+ v
- }& {! Z$ G: C: Y
- /**8 J# z( f. y) V# i+ H8 w/ l( U- A
- * 异常通知 用于拦截service层记录异常日志
! [% K" }, o Q% F# x$ r* r5 R - * @Author 张志朋
% Y# k: p# e- B' Y% N. S - * @param joinPoint) h6 |+ Z% T, V& d
- * @param e void
6 n. R5 B* b) _ - * @Date 2015年6月3日5 f5 S% U8 J& E/ P9 w
- * 更新日志6 e8 `- @ |1 a$ V/ U) f2 a
- * 2015年6月3日 张志朋 首次创建
# P" ~7 I8 V$ a2 h2 J8 X - *" t1 J$ R$ @, B/ M" |/ ?: k# D
- */
! l. ]- L- ?& C# C! Q/ Y - @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") & c0 o5 U% ~& J2 h
- public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 0 [, M. o ^: D8 M4 V6 |7 |# l
- HttpServletRequest request = ServletActionContext.getRequest();
; B1 U; Q2 y g - TeacherEntity user = CommonUtil.getUser();
3 C% B4 [- ?9 P - String ip = AddressUtils.getIpAddr(request);/ H* ]9 F0 e/ L8 j+ I: }, c+ }
- try {% y$ e& X% c: }
- String params = "";
# _7 d* F0 S( u5 t- x/ [$ } - if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {4 D/ Z3 N! \9 p8 y q' Q
- for (int i = 0; i < joinPoint.getArgs().length; i++) {
8 T( s, Y' h% }' S9 C - params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";* }+ S' |( @8 ~, ?# b: S0 j
- }# M% B' W4 `6 [9 n8 t1 ~' l
- }3 a" h' ?1 }/ p- W, I) ?/ \
- String description = getServiceMthodDescription(joinPoint);//用户操作- E6 L2 B" N: t' I1 S" R
- String exceptionCode =e.getClass().getName();//异常类型代码
% N3 q3 n; d" X( |) P/ ~ - String exceptionDetail = e.getMessage();//异常详细信息
8 W1 Q* v+ p) `' r ~) V) } - String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
$ F$ d6 J) I2 N$ Q9 Y$ W+ F - /*==========记录数据库异常日志==========*/
5 E+ l. B- M0 o' U B - Log log = new Log();
. S% ~& V. U- F1 m9 T. d - log.setDescription(description);6 t% Z) ~$ S, @! {( F
- log.setExceptionCode(exceptionCode);
* Z# q n; o& P% f9 v - log.setExceptionDetail(exceptionDetail);
( E' Q! X7 }# m+ J- g2 c - log.setMethod(method);" r) [3 B% |8 ]* K
- log.setType(Constants.LOG_ERROE);// 日志类型8 \8 V6 ?2 o9 s; T: p" M+ ^6 ~$ `
- log.setRequestIp(ip);// 请求IP
# Q8 H$ y2 H s7 V5 n) T6 G$ H - log.setParams(params);//请求参数3 n* ]( f; j7 [* F6 N
- if(null!=user){8 I% D! M! d! i# a' V2 k+ j
- log.setCreateUid(user.getUid());//用户ID# K% K. X/ e+ a( \' M6 W
- log.setCreateName(user.getNickname());//用户昵称8 k, \4 f6 p# c4 L3 S
- }
1 s. {6 v$ C+ K# |$ b - log.setPlatFrom(Constants.SUBJECT_CODE);
9 ]& B9 }* ]& ~1 a( s5 G - /*==========记录数本地异常日志==========*/ 7 L& J: o$ V9 C! \
- //LogUtil.error(description, e);
3 Y. d: p* h& G2 h - /*==========发送异常日志到邮箱==========*/; o: M: y! `2 v
- StringBuffer errorMsg = new StringBuffer();7 {! r% ^/ k" I. u9 @ ^! [
- errorMsg.append("异常方法:");
. o& c0 V! Z( _0 o: t - errorMsg.append(method);
! l. f5 H8 [. m' Y* i - errorMsg.append("</br>");
( H% {) z( V6 S& p- v T - errorMsg.append("异常类型代码:");
1 ` X4 w4 E2 f" n2 k4 Z0 N- {6 V+ _ - errorMsg.append(exceptionCode);$ c$ n5 o/ D# \; s; c
- errorMsg.append("</br>");: H5 _: P: q; v
- errorMsg.append("异常详细信息:");7 \; J5 _+ A, r( `$ \; T5 [6 \
- errorMsg.append(exceptionDetail);
4 X1 F1 ~, | E7 e - errorMsg.append("</br>");5 S7 L. X, j: @
- log.setErrorMsg(errorMsg.toString());5 h+ h3 n* s6 u0 G$ ]' m A
- WebServiceMathClient Client = new WebServiceMathClient();0 w' n+ B O+ I2 ]) C
- Client.sendError(log);
8 [1 [) N7 E; b# a( l7 _ - } catch (Exception ex) {
5 E9 Z/ h5 B" d j - e.printStackTrace();+ x- L( j3 c. l
- }
2 N, `* [% R5 D+ X1 J% q. n z7 n - }
3 o0 K" l& O/ a9 v. [# v - /**
`% B* C7 ]" ?" Z - * 获取注解中对方法的描述信息 用于service层注解 (基于反射)
' }- w6 o- H+ P. {, _) g& A, t - * @Author 张志朋
7 F! F( ?$ c0 g4 D" ] - * @param joinPoint
$ {) w1 f3 F6 L - * @return
* {; |/ Y: `( ^; P$ S: m( i9 y - * @throws Exception String. U% n: ?% {8 ^
- * @Date 2015年6月3日
, H$ M8 d9 B6 y: a - * 更新日志
0 j& w0 n7 w7 o! F; h7 ]) ` - * 2015年6月3日 张志朋 首次创建5 u0 V6 o8 p4 q7 l
- *
) w. u7 s+ L8 W4 {1 } - */: W8 J! F+ S( Z/ ^9 S" t
- @SuppressWarnings("rawtypes")
6 R3 r# S: ?- J- @- c& q I - public static String getServiceMthodDescription(JoinPoint joinPoint)
4 y8 Q: Q& R5 Z9 d3 j7 | i - throws Exception { 4 L l5 V0 R% O% g( {3 i
- String targetName = joinPoint.getTarget().getClass().getName();
0 w* M9 X+ q8 F - String methodName = joinPoint.getSignature().getName();
+ N; n' V0 t* j5 q" W m - Object[] arguments = joinPoint.getArgs(); / w6 c0 D( x9 Q- E' i4 m
- Class targetClass = Class.forName(targetName); _* e$ l) {: O$ b' @1 y
- Method[] methods = targetClass.getMethods(); & Q% ~+ a, Y1 ?+ g) R3 L; u
- String description = "";
* y9 k9 h7 v2 c5 {5 T) \( N" E - for (Method method : methods) { ' k+ W* B. n% x1 }+ C% [
- if (method.getName().equals(methodName)) { / q9 Y. [5 m3 n( K
- Class[] clazzs = method.getParameterTypes(); ; n( z9 v6 f1 v$ x
- if (clazzs.length == arguments.length) { 5 _/ e l" W( o; Z4 {
- description = method.getAnnotation(ServiceLog. class).description(); 9 w, T1 W; A# ?! \& K
- break;
+ |: t8 p; z3 R7 N( S - }
( M* Z3 g& W- o' D - }
* M& d* K+ |' H4 _# f* i - }
( n& B$ ^: m$ L I0 |) n - return description; 8 G' @ X3 \6 |% v" o6 {4 b8 s
- }
) E0 H) K0 h- \$ F, ~ - }
复制代码 + m. N( j4 A6 `/ d W
这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。5 K, Q* i9 D" H* r. C9 `
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
- Q/ D0 J+ n- n) M# A
4 r4 t# G* P! Z: p5 r3 P" ?2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)
% X9 Q' @0 S/ T7 ~2 D3 R3 M- @ServiceLog(description="获取待审试题数量")
- l/ z' h$ q2 I3 A - public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
: g* P$ y+ b4 m( p2 ~1 Z - return quesPerDao.getAuditQuesNum(currentUser);
% Y7 e! T [1 e6 o& Z/ q# ] - }
复制代码 7 I/ B) T5 C' @! I2 h7 z- }! d# Y
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。; d7 q" K( I1 g5 \) p; a
二、AOP实现权限控制
' T% K$ b; B; P( k$ e& Z 上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。9 w2 h! U- {6 j1 D6 s' U
还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。- l, g- o9 m& u4 P2 B
首先配置文件要引入这样一段配置(看注释说明):
& t& a( v0 A- j0 {/ \5 _" {- V- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
/ L9 i; D6 u, Z) q - <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 4 y* O; b! e x# m8 f8 M% p
(1)、定义注释:
! [8 s: \; Q) F* e( ^! V; j- U( l1.Permission.java s5 W& t) G, S; q7 \) {8 A
- /**" n' M3 j0 a5 M' {
- * 自定义权限管理
* N/ n2 B9 X. J+ z1 I7 T - * 创建者 张志朋9 s& h# ~2 L% E* {
- * 创建时间 2015年6月30日 w& }5 F" z( t. N6 t
- *( d9 V4 B0 o$ v6 o3 N3 q
- */
+ O4 N) m2 K: @8 ?9 P - @Target({ElementType.PARAMETER, ElementType.METHOD})
2 r. ]( I7 ]4 }8 r. J - @Retention(RetentionPolicy.RUNTIME)
) v: m& A5 ?! ? - @Documented3 l, p m% }3 N- E
- public @interface Permission {0 t( `) `9 k3 G3 I) V# }
- String name() default ""; //操作行为
& P1 {9 e: n. @ - int id() default -1;//权限值
: b' I7 U2 H7 `7 o/ W! s+ I - }
复制代码 (2)、定义切面以及切入点' T( k/ P" W6 K
1.PsemissionAspect.java
6 U* G' A/ O4 l/ r( Y: B& g; A- /**9 h( v6 ?% \# t% d* K8 R
- * 权限管理
! y; C( Z) U5 }6 n4 ^8 g f - * 创建者 张志朋
1 k) Z8 ^! G' D1 ^2 W7 M0 c7 s - * 创建时间 2015年7月3日
6 {, ?: u5 `5 P/ K! W5 [# ^; Q - *
8 v7 r) a8 z7 x$ s! m - */8 `: m# }8 q0 v: [- [: ^
- @Component
5 n$ p! f1 T4 a( }2 I - @Scope9 z/ U' m5 ^3 y7 E" l% I
- @Aspect
1 ~1 @" g8 R' X. W* E7 S2 _5 t - public class PsemissionAspect {
/ a/ f+ O( \1 y - . o9 p* v& A" k' q5 t& y; W& S
- //Controller层切点 用于权限控制6 N* o+ [) B- X0 y
- @Pointcut("@annotation(com.acts.web.aop.Permission)")
& O) w7 {8 [( |# Y( ?$ t - public void permissionAspect() {& r' l2 V8 j4 e2 ^% \( N
- 2 G' _ ~$ k2 k w7 H& V( G
- }
% y( I4 \4 |- i3 K# L0 F- d - /**! P% j3 U2 }8 |, I# p
- * 用于拦截Controller层用户操作权限(环绕通知)
& T! x6 G N7 y - * @Author 张志朋
6 P* } C' e& k6 S0 y - * @param joinPoint void
0 V" z5 s3 |( R i1 Y) F - * @Date 2015年6月3日
7 d5 J$ U8 O N" W - * 更新日志
7 k% J. |+ Y3 C- j3 X - * 2015年6月3日 张志朋 首次创建. [3 R4 J6 O! v) m9 F8 y
- *
8 e* X6 ~; R1 D* b; }* { - */+ {, k' @7 Q2 O
- @Around("permissionAspect()") % z% h8 Q8 |3 q2 s; a- t K
- public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { " B7 p7 c3 q8 y4 E2 b7 _
- Object retVal = null;
0 Q" h2 F" s7 E" e5 m% T. { - int role = getControllerMethodRole(joinPoint);
5 K b+ W% I# C- A+ v; f, f; l7 @ - TeacherEntity user = CommonUtil.getUser();3 i2 J7 J9 [. Z
- if((user.getSpecRole()&role)==role){//没有权限
_2 R% O% T& A1 q: a# y! D) h - retVal = joinPoint.proceed();% K7 B3 ]+ }, I4 \( [1 E8 Z
- }else{
& L# Y, \1 v- T. v( ?2 S - noAuthorization();$ l% H6 u; N/ m* l5 x" n5 u
- }
( I+ ^8 H2 Z& {+ p* [; [ - return retVal;9 n& e# m! ?, ?: y1 x2 K7 t
- }
. @& ~) q0 K' ]7 }) \% B - /**6 f) O: `/ |6 t+ i2 Y
- * 没有权限 实现跳转' f3 N! Y# o* g$ U7 e2 \$ ?6 g2 B
- * @Author 张志朋% X" j6 Z' A; ~; V
- * @throws IOException void
0 |% G/ {! D# P - * @Date 2015年7月3日
- o f( e6 Q- [: ^% i+ ~ - * 更新日志
. A5 _/ h1 H7 E; S& \ - * 2015年7月3日 张志朋 首次创建' I O7 Z: r, Y% l% ^% K
- *
( ]/ H+ {* {, E9 E0 V& b% ` - */8 S2 h2 {: {/ t' ?. L* q2 N& B6 u# v2 n. G
- public void noAuthorization() throws IOException{+ p2 m; R2 ?0 R% ^ I3 P
- HttpServletRequest request = ServletActionContext.getRequest();8 \" H. o# X8 N9 g4 N
- String path = request.getContextPath();8 A/ [* F% H: x* A1 k% m
- HttpServletResponse response = ServletActionContext.getResponse();% q+ v& u4 c+ E1 j6 q
- response.sendRedirect(path+"/pages/noAuthorization.jsp");
' s3 R, i- e/ z* I - }
4 n% q1 J% V6 S7 O4 {) ` - /**$ f# j! m' ]+ w, }. U( v6 B
- * 获取注解中对方法的权限值 用于Controller层注解 $ H; f& j3 y& v Q0 X, I( S9 S: T
- * @Author 张志朋, F; ], M5 e% ^* K) P
- * @param joinPoint
7 x# o) A4 O. F+ c2 j- T9 }8 ~( B9 s - * @return
5 R- O8 Q1 h: J7 J - * @throws Exception int% v- ]" `, V! t- _
- * @Date 2015年7月3日1 J/ S! \ i4 h* {
- * 更新日志, T( Q& `2 i+ Z% z# d
- * 2015年7月3日 张志朋 首次创建
; y3 r9 \9 l5 ?% I' J( q! ? - * S9 {; e5 ]2 d
- */
6 L# a* Z4 b# X - @SuppressWarnings("rawtypes")& @! X a4 h' S7 ^# c0 B) [- h4 {" @
- public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {) L, e& ^$ `7 P5 ]1 X1 b. J/ x
- String targetName = joinPoint.getTarget().getClass().getName();
' M8 x/ k1 K: I+ c& v3 l - String methodName = joinPoint.getSignature().getName(); . \, R) H2 t& F s: _* e. e
- Object[] arguments = joinPoint.getArgs(); 2 B; m1 E, U* N, _ j
- Class targetClass = Class.forName(targetName);
. b p: C/ i: l3 {' ]* u$ q- h$ e5 L - Method[] methods = targetClass.getMethods();
! L0 Y+ e9 K/ G - int role = -1;
% W) y& m5 C4 p - for (Method method : methods) { * n6 ^# ~! [( t/ V) v M
- if (method.getName().equals(methodName)) { ( R/ g J3 j4 h2 Q1 t4 D2 Z7 S$ \, X
- Class[] clazzs = method.getParameterTypes();
9 |) K' @. c" V8 H% h1 s - if (clazzs.length == arguments.length) {
. d! n) y: Y% J( p! ?" f+ c9 r' b2 W - role = method.getAnnotation(Permission. class).id();
0 E. g7 C/ o/ _+ j - break; 0 R( x4 ~) }9 D0 B6 j9 {
- } " T# o8 N& K) n: r& C" P( E
- }
/ t k$ |5 k+ I& B6 f - }
4 |, Q9 c/ ^6 ] - return role; / `: u" M* i9 s& Q
- }
复制代码
2 z r. S: v: O" A& Z H: N2.action层代码实现:
2 B) F9 n9 J7 @: S" [$ k% L4 a% v- /**& x% |' E- b/ V, [
- * 试题审核不通过1 f w" W7 p( Z s% {+ G- R
- * @Author 张志朋 void) s. d) t% ^- |8 k) p1 T; ?! D
- * @Date 2015年5月4日
# F7 J* `+ e! Q# H; |4 c# _& ^( A - * 更新日志7 g' F+ m: X! a% {: m6 b
- * 2015年5月4日 张志朋 首次创建
- h0 G, `+ T' y5 }3 j6 t8 B - *
. W) J: M) F- ?- k7 h2 ?5 a% x - */
5 g' ~8 m4 X* N+ Q - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)) o( n) t# [" Q3 T' u6 m$ f+ h
- public void auditQuestions(){
0 Y" W( a6 F$ [0 l9 e - try {4 q' e+ m; X2 N( o
- //代码实现( u; U8 b" S" {! D \$ ]) a" f
- } catch (Exception e) {
+ z- @9 n" M ]% e% }. \ - e.printStackTrace();8 `1 B) T# ?$ F( }0 }
- }
+ h; R& k% t1 E- m
4 a. n) s: w5 g- _- }
复制代码
! e E1 ~7 O$ d& t
) B: l. v1 u' T( K: ` |
|