TA的每日心情 | 衰 2021-2-2 11:21 |
---|
签到天数: 36 天 [LV.5]常住居民I
|
忙里偷闲最近在看关于springAop的一些功能、顺便完善一下项目。
$ j8 E" f" f- z& b0 u- i5 ]. U 一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。 s* x4 u& y& ^/ j( m: e( Q e- `
3 C) H: N) E! K
3 |* z. {/ E: `; y% ] 鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
5 e) @. s5 }6 |- a/ x 资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
2 \2 r3 X$ Z* Y6 R/ a' n; S* W7 a
4 @( a7 S6 W( H. F一、 记录日志并发送邮件通知
" a6 R$ S5 ]0 T# d2 `9 g& |( I(1)、定义注解:. ]' S: Y g' J( K
1.ServiceLog.java(各种参数类型详见上面的资料)0 u0 d& R7 [$ i) D+ a5 R1 x) {
- import java.lang.annotation.*; 2 X+ U/ Y* ? X3 G* L, i4 o
- /**3 E6 q+ v# @( l
- * 自定义注解 拦截service
- f5 @* H* x/ n; }8 D3 p2 [ O - * 创建者 张志朋
1 V5 \0 H0 h$ r - * 创建时间 2015年6月3日
) \6 r n L. b4 k) A4 m* {' s/ H9 B - *
- U. I+ b+ G* v6 h - */9 c9 g' C# i$ H0 o/ F% d0 }
- @Target({ElementType.PARAMETER, ElementType.METHOD})
`0 B5 s1 Z. I! A+ H2 Q4 N+ c( O - @Retention(RetentionPolicy.RUNTIME) % J: a. X, ]4 O- U$ Y' d
- @Documented
* t" L) c8 T4 |, Z: q# c - public @interface ServiceLog { % g0 d9 c. R" k2 N& |5 U h
- String description() default "";
r w1 C( H. |( X - }
复制代码 2.ControllerLog.java
3 M4 f' w2 Z# \7 Z: R. h3 w! Z- import java.lang.annotation.*;
' }6 Y0 F! H% J - /**
9 G( g% u2 T4 n1 o+ D" V! t; [ - * 自定义注解 拦截Controller6 V4 n2 i1 A5 p' J8 l" {# w
- * 创建者 张志朋 {0 f$ ^8 o3 Y4 O: R
- * 创建时间 2015年6月3日
% _' D+ N9 T. ~/ S - *( K$ N' G( x: l) @" {
- */ R: u# Y4 W6 m" p
- @Target({ElementType.PARAMETER, ElementType.METHOD})
9 U( O4 J. {" r; C - @Retention(RetentionPolicy.RUNTIME)
& C5 G% H. v4 o4 K# Z2 n - @Documented
% V8 T9 N% H$ Y) v" g6 Q e; ?& H - public @interface ControllerLog {
$ T/ o- f; V$ e% p! h1 ?8 k - String description() default "";
, n8 Z! s; u) p; Q8 f$ u. c3 G% V - }
复制代码 (2)、定义切面以及切入点! B0 B. O3 m& r
1.LogAspect.java
% M; a3 t# {9 V; @3 q- /**: _$ p5 W: b9 u/ s
- * 日志记录AOP& L+ e m5 B0 {+ U+ o
- * 创建者 张志朋
5 `% w* h: A) b - * 创建时间 2015年6月3日
; Q! x1 t+ j( s" x# A) ?( t9 M" A9 @9 A - *
6 ]0 m, n% I% v - */
}, i. r, T+ }. s' G: w( j9 ^( Q - @Component% D; Y3 z% q; I9 N3 j3 M0 U
- @Scope
% K7 ?& o. w( a8 ~9 A" f/ D, q - @Aspect
0 i/ A* c1 x8 ` - public class LogAspect {. T% Y @# M" \% f4 R
- //Service层切点 用于记录错误日志
& O! {" B1 Y& t; l. @ - @Pointcut("@annotation(com.web.aop.ServiceLog)")
6 p- S# E6 x" B* U2 p% R& {; T4 I; n - public void serviceAspect() {8 ^) @' @4 x6 ^" n) P M
- . g% }6 G( v7 x; d, y. ?
- }: ~7 v I9 b2 R, M
- /**
1 B9 _' X7 f6 X8 x. C9 r% [ - * 异常通知 用于拦截service层记录异常日志
& R$ w* B! I C9 N2 V+ Z - * @Author 张志朋
: V" ?4 p" U. ]" H) | E( \ - * @param joinPoint7 r9 C, z2 F# x- p L& h) z: ]
- * @param e void; ^( X; c$ t: `
- * @Date 2015年6月3日
# x- `. Q/ c" Y8 g - * 更新日志
% [' n# b9 }3 k+ u ?: E* ^ - * 2015年6月3日 张志朋 首次创建
) g. n. w4 }& N1 J1 T! B7 V8 a9 w - *- ^& K: D) j$ u' Y
- */
' L4 ?/ k V; E! D5 j, K - @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") $ H8 U3 b+ `: f( t2 ^% y6 C' l
- public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { % P/ ?* [- U# X" Q; z
- HttpServletRequest request = ServletActionContext.getRequest();
1 d$ R, `6 [6 @$ E6 N - TeacherEntity user = CommonUtil.getUser();4 D6 h" C2 s6 J9 e
- String ip = AddressUtils.getIpAddr(request);
! \ f+ ^0 ^& S) I: [5 Y - try {3 O7 W7 |. u: A4 {* v7 {2 N* z
- String params = "";' c6 t6 O& f& r: p$ U4 _
- if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
" y" m/ B& }1 Y0 K- G - for (int i = 0; i < joinPoint.getArgs().length; i++) {
( H& E4 t, m) Y3 `7 x) [ - params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";
* `' ?! C3 O! e2 x8 T* b; o/ g - }
9 r0 }) ^, D' o" {5 h8 T2 Z5 [ - }+ x( ]# q: c2 \9 x
- String description = getServiceMthodDescription(joinPoint);//用户操作. |, d6 c' U" D
- String exceptionCode =e.getClass().getName();//异常类型代码( ?7 t* w. [- O3 a1 C0 k
- String exceptionDetail = e.getMessage();//异常详细信息& J# y, I" [) B# _
- String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
, a0 b: u( w- N& m0 @ - /*==========记录数据库异常日志==========*/
, q" s7 B1 }. O6 h: g - Log log = new Log();( @1 m7 z% b! Z* n- A
- log.setDescription(description);
$ x2 [. {8 _: J7 I$ X/ P, l! H - log.setExceptionCode(exceptionCode);
" z/ ]: N7 T, ]4 L2 [* V2 V - log.setExceptionDetail(exceptionDetail);7 h& h* Q3 I- L* U& L
- log.setMethod(method);
: ]7 c9 d5 z$ p3 ? - log.setType(Constants.LOG_ERROE);// 日志类型
5 c* @/ [3 g9 @+ m - log.setRequestIp(ip);// 请求IP) F2 Y" }/ ^/ |! P- z c! f
- log.setParams(params);//请求参数' |" N c: u2 A2 U& F- U
- if(null!=user){. v9 n) q0 n& v( ^# l% t
- log.setCreateUid(user.getUid());//用户ID. E; J1 P+ b& G' k/ Y+ ], x
- log.setCreateName(user.getNickname());//用户昵称! M M8 Z; X) R- v4 K( Z
- }
$ f- Z. k3 h L - log.setPlatFrom(Constants.SUBJECT_CODE);
* |2 ]; u$ V- ~- B - /*==========记录数本地异常日志==========*/
+ W% G# k/ n7 A4 w" a - //LogUtil.error(description, e);( d E! i, E9 q: R' C% I. M
- /*==========发送异常日志到邮箱==========*/4 ~0 M H( s5 l+ f3 q
- StringBuffer errorMsg = new StringBuffer();
, X/ M9 U: h2 ^1 X: `$ ? - errorMsg.append("异常方法:");) {, [$ t. E; k- b
- errorMsg.append(method);" ^" x- Y* }8 z2 |( F1 V1 d6 O% l1 F
- errorMsg.append("</br>");
* u2 c# n, Q: t8 y. o - errorMsg.append("异常类型代码:");
9 G3 U7 d' `% F: i# K; P - errorMsg.append(exceptionCode);$ h% S) V3 i2 U
- errorMsg.append("</br>");
1 v4 f/ V$ L7 g8 t - errorMsg.append("异常详细信息:");
* `* c+ Y8 |/ ]/ } - errorMsg.append(exceptionDetail);
' Z& X! {# z: c! ^0 y - errorMsg.append("</br>");
0 n. F: g5 J. w: D# C, T2 N/ G - log.setErrorMsg(errorMsg.toString());$ w+ x' E. H% g! R+ r0 S
- WebServiceMathClient Client = new WebServiceMathClient();
; L1 U$ v& z/ C, C* h - Client.sendError(log);$ R" R0 P; W) Y4 o$ i
- } catch (Exception ex) {4 P2 n! t' { T+ t) q4 c9 ^
- e.printStackTrace();' ^* E) ^9 d8 z" r2 a$ p
- }2 o, Y$ ]# e) i5 F5 }9 J
- } 5 f6 v! Y1 |& B0 f, u9 Z9 f
- /**! d$ Z" q" o$ n( E' W
- * 获取注解中对方法的描述信息 用于service层注解 (基于反射)
' s' e3 ]' I" Q8 T" p- o$ v( c - * @Author 张志朋
. s6 `2 w6 b, K, E G5 M3 i - * @param joinPoint
2 A! C' c A$ ^+ }9 n - * @return
. @2 Q" g, S* ^* l5 j9 M3 j, v - * @throws Exception String5 O+ L3 c$ M! }0 d
- * @Date 2015年6月3日% {( V, l8 p4 |; e; X
- * 更新日志
% ~# b! Z5 f2 d* N Z - * 2015年6月3日 张志朋 首次创建8 Z+ v0 n" a: L9 n. J5 S; O
- *0 x: _+ ^; z$ w* U! B+ r5 e2 F
- */" P0 |) X& x# o8 [5 R8 V0 Y$ @2 ~
- @SuppressWarnings("rawtypes")! j6 s* E& W- O: _9 T
- public static String getServiceMthodDescription(JoinPoint joinPoint) ( d; j" [" P7 V
- throws Exception {
1 r# \8 X& i T8 t; H4 A6 m# | - String targetName = joinPoint.getTarget().getClass().getName(); ; K9 r0 s1 m/ K
- String methodName = joinPoint.getSignature().getName(); $ P- p9 d6 w6 N6 X. V
- Object[] arguments = joinPoint.getArgs(); $ v+ m. f/ g1 S0 n8 K# w- B3 K
- Class targetClass = Class.forName(targetName); ; \9 U7 |, F- t. g! h! A5 b
- Method[] methods = targetClass.getMethods(); 3 l3 L5 h4 s' F
- String description = ""; % `1 H* e' j4 e: T4 G
- for (Method method : methods) {
3 A) s a+ D' w7 P1 u - if (method.getName().equals(methodName)) {
/ i; l9 R( B8 Y$ e8 E - Class[] clazzs = method.getParameterTypes(); 1 s! \6 N; n% B$ Y& z
- if (clazzs.length == arguments.length) {
' c. F7 k% e0 ~; H- K - description = method.getAnnotation(ServiceLog. class).description(); 9 B, ^, ]. R$ u: A: ^0 _
- break;
8 N% d2 B9 A0 `: | - }
! G- c( `) g" D: q6 L1 Q3 \ - }
0 z o5 g! Z7 q, Z0 y M - }
/ i, y& \/ D1 z* ^0 y, y" y - return description; 5 a ^' m- I G9 x) L
- } * Y" a, _+ L0 b7 _& I5 v5 K& T$ ?
- }
复制代码 % `9 n( `. i$ T' a
这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。
, A9 M0 C8 d5 P* L# y" TdoAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
7 @4 a$ \2 @6 S( {5 D5 r) |
/ {& |& ^7 Z/ N# w- S& I2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错)
$ R3 x* |9 _! {/ Z8 d6 T/ {$ i8 W- @ServiceLog(description="获取待审试题数量")
4 q$ v8 W$ h- @. n - public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {
2 J% O) N% u+ a6 A4 g c - return quesPerDao.getAuditQuesNum(currentUser);3 g, q4 g* p5 Q9 H0 V4 w8 S; W0 B
- }
复制代码
/ z9 \; A' d2 V之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。6 S" C. Y. H/ z( t ?, E
二、AOP实现权限控制" t L$ o( G) R, m- \
上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。8 _) S, |6 L7 r% w! V
还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。0 `! I+ F: b4 G2 n3 ?
首先配置文件要引入这样一段配置(看注释说明):" W6 o3 q ~0 ` H2 R- h+ B: m
- <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
% ^2 ], K; \. p2 o3 }3 b! _; | - <aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码 & k C+ ~- p- O
(1)、定义注释:
/ {" j/ F& a3 s% s1.Permission.java
, L( G' }0 M9 ?* N& c7 r# C% Q- /**
! l* L7 U2 ]2 V& f - * 自定义权限管理% a: n* r1 _$ x7 r
- * 创建者 张志朋! T% n* F( k L! z' {
- * 创建时间 2015年6月30日
8 m" ~' B* B6 z( [3 x3 n - *9 L7 M5 C0 Z4 p9 m4 ?
- */- c2 D* F( T/ R& A N) Z) ?$ [4 R
- @Target({ElementType.PARAMETER, ElementType.METHOD}) 6 D- D) E: j# d0 s4 t3 g
- @Retention(RetentionPolicy.RUNTIME) 0 O; s" T9 ?; l; o/ W
- @Documented
" y( H7 \' B: f5 F - public @interface Permission {
~+ h8 O$ B7 l6 W0 [" _ - String name() default ""; //操作行为
( K0 P3 a5 ]& i: B% E6 Y - int id() default -1;//权限值
* L: B& w2 F% ~% p* d- H" a - }
复制代码 (2)、定义切面以及切入点
* f8 g4 i. R3 C+ \6 ~7 `5 K1.PsemissionAspect.java
9 V l5 O2 \0 j- /**- e( O( f7 V* y- s5 B
- * 权限管理
$ r$ `6 J" c- a - * 创建者 张志朋% A$ C2 p! o1 o5 I4 t
- * 创建时间 2015年7月3日( a1 L" f4 o( [8 l" G
- */ I* s: P& p. y. ^6 A: W- y3 a
- */0 x! E8 e: B; ^% j
- @Component
+ }8 C( Z: w) K$ H - @Scope4 e+ i! H5 w1 t% d- A8 U/ ?
- @Aspect, M- d, c. ]/ E0 ]
- public class PsemissionAspect {2 C+ m/ g; |. E& {0 |
- # d, m, t4 d$ i4 O" ^3 ?* O
- //Controller层切点 用于权限控制
5 S. B% Y8 v& S( G+ g. } - @Pointcut("@annotation(com.acts.web.aop.Permission)") 9 h& s: g3 D/ k$ E! ^7 F0 C
- public void permissionAspect() {
5 m6 o2 {# p& s7 Q; m -
4 A( U- R% ^5 u$ S P - }
: O. ~1 R; C3 [: t' I) U6 }' b - /**3 ]0 s/ t& t6 U# P- T0 N) H$ h+ B3 \
- * 用于拦截Controller层用户操作权限(环绕通知)
. j4 D3 l. Y1 D' B - * @Author 张志朋
. A7 J6 j* x: }& |* y! N - * @param joinPoint void
$ K) J/ c0 a& [, x6 V6 t - * @Date 2015年6月3日
- [2 z1 ?8 D9 B7 Y- g2 v - * 更新日志
7 a5 I4 \7 ~5 S3 I; s! K; K7 {: Y - * 2015年6月3日 张志朋 首次创建1 e# P; L: m% y4 a7 h/ y. ~
- *4 N! P& L) h: T( [; n+ }9 O8 `
- */
7 s" @. v. d# [9 d$ V - @Around("permissionAspect()") . A- q9 P! G% ~2 b& S/ H
- public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { ) K" |5 G- [3 G1 F7 V& |
- Object retVal = null;4 z6 y3 W+ x, ^3 `
- int role = getControllerMethodRole(joinPoint);
2 K) a; o1 ~; t& K& x( Y6 y+ _' `/ b - TeacherEntity user = CommonUtil.getUser();
; ?8 X* | H" V7 y: e3 E; H - if((user.getSpecRole()&role)==role){//没有权限
4 X2 \# i* N2 k' C: F$ m: A& @ - retVal = joinPoint.proceed();) ^2 r6 M% U. }8 M
- }else{5 \ s6 h+ \8 ^ c; p: K# z* v
- noAuthorization();
1 w+ p( p. O! o) p6 a' v, B - }
' S2 e& b k% U3 E" a+ h3 F - return retVal;% [+ w+ m* I* F. T \+ O6 t
- }
/ I' `7 d" W, c0 n0 O/ j - /**2 V: ~7 F+ R4 j% W' Y3 N6 m
- * 没有权限 实现跳转
* H4 c* j6 z3 }9 _6 \9 b0 i, m8 o* V - * @Author 张志朋
7 R3 u0 V" r, G( p0 ` - * @throws IOException void
& j) f+ [8 E) b& P0 `* O# n - * @Date 2015年7月3日/ H, q8 t: {% ^$ {2 b% u7 S8 o1 T
- * 更新日志
5 s6 ]/ y h% f$ n+ _ \ - * 2015年7月3日 张志朋 首次创建9 C! l$ F9 t; J8 `. I! O0 L! Y
- *+ B4 K, Z' ` T R! z
- */
, F& Y5 s( Y1 ?1 r3 n2 N - public void noAuthorization() throws IOException{
; b! z+ C* ^0 P4 I7 o' q8 E - HttpServletRequest request = ServletActionContext.getRequest();
' y5 d: U3 r8 n- ] - String path = request.getContextPath();2 F( T+ C: y* B! ^5 A0 x* v0 j; n
- HttpServletResponse response = ServletActionContext.getResponse();
5 w! y+ v s$ @2 @- ]9 j0 q. q - response.sendRedirect(path+"/pages/noAuthorization.jsp");- u: q! h9 y/ w) F# ~
- }
/ O; R# R% R5 S6 w - /**# r G8 x# ?" v! p/ t8 I
- * 获取注解中对方法的权限值 用于Controller层注解
: g7 F% W* D5 {% ? - * @Author 张志朋$ u7 T6 V4 T, K E* q
- * @param joinPoint) _2 Z! X4 A& ~
- * @return
, b$ c/ _4 s9 p - * @throws Exception int
1 y' g; z' ~& w! }- ? - * @Date 2015年7月3日
2 M6 I: o1 m9 \3 Y) C: F - * 更新日志% O5 m& M" f1 z4 A* {* w# C
- * 2015年7月3日 张志朋 首次创建
# y, M1 u' \# M [! l e - *
K& m, x1 X/ @% ]4 x7 [2 i, L - */
s# U& C& p4 Y2 ]0 {; u - @SuppressWarnings("rawtypes")
9 m5 Q/ p: N1 j2 T - public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {
0 \ E3 k5 H! S% m& J - String targetName = joinPoint.getTarget().getClass().getName(); s5 I' x2 o I( C9 p8 X
- String methodName = joinPoint.getSignature().getName(); % x8 i& q7 p Z4 T o
- Object[] arguments = joinPoint.getArgs();
6 D: s/ h9 S: ] - Class targetClass = Class.forName(targetName);
, G! G1 e2 ?1 Y* d& g - Method[] methods = targetClass.getMethods(); . j; s2 _3 w8 m* ^9 V
- int role = -1;
* Q0 J- q, }, {% O: i1 W - for (Method method : methods) {
1 a. ^" N8 P) d - if (method.getName().equals(methodName)) { 4 K$ A/ C @. P2 h
- Class[] clazzs = method.getParameterTypes(); + A5 E+ Y% N$ G
- if (clazzs.length == arguments.length) { 6 Q; j- n4 J; \ v+ I U& J, ]! V
- role = method.getAnnotation(Permission. class).id(); 7 ]/ F: d) k9 w% m. j3 b8 H. l" h
- break;
: e5 W I: W/ w j2 Y$ _ S - }
8 @6 B% _1 b8 G; E0 \) f - }
7 Q# y* Y7 {) Y% j6 z ?/ J - }
' x: y! D0 n8 D - return role;
( E1 o, t: X$ ~7 @ - }
复制代码
2 L7 v" `# |3 G: \0 l. k2.action层代码实现:
; e9 x8 {5 \7 _5 ]4 s6 D* U# R- /**
! U. W8 B; K1 e4 ?" m - * 试题审核不通过" e& L: k& k9 J: M
- * @Author 张志朋 void5 \2 n% V. G6 N8 @- x2 s; c! W
- * @Date 2015年5月4日
) ?0 G% S) e/ d w% K& E% ^$ \ - * 更新日志; G* x9 |0 R0 `+ ?/ }# Z
- * 2015年5月4日 张志朋 首次创建
0 D" o Z( i! i8 X$ {: z6 [ - *
0 ?% O* A9 l) ~* k - */$ ?5 ]5 |+ ^2 |) ?
- @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)
2 j4 U4 T( ?7 P; g; s - public void auditQuestions(){
% a4 z, ]/ F3 b" ~: F9 | - try {
" {5 O4 _1 I% L - //代码实现
( Z- |. B6 F8 U# B - } catch (Exception e) {7 @0 ` @0 B& `' w7 a2 h( t3 ?
- e.printStackTrace();
4 |' E3 H) w/ `6 c! b5 |- P - }
( G n5 L% i9 I9 E - ) t0 p7 v9 F2 Y5 t
- }
复制代码 * X) K2 y; H3 _
# E6 _( ?8 n0 D6 l. L
|
|