TA的每日心情 | 衰 2021-2-2 11:21 |
|---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。 - t5 |$ S" g5 h
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
4 H0 E- }# }) a. [' v8 j' d5 R0 x. ~- n
+ t2 C/ ~- ]5 w4 g! Y: z: S
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。# t6 _$ B0 ?: t0 R$ r
资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
& Z# E* h! F# G- _/ ?# k# y2 h
7 n; x: [$ t, c1 n( g7 ~- ]一、 记录日志并发送邮件通知) @8 |' K" H c; r, b( ?
(1)、定义注解:! M" I% C* p+ E1 f, o# }% a
1.ServiceLog.java(各种参数类型详见上面的资料)
, Y+ ]: b: Z$ ~* W' K- import java.lang.annotation.*; 6 G: S! G" n9 R! g6 P4 Q
- /**3 ]- {! O( [& O: _) i8 T/ H
- * 自定义注解 拦截service ( d* X: g5 v& U) p! v: {
- * 创建者 张志朋
2 J2 ^/ Y, i7 H1 { - * 创建时间 2015年6月3日
, P- \$ W3 i* z3 J } - *8 a. ]5 t. M- o9 g
- */2 ]3 E; [: s; G: }& q# L
- @Target({ElementType.PARAMETER, ElementType.METHOD})
- @. U5 J* @# Q) \1 I( ?$ l - @Retention(RetentionPolicy.RUNTIME) 4 }8 C% I+ o9 \; l7 M; L. W
- @Documented
8 v1 H# ~8 F( c1 E+ Q - public @interface ServiceLog {
& ~+ g1 w/ \7 v v9 W" I3 S - String description() default "";
! u' B" V }3 N# u' y9 W# ] - }
复制代码 2.ControllerLog.java
5 F9 t4 f- E9 w) w- import java.lang.annotation.*;
0 d. G5 P& m" {8 B; z0 Y X& |1 M - /**
. `5 T+ I5 ^- a. W - * 自定义注解 拦截Controller4 S# ^! f$ s$ n1 c$ l" X' P
- * 创建者 张志朋
4 u5 r) i7 z3 u, x, o$ O$ I - * 创建时间 2015年6月3日) f+ o, m8 v# ^& v6 z
- *% m" R8 M3 Z( B' y9 L# n
- */# X( N: W5 m* a t4 \1 @- f# U
- @Target({ElementType.PARAMETER, ElementType.METHOD}) 9 m$ k1 l% v w
- @Retention(RetentionPolicy.RUNTIME) ! g2 M$ F9 `/ H! N5 V2 b& \
- @Documented
- V9 ~/ P! b* g* {' F( f3 F7 ~2 Z; U; ` - public @interface ControllerLog {
! ]* L6 R7 ~2 _9 x% C _ - String description() default "";
l' v. u/ i+ x1 E" I - }
复制代码 (2)、定义切面以及切入点
" ]9 W5 Y; d; P: e8 K1.LogAspect.java
- @5 J L$ x: J% P( E9 {* M2 q- /**) v( O5 k) E" Z5 ~ y% u) M1 [0 C: d
- * 日志记录AOP
- J" D" \, r1 f- G# r; ^* y | - * 创建者 张志朋
# I3 {* S3 z; q7 O - * 创建时间 2015年6月3日/ g/ }* D2 ?% k. ~( _* N
- *
+ k. P {6 }' x; X9 {- `' m# G - */9 c. Z1 Y$ Z6 c6 A! I' N2 R- z
- @Component
1 Z, [) G. V, d& F# k0 F% b - @Scope
- }! L. L( H: F! U- b/ p - @Aspect- \; t* w. j& k
- public class LogAspect {# W- m2 ?: B$ f2 C, Q
- //Service层切点 用于记录错误日志
% E- R) [: f0 b( i) E - @Pointcut("@annotation(com.web.aop.ServiceLog)")
' b! m: w8 Z/ x$ D& ] - public void serviceAspect() {
7 _2 x( C8 q0 b% {4 g; G# v - * v) K* c5 x1 [/ L5 I0 {% h
- }6 ~$ u$ r p6 X' |. ?/ w% [
- /**$ T. u: A0 [$ [$ ]8 p/ b
- * 异常通知 用于拦截service层记录异常日志
& [9 o: P4 R; E. {7 k) ?% ]5 W" @ - * @Author 张志朋; R. `; u2 \( v- e& C2 T
- * @param joinPoint4 T# K! ]8 G) c1 K
- * @param e void
- ?+ M& @& p) E0 d- s$ ? - * @Date 2015年6月3日- H4 Y8 W2 o: b4 L& A+ O! O/ d( p
- * 更新日志" d9 f" r' u# Z$ @& v
- * 2015年6月3日 张志朋 首次创建
, }: X5 w' G* w9 I - *
- Q6 H, k: O' O' t( v* w; _ - */5 N+ @, ? k' m, ^) Q
- @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") # l# l( U5 O) ^
- public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { + i n1 Z' h# |' [
- HttpServletRequest request = ServletActionContext.getRequest();# n9 U7 {5 j! h! ~
- TeacherEntity user = CommonUtil.getUser();
2 j" f- n6 q$ c# B6 \ - String ip = AddressUtils.getIpAddr(request);
4 k) r7 t* e& e - try {
! A- _; L) q3 [! F - String params = "";
% C* b( S8 K2 R4 |2 ] - if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
P4 A* n: ^, o( W7 e - for (int i = 0; i < joinPoint.getArgs().length; i++) {9 e: d2 }1 O4 }8 ?
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";& @% } Z/ c2 [1 |
- }6 A' U5 b0 j9 D I, I b! W* T
- }+ z) v7 u+ u( |; K$ w* s
- String description = getServiceMthodDescription(joinPoint);//用户操作! ]. L$ ` g n& F& g+ D5 _
- String exceptionCode =e.getClass().getName();//异常类型代码
2 G2 M: z* g5 Y" h! z6 ~ - String exceptionDetail = e.getMessage();//异常详细信息1 _5 O4 s& C" m$ E+ }
- String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法, {0 C. a6 a# k0 r$ z
- /*==========记录数据库异常日志==========*/ ( |" k# c8 r! j( `/ B5 ]( B% R
- Log log = new Log();# E: K$ J1 T% G7 W6 Q
- log.setDescription(description);
+ S3 B: W% S2 a s, d; q9 N - log.setExceptionCode(exceptionCode);6 [: B! A& |# g: X e: x
- log.setExceptionDetail(exceptionDetail);: I, b1 f/ X; t% a) q
- log.setMethod(method);. _# {6 f# N. {7 _1 h* X
- log.setType(Constants.LOG_ERROE);// 日志类型4 Y' [ l$ j; I
- log.setRequestIp(ip);// 请求IP
/ E% R! T+ c& Z9 r6 Z - log.setParams(params);//请求参数
+ h. h- t( t3 a* F! h, Z- }% p. s - if(null!=user){
' d* Z: y4 x! C) |; i - log.setCreateUid(user.getUid());//用户ID
, I( h" @/ y# a _7 x+ @4 Z5 i% r4 s. ?: Q - log.setCreateName(user.getNickname());//用户昵称
2 A V# s/ B" ]' S - }
/ u$ ]: G7 X- }: H' ?/ x3 Z% f" G - log.setPlatFrom(Constants.SUBJECT_CODE);
* M. |. W$ a7 J4 ? - /*==========记录数本地异常日志==========*/ \0 z! R2 b6 Z% D0 a
- //LogUtil.error(description, e);
* B- g9 d) l' R6 o, Z S- c2 M - /*==========发送异常日志到邮箱==========*/' |2 K4 _: [) N9 H$ n
- StringBuffer errorMsg = new StringBuffer();
: U9 N! C3 v8 Y1 T y/ r& d9 U% K6 M - errorMsg.append("异常方法:");
5 {( s' a! N" l6 u& u4 l" K - errorMsg.append(method);# j' H# M* H( H5 D4 Q A
- errorMsg.append("</br>");' {- _, t) P2 S& ]6 @
- errorMsg.append("异常类型代码:");7 k+ m& {2 P+ _% Q$ Q
- errorMsg.append(exceptionCode);
1 P, A2 c& n0 J& C/ x - errorMsg.append("</br>");
# F4 ?( }3 g1 e1 U; J" O$ ` - errorMsg.append("异常详细信息:");
* r! c9 S0 x5 |. d! e - errorMsg.append(exceptionDetail);
; T* N+ M2 p U0 E: I0 [* s0 O - errorMsg.append("</br>");0 U8 |' }* @: u9 {0 \0 z
- log.setErrorMsg(errorMsg.toString());" |/ j* `$ r9 Y; Z7 u" O
- WebServiceMathClient Client = new WebServiceMathClient();
+ |8 m/ @7 C6 Y# B - Client.sendError(log);
" ^9 @6 Z; X8 X( m% U0 }4 X - } catch (Exception ex) {
4 c( z$ k+ y. f$ c - e.printStackTrace();
% @' Z5 |# O2 K - }
2 e3 }: [$ o3 i& |8 M1 P! @ - }
+ S' M/ t( [4 c* K5 J( ^, a9 H - /**
' a/ C I7 `1 D: N - * 获取注解中对方法的描述信息 用于service层注解 (基于反射)+ L; C; O7 E( y/ m8 Q
- * @Author 张志朋" X$ P) P w& ?3 U) I
- * @param joinPoint: K0 V0 t$ f* L% b( l7 Y' ?* m
- * @return8 H! d- ]/ ^# \# c
- * @throws Exception String
: k, h; P" X* G' ]% {% A - * @Date 2015年6月3日
8 |4 Z5 a% F6 w( X - * 更新日志6 m) ^' b" X+ x! U0 V
- * 2015年6月3日 张志朋 首次创建
" e9 |1 Y- `1 x4 R% o% { - *
! p8 y# \& T- U: G2 K4 K7 D - */
- w' [ i O5 _/ q& o$ {; j9 v/ n$ U - @SuppressWarnings("rawtypes")
% K5 b7 F( m- z6 o7 B4 g) x" Z - public static String getServiceMthodDescription(JoinPoint joinPoint) - L3 [' P k" n0 L# R
- throws Exception {
4 h" O) B/ l( n! Y2 f - String targetName = joinPoint.getTarget().getClass().getName();
( O, C s+ C' ~9 t - String methodName = joinPoint.getSignature().getName(); - F" ] h+ j( i- }
- Object[] arguments = joinPoint.getArgs(); , m+ u* n& C/ w- q( X' ?' }
- Class targetClass = Class.forName(targetName);
# a, Y: R$ g$ r0 f( I1 c' W$ C - Method[] methods = targetClass.getMethods();
5 P' x' j% a$ K, l - String description = "";
( [9 w8 O1 A8 W) z- n - for (Method method : methods) {
# Z- v. L) m6 j, u4 N - if (method.getName().equals(methodName)) { " W: f8 \( q+ a$ h6 a
- Class[] clazzs = method.getParameterTypes(); : d& N+ C% w+ z( \7 a1 X# t' I) b
- if (clazzs.length == arguments.length) {
, T, L5 b' g! ~( {$ U: ?. _ - description = method.getAnnotation(ServiceLog. class).description();
: V' P* m0 H/ z! d2 [% I! h - break; : ^7 g9 B/ g# J' L' K- \" O9 ~
- }
6 x' H1 s9 p+ X, D - }
& @. d9 u/ k' C% @; q5 | - }
$ R$ w2 {# {/ u7 O. z1 k: p - return description; 5 W# e( o) T: V1 Z1 j% u' Y
- }
, Z0 w# y* O/ h, s) ] - }
复制代码 + _: h; e4 c6 X! L) Y
这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。 m5 g8 t* `7 f9 Y2 t7 u, c$ g: _. F
doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
, X, G+ o5 Q. [; l1 J! S$ |$ L( j
3 P, B' v1 k" _6 z- n3 o7 X/ j2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)( I% N& q5 l5 ~1 e: Z k4 n
- @ServiceLog(description="获取待审试题数量") @% `7 E, y9 j: v
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {: Z+ E! q+ a" {$ W% e: g" d! E
- return quesPerDao.getAuditQuesNum(currentUser);
/ b: A, S' E. n - }
复制代码 3 S; G: f! _5 M. R7 M2 X& d
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。
: Q: s2 d. {9 F# w: L二、AOP实现权限控制& Z: X* |5 r+ U- }& \
上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
6 _( \! b+ F1 X 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。3 r" O* V3 R, g O0 @) g
首先配置文件要引入这样一段配置(看注释说明):1 v: `7 c4 K/ t( v2 M
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
+ b: J7 [* }* d& [1 p; g - <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码
' c3 \7 e6 B/ T, P(1)、定义注释:
O" }$ F5 l$ |8 ^7 D F! f, F1.Permission.java
8 s* b7 K+ i7 f" O% x' h- /**: e- v( } L8 O6 G2 r! g2 A
- * 自定义权限管理
0 ], p7 Q* D+ H6 `0 y: P+ [3 n - * 创建者 张志朋
8 ]- K. W( s- n+ t' G% k - * 创建时间 2015年6月30日3 G. j5 G/ X: i! W2 R) M7 f! x" q5 u, ^
- *
* @) [+ s j9 |7 l# p: H - */
7 B8 \5 ^) P3 i# h" C - @Target({ElementType.PARAMETER, ElementType.METHOD})
. Q, Y# Z1 O7 {: X: A. m. C - @Retention(RetentionPolicy.RUNTIME)
9 d' z, i2 X" t( ]7 @1 | - @Documented) z$ O! ~2 d' `0 d& w9 d8 u9 i3 z
- public @interface Permission {
, X2 Y, p/ q$ J$ F5 O3 O+ { - String name() default ""; //操作行为/ o/ x; f: A, O
- int id() default -1;//权限值# B y. ?9 P {1 Q+ E6 H2 v& ^5 ~
- }
复制代码 (2)、定义切面以及切入点
. I3 ]! I0 ]- U- m1.PsemissionAspect.java. [. ~8 e' f6 j+ ?. D
- /**; r3 S( a1 v& M+ t, y& K
- * 权限管理
! ~! e9 u" ]2 v( R - * 创建者 张志朋0 _1 k3 @( E' W6 Z3 _0 p) g0 L
- * 创建时间 2015年7月3日9 ~1 G* M$ M( T
- *- G t2 C( a+ I8 {1 v7 H
- */
5 G3 p5 x8 t+ H* j. I( y2 ]6 J - @Component
) ? E; D M X' U+ l - @Scope
0 `2 P6 j! X, k% h6 G- R - @Aspect; M M/ N# C* j; V
- public class PsemissionAspect {
9 Y/ k5 F& B# z% L. H& E8 V `: |8 x; K -
n. w" P0 u! }5 B( Q - //Controller层切点 用于权限控制8 L6 j' f+ s# w
- @Pointcut("@annotation(com.acts.web.aop.Permission)")
8 ]% v9 n+ o3 F1 E) Q - public void permissionAspect() {
+ x+ E( A$ e3 {+ m9 M7 L3 m -
8 S) _0 @) O! T, i - } _$ `' p7 f6 Y( c
- /** E0 A4 \* g: f$ F
- * 用于拦截Controller层用户操作权限(环绕通知)
2 A% w) A+ m0 H H# ] - * @Author 张志朋: g5 U* a E/ e2 i5 @
- * @param joinPoint void
# `' E* y* p2 [1 E0 J% Y& ] - * @Date 2015年6月3日
! w F _5 q% O } - * 更新日志4 ~% _) M X) n& R9 J
- * 2015年6月3日 张志朋 首次创建: _) V( D* y8 T5 a" }
- *
; x9 m8 n K/ p) r8 y0 K* ] - */
; U7 n) v: A& \ - @Around("permissionAspect()")
4 @: J( r5 r3 \5 q - public Object permission(ProceedingJoinPoint joinPoint)throws Throwable {
" }* O7 O+ R/ c9 U! n2 L8 { - Object retVal = null;
7 G2 B7 R8 K# P+ ?2 l - int role = getControllerMethodRole(joinPoint);( I$ I* x$ a; |, f- m0 J& r* {
- TeacherEntity user = CommonUtil.getUser();$ W# j6 F. o( x0 v
- if((user.getSpecRole()&role)==role){//没有权限
9 v! t+ q8 z' t* D - retVal = joinPoint.proceed();
1 u5 J9 f0 k! q3 X6 Q' w - }else{
, ]3 d5 `2 L: a$ `+ e# o' d# ? - noAuthorization();
# O! g9 M3 t* h" f C: L! z8 l - }8 h# e* a5 I# s) ~# m+ m' a b
- return retVal;
e2 Y$ m2 j) [4 c$ T - }
. U/ T' |* i6 o- s - /**
* g8 n' y& \7 H) P - * 没有权限 实现跳转
& |- r6 J' F6 `; p% S - * @Author 张志朋
, O, V: d) v# [" H# P) b) P2 F/ S - * @throws IOException void. ?( {! c& B: c* Y6 v
- * @Date 2015年7月3日0 X5 [" r N+ l7 d: R) m
- * 更新日志7 ^+ {/ I7 j9 Z- |. `1 R, a
- * 2015年7月3日 张志朋 首次创建6 h# j* O! Q3 v7 b- v
- *
* i. x8 i9 B( [. n2 Q- }# l - */
7 w* d3 Y, P/ |" A: M( p - public void noAuthorization() throws IOException{6 J" X; X+ e [: R) A
- HttpServletRequest request = ServletActionContext.getRequest();
' ]6 `2 V5 l# \% ~ - String path = request.getContextPath();* ]. f' }' U* v
- HttpServletResponse response = ServletActionContext.getResponse();
# P6 x- r5 B3 P: o1 n. o - response.sendRedirect(path+"/pages/noAuthorization.jsp");
0 ~/ m1 B; l0 e - }- t M) Z8 g; k4 j' A1 Y
- /**, C) g# M3 D, I2 d
- * 获取注解中对方法的权限值 用于Controller层注解
& G9 [3 P" Z& n8 H) E @ W - * @Author 张志朋( }3 b9 ^# I: ?( F: ^
- * @param joinPoint* M7 B: d! ~/ p! g7 X W
- * @return1 L/ e9 ]* ~+ f* I/ V
- * @throws Exception int( B, E7 t9 g( Y( t2 ?
- * @Date 2015年7月3日
& l6 `# Z6 m9 B - * 更新日志
4 o4 {8 @$ b8 u# N9 s5 ?% P, e8 G - * 2015年7月3日 张志朋 首次创建
9 ~1 C5 g& K9 n g2 @ - *5 m5 s. l$ E X6 e1 O% e4 |
- */
3 D$ E0 ~4 e. d' S/ {) z, z - @SuppressWarnings("rawtypes") \4 i- `& d/ U+ W: G! }, y& n8 F/ E
- public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {7 ^8 T. G$ k2 U. m: T
- String targetName = joinPoint.getTarget().getClass().getName();
4 h9 Z, F6 L" }1 n - String methodName = joinPoint.getSignature().getName(); - ~. {% a5 ~8 |" l
- Object[] arguments = joinPoint.getArgs(); / N# _8 r8 Z3 l; w$ I$ e" i
- Class targetClass = Class.forName(targetName);
p- |, G' m. L9 t - Method[] methods = targetClass.getMethods();
% ]. a* Y4 @4 | - int role = -1; $ M1 V: |3 ~+ g1 v
- for (Method method : methods) { % J6 L, G2 ]$ h4 f8 g7 U2 R: m
- if (method.getName().equals(methodName)) { 0 o; O- Z2 L! t0 c4 }: {/ H) `% h
- Class[] clazzs = method.getParameterTypes(); 9 E. O2 t: }# j! J, t1 A! {
- if (clazzs.length == arguments.length) {
! C% j! A H+ G* t9 w( s" T! R - role = method.getAnnotation(Permission. class).id();
7 U$ }. ]) N* h2 z+ I' B% h - break;
* J5 U9 L# h; Z: V, h - }
, ^$ K# A" F2 ?3 |& N* X - } ' X2 Y) O9 y1 u; F
- } " `* n) l# k. @( O
- return role;
) S9 F+ l9 K- f: z- L - }
复制代码 8 e8 B o6 o1 i1 X0 _
2.action层代码实现:2 A1 ^+ J' |# J& S j
- /**
; w, |) w+ F' t, j# b& R0 B2 v - * 试题审核不通过
: L' c9 {* X. y - * @Author 张志朋 void% F( r; ]4 i, G9 n
- * @Date 2015年5月4日- H+ s6 D) d$ J- V) u# f
- * 更新日志
" o" l3 h; Y( V: L - * 2015年5月4日 张志朋 首次创建- g7 ~. D' \) Q- ]. I' H
- *
" b) [% N# x1 c9 Q* b! R. a - */0 d, F9 X7 \: ~. j# J# M; A
- @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
. X; t* w* I) _6 ?9 r - public void auditQuestions(){0 X/ z; a4 i: Y" {3 g# b7 y
- try {
; U2 Q" ^% t! N - //代码实现+ m2 B, [" T- f
- } catch (Exception e) {$ V- R+ l, `- a M
- e.printStackTrace();
; f Q( ]# K9 S - }2 `! z* V5 u& ^& d* ]0 \
* ^6 g+ R: N. T9 q- }
复制代码 * W5 @! o' e: L0 r
; K9 J* P& Y# d' C9 b% d- V
|
|