TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。
* X1 O, C: `4 N0 g0 l 一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
4 c, ?& w; }# V( U/ n; ?+ B
" m; w+ p% Q- }' W) \( ^7 B+ o/ d# \# L$ b8 B& P: D2 r6 M
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
: Q" r8 ?0 ^- X3 l 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html7 E& E0 G) c' l
& W8 D7 q7 R2 k }4 Z+ |一、 记录日志并发送邮件通知) ]! a9 y/ x$ ^4 }3 N$ w
(1)、定义注解: n4 X! {0 E& u+ d+ e& R
1.ServiceLog.java(各种参数类型详见上面的资料)
- M- y- k( \3 Z: a7 [: g, k* _- import java.lang.annotation.*; ) @& a+ X4 ~$ W9 B$ n
- /**9 c" h4 M. L8 z* }" G/ N
- * 自定义注解 拦截service : ~/ g+ u& p! t4 L
- * 创建者 张志朋
* t5 c. Q5 [6 t" N - * 创建时间 2015年6月3日" I* ?) _6 g5 i0 Y* u
- *( c) R4 b X% |8 B6 f T. m
- */
7 X' L5 f \- N( i4 t8 F+ V% Q* c8 ^ - @Target({ElementType.PARAMETER, ElementType.METHOD}) * s& w+ R* ^. P
- @Retention(RetentionPolicy.RUNTIME) ; y* ^9 V( f4 u0 L, i! r' m7 W
- @Documented 4 s5 _' o, V3 q! J6 S3 Q, v$ A
- public @interface ServiceLog {
" R% ?6 f" [; h( t% C - String description() default "";. h4 v& }7 ~3 }: F6 o( l$ O) ^/ b
- }
复制代码 2.ControllerLog.java7 P7 L4 A. X5 ]% ^0 k$ b0 b" p
- import java.lang.annotation.*; 5 R' H9 j2 |1 e" l0 y9 v
- /**
3 ^( x- V1 b8 D9 |+ D; _+ N r7 k9 ~, x - * 自定义注解 拦截Controller
. k4 z' a4 g' n4 p* j - * 创建者 张志朋, z# u% E, Y5 b+ e1 U' e# O4 ^
- * 创建时间 2015年6月3日, q" O2 _( d# ?) c3 K; k; k5 L
- *
1 H7 T! T* b0 w w - */+ B F! ?6 q3 _, p
- @Target({ElementType.PARAMETER, ElementType.METHOD}) $ n) n8 X5 e/ L( m: q
- @Retention(RetentionPolicy.RUNTIME)
! c# S! Z: R. H% Y7 i6 ~3 H - @Documented
( v6 l' ?: m) @# v# r! F# Y# F' Q - public @interface ControllerLog {2 C7 W- S! c4 U7 R2 e9 c
- String description() default "";
" i( I1 N5 j! Q2 x6 W - }
复制代码 (2)、定义切面以及切入点* y8 w$ O/ s8 _! {' n& p
1.LogAspect.java5 D: f+ C) ?! ^8 c6 X
- /*** X- T! s" s+ `' i# i) p' ~! D6 L Y* f- G; U
- * 日志记录AOP" l0 `) u' w, |! [/ n: t3 F
- * 创建者 张志朋$ w$ R2 z0 u3 M Y
- * 创建时间 2015年6月3日
5 q2 Q h) H9 }/ y$ L* X0 C' M - *, U" w t( J) h* g
- */
+ Y5 w. V! G: \ |8 _ - @Component/ z* C# s, P7 d7 x: k& l
- @Scope
9 m) Z$ D8 C$ M! Y/ D! E( ?; [3 h - @Aspect
3 h, z" z) p$ N1 C: X; F8 Z - public class LogAspect {
7 n) A$ d/ w; y! h" s - //Service层切点 用于记录错误日志2 E8 T6 h4 T; m% G- s! O3 o
- @Pointcut("@annotation(com.web.aop.ServiceLog)") 8 B$ Z; g+ l) b6 q/ l! v7 w$ I! I
- public void serviceAspect() {3 C- e( f# K) X: S! j+ Y# ?
- . o, {) g: K. R. X0 e1 _
- }
$ m) j; `7 h ? - /**: p* K* s, w: \3 U( f: d
- * 异常通知 用于拦截service层记录异常日志 ! ?9 G: v' A2 C. @+ i' G( a
- * @Author 张志朋
% k6 ^; g8 d/ m( U8 @. ~ - * @param joinPoint
5 x; ]# l$ f9 E# M# ]1 M! D - * @param e void* H) ]* Q* f& f! w& ~5 X0 G
- * @Date 2015年6月3日
' j; z( \* c0 C% Q' Q - * 更新日志' L+ l5 _8 ~' ^8 q5 C
- * 2015年6月3日 张志朋 首次创建
9 W( V# r8 z! i+ l7 \ - *& [4 I0 f! d& ^% e; }5 l. i
- */
]( k' C; K, G+ f9 v - @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 9 A% }6 O) {- l
- public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 6 f. x& Y! O+ s M
- HttpServletRequest request = ServletActionContext.getRequest();+ G! b9 `% B0 C! b# W6 d/ m, V
- TeacherEntity user = CommonUtil.getUser();
$ A, B% p1 A h+ C9 B - String ip = AddressUtils.getIpAddr(request);% @1 M7 }0 f8 Z8 D
- try {
t5 E& u. g1 X% y, ?7 X - String params = "";
: n. d& g' p; d# V - if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {$ r7 T# d/ q. ^7 `3 q5 \$ f
- for (int i = 0; i < joinPoint.getArgs().length; i++) {* X' U+ a, z# J4 G$ Q5 B0 O0 ~
- params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
* n$ W% d0 v: ?3 e2 l6 |, q - }- r2 g" s1 s$ L7 _, t
- }
! s& v) A2 O4 e - String description = getServiceMthodDescription(joinPoint);//用户操作: D$ B+ F* `. g+ i& k; I
- String exceptionCode =e.getClass().getName();//异常类型代码6 I/ e% \5 J& q i. W1 x$ w$ M0 Z; h
- String exceptionDetail = e.getMessage();//异常详细信息0 G$ g- d% k. l- |% U k$ s9 A
- String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法8 I" G7 K5 Q C+ q% g) t8 ~
- /*==========记录数据库异常日志==========*/
6 e8 a4 T) i3 T- a0 i- k- a - Log log = new Log();2 K6 r1 l+ r: Y, z- i
- log.setDescription(description);
. M$ E6 j7 Z% Y. M3 X - log.setExceptionCode(exceptionCode);: w" L# ?3 ]) `- l4 z8 y; h
- log.setExceptionDetail(exceptionDetail);
- x+ X9 o% S0 d' [% Z - log.setMethod(method);
, u9 {+ V) B+ K7 V/ T P% Q7 O - log.setType(Constants.LOG_ERROE);// 日志类型
8 D1 w% t$ k4 t7 b' ^9 C% N/ h - log.setRequestIp(ip);// 请求IP9 R6 K3 f* V) i6 D. w
- log.setParams(params);//请求参数# K5 L6 B( ]6 n0 _; m: }9 a) a& u! k
- if(null!=user){
4 r1 }/ j" \: w; C! t( j& e - log.setCreateUid(user.getUid());//用户ID ]4 `( G. h, H- M& R/ G
- log.setCreateName(user.getNickname());//用户昵称
}' e( c7 S9 a, s9 _1 ]( ~- ] - }
# J( @ D. {8 \- ^" T% a - log.setPlatFrom(Constants.SUBJECT_CODE);
' W' ` y* f. t1 q - /*==========记录数本地异常日志==========*/
) q" B% i- k' j, |" }, e2 l - //LogUtil.error(description, e);# t- u+ R9 c+ t
- /*==========发送异常日志到邮箱==========*/- v% P: U0 x4 K+ J* k( w6 n
- StringBuffer errorMsg = new StringBuffer();( l1 d- w1 U2 I6 z
- errorMsg.append("异常方法:");
3 N! W& R3 N+ V$ C s4 n - errorMsg.append(method);' ]! l: }; H& n0 M. T
- errorMsg.append("</br>"); L3 z8 O- o, A' {5 `0 P$ x4 V
- errorMsg.append("异常类型代码:");
$ F' p$ p6 j+ L; |1 z - errorMsg.append(exceptionCode);
+ z0 O6 S* ^2 U T - errorMsg.append("</br>");& e5 }7 S5 f+ d+ ?. e
- errorMsg.append("异常详细信息:");6 T0 Z( J/ ?$ y+ u+ u% K' F- A' z" t
- errorMsg.append(exceptionDetail);* }6 q) g* @: X4 S3 J/ p
- errorMsg.append("</br>");- X+ ^! R$ f% [ z8 u8 `
- log.setErrorMsg(errorMsg.toString());0 j* A$ U" p1 `2 c5 `5 n
- WebServiceMathClient Client = new WebServiceMathClient();
! ^5 b: t1 i- M! ]7 [ - Client.sendError(log);# Z& W9 p5 T4 O- f- ~3 u) s* z
- } catch (Exception ex) {
. z* \- Y: Q. ^, t& Z - e.printStackTrace();
( c0 {( \" R0 m; | - }& B4 I; w* o+ f; b& b6 x1 O4 s5 j- q
- }
5 \! l* O3 @% | - /** m6 g0 j% F# M, l" u& N3 F
- * 获取注解中对方法的描述信息 用于service层注解 (基于反射): W0 O5 O% Y% T. b
- * @Author 张志朋
4 } w8 n9 z6 n6 O' R - * @param joinPoint
d! b5 |% c+ e8 H# J* E) Y - * @return! n7 S* u) T- \ T2 a; w
- * @throws Exception String
' V5 \# u! ~; p( c" M* h1 {) I - * @Date 2015年6月3日
( l3 K; D' U6 t' G- d3 E% | - * 更新日志2 C# I9 b, E. s# C; g- X" e( D
- * 2015年6月3日 张志朋 首次创建2 U$ s8 W) x& o* i; Y
- *
) y- X' k; C1 c - */
* m, ^( X, r* j0 y# Q! ?+ U - @SuppressWarnings("rawtypes")
) j# U) @( |# A R0 q4 T0 W- V - public static String getServiceMthodDescription(JoinPoint joinPoint) : p; t! A' e ~
- throws Exception { 9 G1 R$ G8 M& {7 Q8 f0 n% B
- String targetName = joinPoint.getTarget().getClass().getName(); / I2 n6 [% B; h. X
- String methodName = joinPoint.getSignature().getName(); ! ]7 q- d1 J2 c$ O
- Object[] arguments = joinPoint.getArgs();
, Z4 _' f. b: B! V - Class targetClass = Class.forName(targetName); - D( w* w& e* q! g$ f# m6 \
- Method[] methods = targetClass.getMethods(); 6 i) z! }! e% n. p' g" ?
- String description = "";
) h" l- A- \7 \" ^$ H/ ~5 p - for (Method method : methods) {
0 O, v2 R& H1 l8 \: L! Q - if (method.getName().equals(methodName)) {
* Z7 I1 M! Z3 S6 z2 T. z - Class[] clazzs = method.getParameterTypes();
4 p! z. ?0 h h8 {% N# c- I! B e - if (clazzs.length == arguments.length) { 4 O1 l; R; L, G. x# l& {
- description = method.getAnnotation(ServiceLog. class).description(); 0 Y2 e Y( g9 v% l3 _
- break; 9 ]( e' E8 Y0 w2 e7 q+ b1 u
- }
% r L. R: O6 Z; ?+ g - }
' f) R1 F( k V; w! P1 O - }
" Y8 `5 V% {, B2 O - return description;
4 p4 o& \, `# C/ K7 I - }
, O) `2 N; D! C: c0 O; n - }
复制代码
3 B+ K) _( @, l2 W0 i' r, |8 m& n这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。
" H2 g3 V0 q! ~' ]4 edoAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。$ |( \- z' ^! k' o2 k( ~: a, N
% E( C; I- S5 n; K( Z8 J: l2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)+ T9 w* B: R% T" e3 h$ B! @
- @ServiceLog(description="获取待审试题数量")$ b0 \. ~* } F7 {
- public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
! I4 |4 A: z) l3 Y/ S8 R4 S0 A/ k - return quesPerDao.getAuditQuesNum(currentUser);( U2 M6 `- K7 x0 d! Y9 Y
- }
复制代码
: O! b+ P( B4 B6 x2 C0 ?. f! O' I之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。! y7 j8 o. @4 m: f7 S% C; G
二、AOP实现权限控制4 \# d' @! a& F. v ?1 w
上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。8 U$ p% L# a6 ~. G8 ~
还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。( ~% B8 {2 U2 B# u, m- R. h9 U
首先配置文件要引入这样一段配置(看注释说明):- T1 c% i- a9 o7 r* k5 O, I
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->5 p5 ]# V, _) A- G `+ {6 c9 _
- <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 9 Z" b2 i9 @5 m$ N! s( \
(1)、定义注释:7 a; f% n6 s8 V8 W+ ^. d \, O, X3 f
1.Permission.java3 G1 \' {4 p, j* i9 T9 k
- /**
& m3 f& v4 i7 _! V$ I, o: o - * 自定义权限管理
& g Q* @2 l6 P- y2 _* o) E+ q- h - * 创建者 张志朋
, _4 s! N" F0 I; C7 p* J - * 创建时间 2015年6月30日! r7 |- P# }& c+ }4 \8 b/ g( ?& _ P% n
- *9 K3 E# H: V, y8 @- a
- */
Z" O) m) s1 h6 y - @Target({ElementType.PARAMETER, ElementType.METHOD})
' _% J( G4 T$ S2 c- `( T - @Retention(RetentionPolicy.RUNTIME) : b S8 Z; k, G m
- @Documented+ A A. M4 y+ n. f ]: y
- public @interface Permission {) t& v7 m( y0 ?$ J4 y' e
- String name() default ""; //操作行为$ g- j3 k8 j3 o$ `- S; y! J
- int id() default -1;//权限值: R: t* w- {+ G9 d4 D7 g
- }
复制代码 (2)、定义切面以及切入点$ B6 o- i( U' a8 y
1.PsemissionAspect.java
; e' ?/ I6 ]2 ?+ n( J7 W- /**
4 H/ D5 g6 l1 G4 }5 A% N X - * 权限管理" B, [, P2 K4 [% n T
- * 创建者 张志朋
/ U; u9 P$ G: S% A% l# w! h1 U8 ~ - * 创建时间 2015年7月3日
# I8 i8 m" w0 o' P2 S5 w - *. e' u" Z) @2 i4 I$ H! e, Q/ ]
- */9 D3 a5 `* r* L& ]8 O& {
- @Component. x) h4 u% R% s( X
- @Scope3 i& R, T" t$ R% m+ l5 b |" N2 _
- @Aspect; S2 r5 i, I' L8 n2 e7 J
- public class PsemissionAspect {
8 [9 ^. `. f" a1 }, W0 n1 l8 a -
& w" e" T/ `% q% c% ~" r( Z - //Controller层切点 用于权限控制
0 d- V J ^* | - @Pointcut("@annotation(com.acts.web.aop.Permission)")
- s6 _8 i( E- k3 I3 N - public void permissionAspect() {. f: `4 G+ U, a& h
- ) M" w7 \4 ^! |; z
- }4 v5 ^4 s+ B R7 A) }7 h6 b
- /**
% ~+ l/ x3 ?# @( s# j1 M7 J - * 用于拦截Controller层用户操作权限(环绕通知)2 w: z' X! o! z9 e' F- z
- * @Author 张志朋
. u/ _0 f/ O! y' E u - * @param joinPoint void
* I) {7 h* m# P - * @Date 2015年6月3日
b( u% v" M) S3 x - * 更新日志% P- V' T2 W& ?+ b$ R) d
- * 2015年6月3日 张志朋 首次创建# q- O+ }* c7 @: `- A" d, K1 L
- *: x2 q* z* e' H- X* k5 M% n% F' |
- */2 o0 Y5 u f! M1 K, |- h& [
- @Around("permissionAspect()") 8 |! k( p. Y+ L$ t h6 l/ x6 R
- public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { 7 Z" M* ^! G- g% s# b3 }
- Object retVal = null;4 y1 H5 Q2 S0 \" k( q; v% o6 ~% c
- int role = getControllerMethodRole(joinPoint);
9 ~4 [" {1 ^! R' q - TeacherEntity user = CommonUtil.getUser();
1 P5 k1 d2 L5 }/ Q- C( m8 k X2 m - if((user.getSpecRole()&role)==role){//没有权限; E k3 ?! E2 l6 m
- retVal = joinPoint.proceed();
, ?) M8 F8 c8 o# } - }else{
7 C* y' ]+ t L( r6 s6 `; F2 e - noAuthorization();; \8 j/ j6 z1 W8 {. u
- }
3 z. H$ M5 |6 p - return retVal;
4 y& [5 X+ J$ f4 m: G) | - }' a( Y7 f6 e ^' c
- /**( _4 A0 B" a {2 G9 \
- * 没有权限 实现跳转
9 V8 ]+ m8 Y F4 j - * @Author 张志朋
+ O; Q& S* r, O0 |5 d - * @throws IOException void" v+ ^2 K5 O+ a- ~; ^" ?# L/ q5 o# S* A
- * @Date 2015年7月3日- Q1 U0 N7 y) h' s) e
- * 更新日志
9 B" f) K6 e+ K, C - * 2015年7月3日 张志朋 首次创建# e1 J6 E9 n \( p! c
- *+ P/ A' D0 Q. j1 H; z# M4 S! W
- */& w3 X6 b+ N% P
- public void noAuthorization() throws IOException{
) S1 ]; U$ Y6 S - HttpServletRequest request = ServletActionContext.getRequest();5 p/ O* O3 r/ S* R; A6 Z j
- String path = request.getContextPath();; F5 B& Z4 K+ t2 M% R' C0 J6 x
- HttpServletResponse response = ServletActionContext.getResponse();
; z: m! N, L: ~ - response.sendRedirect(path+"/pages/noAuthorization.jsp");
z: G. b4 a6 Q1 r+ ] - }: L' J6 j# [% P/ c- A0 ]" x0 e
- /**% W) Q8 O. q2 K
- * 获取注解中对方法的权限值 用于Controller层注解 / h/ ~+ M, s5 N1 q; R* g- R
- * @Author 张志朋* n% ~) h7 ^1 R" m
- * @param joinPoint
3 a) q3 s# B3 ^# z5 g - * @return3 t8 E1 T% S2 t1 k
- * @throws Exception int
! o9 I! ^/ f: ?9 s1 Y5 u - * @Date 2015年7月3日" b0 `# ^ _) v, c- n
- * 更新日志( P4 {- ^, M$ [0 B! W; G* D
- * 2015年7月3日 张志朋 首次创建
% n, x. ]/ b6 w7 D& W% z+ |3 k - *
. m L) U% S* X5 z# _ - */
: U5 ~+ n" s# o: k! |0 T2 M' E - @SuppressWarnings("rawtypes")% W" x) _; o5 u# z" ?
- public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {$ r R0 y1 N& N, _
- String targetName = joinPoint.getTarget().getClass().getName(); 7 S" n' V& _ y
- String methodName = joinPoint.getSignature().getName(); . D9 C% D6 I6 u& L0 ~8 M
- Object[] arguments = joinPoint.getArgs(); - b/ B3 u0 T. a4 K n
- Class targetClass = Class.forName(targetName);
R! _0 T8 q3 ]+ ]( Q5 p3 k7 E+ m - Method[] methods = targetClass.getMethods();
- S# K# v; n7 A1 z8 Q* F y: Q$ l1 l0 W - int role = -1;
( m) C b5 x9 q+ a& e3 s; q - for (Method method : methods) {
0 ?9 _' F' A h/ |. y - if (method.getName().equals(methodName)) {
8 ~" r* o6 w- m: X - Class[] clazzs = method.getParameterTypes();
* U# I8 A b; o$ h% k - if (clazzs.length == arguments.length) {
' y2 f' C7 m1 c/ |+ M: w' W. z - role = method.getAnnotation(Permission. class).id(); - D- `$ \" [3 a1 A G/ F
- break;
+ F3 e0 M0 s% T4 ~; ~$ A' K; W - }
6 {# N/ x0 r- X+ C. K - } ! }: y/ ~) a5 x
- }
9 F7 y; e0 t, B% {* M8 [ - return role;
5 Z C. f8 g& j- A3 J' h% A1 J* q - }
复制代码 # u8 B0 K6 p4 P) ?
2.action层代码实现:( w/ B( R$ e3 H* u: F- I3 e% Q8 l
- /**& D# V" h) E! m2 \+ M& J
- * 试题审核不通过# l& i( m( x6 M: U4 o( j. q
- * @Author 张志朋 void
! m3 S) q+ G; u5 Z9 c- v; j - * @Date 2015年5月4日5 m9 H. a. N! r; t8 }' ^" v! k
- * 更新日志
$ G ?* {1 b! l( W7 U( ?* p - * 2015年5月4日 张志朋 首次创建# Q, C9 S; f$ P; V; }2 f
- *
4 f5 [9 f, u: d+ G$ L q( _ - */
& e% Z7 V) d+ ~8 K# j G5 K, j- f - @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
( B' s" Y% s* J) R- Z8 \" i - public void auditQuestions(){
2 H) Z8 O8 K, x# f6 l0 @* f - try {5 @/ s) x. Q- a) l, g- z) ?; J
- //代码实现
! m2 K; E$ [3 q# ~) h& T4 ? - } catch (Exception e) {- K2 N$ B c) F9 [: j
- e.printStackTrace();. B4 R8 @, f- W6 I4 \
- }
. ~1 W1 M5 A" U( m7 A2 q0 x - ' r2 G ^3 z1 ?. a2 m( [* z
- }
复制代码 8 |5 T9 k/ ]5 T0 o4 W1 O
$ R/ P7 d n0 n0 h& N |
|