TA的每日心情 衰 2021-2-2 11:21
签到天数: 36 天
[LV.5]常住居民I
忙里偷闲最近在看关于spring Aop的一些功能、顺便完善一下项目。
1 D- O" ?% {# ~% M2 S4 F7 r8 }7 h
一般我们的异常都会抛出到控制层,如果使用struts 2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。1 Y* k/ A/ J9 t
7 c |8 k" e, v9 C1 l/ c/ p
% g. a8 E0 ?/ s8 f$ ]6 m1 r
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate 的model类注释实现与数据库关联以及我们最常见Override。; S" ~8 ?; F) c/ \2 n" h4 h& d
资料:java 如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 1 u, S/ c S) ? x" L* Q' R
0 U- u! ]/ A% g" r$ S 一、 记录日志并发送邮件通知
8 w- F+ p. ?1 c2 w+ ` (1)、定义注解:
) L: G) g) Q% k) w 1.ServiceLog.java(各种参数类型详见上面的资料)
, \+ _: ]: {9 w3 H import java.lang.annotation.*; 6 _% P2 Q0 O+ I3 P3 Y
/**
0 r6 s# b. v3 p * 自定义注解 拦截service + F, `3 O' w+ J
* 创建者 张志朋
; x ~2 J6 f6 g I2 A' k0 p * 创建时间 2015年6月3日" |0 g9 j. D V6 b* p8 v
*# ?% i8 f( P5 u. y7 j9 n
*/- m( O+ o6 S0 p( G
@Target({ElementType.PARAMETER, ElementType.METHOD}) ; G- I6 O. [2 W s% |2 G
@Retention(RetentionPolicy.RUNTIME) 3 ?: {8 Q x7 d+ u
@Documented
: K* {5 f& S2 o0 P: A5 t- _ z5 U public @interface ServiceLog {
J( D! w1 K: `4 }8 Q- f2 y3 l+ W0 \ String description() default "";
5 {; i' Y' d& c: H3 K# ]+ I4 U } 复制代码 2.ControllerLog.java
# c# L2 n/ y- R* E$ t- r: y import java.lang.annotation.*;
# s: x, X/ n; u /**
- r& ]! f+ ]. R! m1 I: n, L' G * 自定义注解 拦截Controller( e( i5 s. F" Z
* 创建者 张志朋5 z* T8 o+ Z; A% h
* 创建时间 2015年6月3日! ^. F! _2 z$ k0 a7 k
*
* y- L4 a5 Y9 m */5 {5 n3 p4 n. ]3 X+ K. C C
@Target({ElementType.PARAMETER, ElementType.METHOD}) : g5 Z1 w2 u2 G4 W; T
@Retention(RetentionPolicy.RUNTIME) 2 f7 V0 n# G& p) ]! Q9 k
@Documented' I; {% D) k" L6 I6 J# L- |2 \
public @interface ControllerLog {5 D5 f, Q; U6 i% A3 R7 W
String description() default "";
, v5 i& T/ {, h6 L( { } 复制代码 (2)、定义切面以及切入点; d" `) a. {( M4 T! p7 f
1.LogAspect.java
! T+ N$ c) S- ~, o: U /**9 q7 Q+ \! W: z+ {
* 日志记录AOP% ?( \7 m6 p) g! @" u
* 创建者 张志朋: P1 e. v: ?6 H5 Y* f- p
* 创建时间 2015年6月3日
, s1 u: a3 w8 a0 ?! A *
. k( l5 w6 z! y) I */7 `* T* u/ y) \5 M7 j
@Component
7 i+ R8 S3 j8 U6 V @Scope
4 |) t# x3 W$ ~. z+ x @Aspect8 r M p: p) k; N$ C
public class LogAspect {% p7 n# ^7 ]) g( |! `2 s/ ^
//Service层切点 用于记录错误日志! t( g/ w- R. y( I
@Pointcut("@annotation(com.web.aop.ServiceLog)") & P6 N% M/ o" X' i- o+ i
public void serviceAspect() {* C1 z ~/ \2 d/ \0 q' y' A7 E
, O8 p- g* N3 i1 r5 Q
}
9 d3 S5 T e7 k0 `" k /**
9 v7 }/ n; i2 w3 q) {1 t * 异常通知 用于拦截service层记录异常日志
9 v; S9 K' W% o7 A; l7 z * @Author 张志朋
$ T. Q/ i' f7 e; |7 ^9 n7 x * @param joinPoint
, {% q% y) v6 V, b' B6 y * @param e void+ o H) Z5 R/ ~& O6 i
* @Date 2015年6月3日
1 r" I Q1 j" W0 d* N) N t' }6 w * 更新日志; N O/ M3 E! N7 z6 p. E
* 2015年6月3日 张志朋 首次创建
3 y$ a" i0 N9 a) M- b *
6 Z# ?" m, J' |* N! Q */2 o+ r5 f( s6 R5 R' D* J! {
@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") & B& V+ i( m. m3 e# K3 l
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 1 v' u, e5 ~% s9 P
HttpServletRequest request = ServletActionContext.getRequest();2 s$ \: {; E% f+ ?
TeacherEntity user = CommonUtil.getUser();
+ y0 h% _& H9 ~7 p% F2 x* ? String ip = AddressUtils.getIpAddr(request);
6 d4 H5 }3 k# C try {& D6 ^* A0 j! J4 ?$ s& N# T+ d
String params = "";$ |! @) P5 @2 X5 ?
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
/ L* I+ f8 L g7 I+ R for (int i = 0; i < joinPoint.getArgs().length; i++) {
3 c; _8 ~$ K) C/ y9 K, s$ D4 f params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";1 J, l* c7 a. `+ Z0 k
}
/ L8 o) [- P2 E' r3 i }' p0 t$ x4 z! y4 C5 q0 [! n1 y
String description = getServiceMthodDescription(joinPoint);//用户操作
. I6 B, M6 s3 |9 T8 R String exceptionCode =e.getClass().getName();//异常类型代码1 f( B9 ?! |3 {9 p# m6 R
String exceptionDetail = e.getMessage();//异常详细信息
; o5 t% B3 m9 @0 u" ^: X- N6 | String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法
/ o0 y$ j* o; ?$ ` /*==========记录数据库异常日志==========*/
! i. `3 v) T# ?) v n3 c: p Log log = new Log();/ i- N, T9 X' Z- ?& s- c6 a
log.setDescription(description);4 i/ m1 ^; O$ ~0 q7 @' B6 I
log.setExceptionCode(exceptionCode);) D) y/ g Y3 D- c
log.setExceptionDetail(exceptionDetail);
# @+ [" G# f4 } log.setMethod(method);1 D' |9 U, U; b' \ H) o, e5 L3 h# E
log.setType(Constants.LOG_ERROE);// 日志类型& b j8 ~6 q& f8 P7 G
log.setRequestIp(ip);// 请求IP0 G0 B) x ~" A, m
log.setParams(params);//请求参数6 |, z* U8 R q6 ^6 t' [( z
if(null!=user){
! x+ z9 f0 x" a6 A log.setCreateUid(user.getUid());//用户ID# V$ F( x( m, u$ J- `: B
log.setCreateName(user.getNickname());//用户昵称
! M' A$ {( r$ g/ d, a }) ?4 `; y# J% [5 f; S2 i, d8 @7 U
log.setPlatFrom(Constants.SUBJECT_CODE);
5 T4 g* t9 j' T2 R /*==========记录数本地异常日志==========*/ $ E8 x {( h( ~' w! B
//LogUtil.error(description, e);
( Q( M# T$ w2 T' |( x /*==========发送异常日志到邮箱==========*/
( Q& D0 K" X, ~; d, l StringBuffer errorMsg = new StringBuffer();
, `* q' G* G% P4 P7 R" V$ a errorMsg.append("异常方法:");
7 ]5 x, |8 P$ d( K6 M3 T errorMsg.append(method);' t4 H$ [1 Y* w' w/ a' P. r
errorMsg.append("</br>");
' i0 l; ~& ~, t- T$ A errorMsg.append("异常类型代码:");
6 D$ K4 F5 m; }$ y; i m errorMsg.append(exceptionCode);
; |, E. h5 l" H, \/ F) U* ? errorMsg.append("</br>");
% A8 B- Y( m5 R- k1 o; W X errorMsg.append("异常详细信息:");
* U9 z3 ]8 K$ l _3 q$ ] errorMsg.append(exceptionDetail);
`1 o6 y' ?$ Q% \ errorMsg.append("</br>");$ b* {' o7 l1 k" ]
log.setErrorMsg(errorMsg.toString());* z, l: o4 u) H$ }; w: w- O
WebServiceMathClient Client = new WebServiceMathClient();
% c6 W" a8 q; ?2 M# Q! C Client.sendError(log);# J& O* z( {$ D1 ~( d" F% N: b/ `1 Q
} catch (Exception ex) {, \6 U2 ~; ^1 M* D4 d5 H
e.printStackTrace();; i% p, F% C% ^3 k' }
} v0 @8 S( e1 a; G3 z! b
}
, m3 n4 X2 L1 \* N" ^- }7 {, A /**
2 ?$ @) ~. @ w- w * 获取注解中对方法的描述信息 用于service层注解 (基于反射)! Q9 g% J- N" a* `8 u0 @* R$ Y
* @Author 张志朋' e; B# A. N/ Y" A* s
* @param joinPoint
* ^$ Z: r3 l7 Z$ |) c& Z * @return
, z6 W% Z5 ? M2 p3 X * @throws Exception String# ~9 t( |% X: q4 P5 k+ o
* @Date 2015年6月3日
8 ]! R1 z" J* E * 更新日志
. V0 O# M8 R7 |' E * 2015年6月3日 张志朋 首次创建
4 v+ j. k* G+ a *
' g& u$ U# B7 }* N8 K& m# Q, Q4 n" y */
# P9 C$ Q$ x# _, w! g* C @SuppressWarnings("rawtypes")
6 I" f8 E1 j2 r& K public static String getServiceMthodDescription(JoinPoint joinPoint) $ A/ Q$ w7 ^/ r& x
throws Exception {
3 ]' L5 P) k2 Q3 O" f String targetName = joinPoint.getTarget().getClass().getName();
: _1 b7 m7 X' h' b# w2 k% i2 I String methodName = joinPoint.getSignature().getName();
/ k0 T1 h+ \4 z0 \- F2 k2 S Object[] arguments = joinPoint.getArgs(); 4 f$ g- Y- A1 T0 a
Class targetClass = Class.forName(targetName);
; r p% ^8 l% d { Method[] methods = targetClass.getMethods(); ' Z1 I$ c- B6 H- ~/ }" t! c
String description = ""; * P1 `$ T# f% \) V# @
for (Method method : methods) {
8 O& Q3 [% N* Y2 G% j if (method.getName().equals(methodName)) { , e6 ]7 ~; @: U/ j% h/ b) u$ |
Class[] clazzs = method.getParameterTypes(); & i- c! |( a6 j+ S( {" p5 A
if (clazzs.length == arguments.length) { - r+ X: a* d, E# o
description = method.getAnnotation(ServiceLog. class).description(); " b$ B% ?, l9 Q
break;
* m/ z/ S O' o V7 }/ Z } 1 N1 d0 T/ H$ ^% D1 H, t
} / D1 F- R* B# ?1 |4 }
} 5 _! |3 a% f$ @; P
return description; 0 @+ k. s1 u: z A
} : f- x9 l& F9 _3 o$ w9 n
} 复制代码
. N0 n& p3 ^; H* t, a) n 这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。
1 T- i9 s( o: l2 w4 k, E6 @9 g& r doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。
: ?" C/ E7 J* r u6 {7 J# I( }
5 l; H S. L4 {+ \ 2.QuesPerServiceImpl.java(注意此类必须实现接口 默认JDK的动态代理实现是基于接口实现的 否则会报错) . `& L" j, z$ H4 o- U
@ServiceLog(description="获取待审试题数量")( z1 x& x( o8 A; v
public long getAuditQuesNum(TeacherEntity currentUser) throws Exception {4 u C- i2 g7 X/ n
return quesPerDao.getAuditQuesNum(currentUser);6 A2 g& x& y, t) E( h# V" y
} 复制代码 * k( f; J0 c& q4 ^! o+ a" `; d
之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。 , ~3 H. a: }* z, D
二、AOP实现权限控制
. n% h# w2 d; [" ` 上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。
& H# v( _8 R3 ]( V 还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过 CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对 finall 类进行继承。% l; ^7 ~! J8 f2 R8 c
首先配置文件要引入这样一段配置(看注释说明):
9 \ f' X( v) E! [2 V! w <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller(Action)-->
. g3 c; T+ k% ] <aop:aspectj-autoproxy proxy-target-class="true"/> 复制代码
( E$ A; K0 }# w7 z O0 _9 w: W (1)、定义注释:
% C) \9 s8 C N0 B 1.Permission.java $ m8 K7 b3 G8 o
/**4 O7 {, F- j; a! I: [7 @7 r7 |, l
* 自定义权限管理
! O+ ]8 Y' s4 _& T- Y) c4 Y * 创建者 张志朋0 K0 T/ B& Y: L+ |
* 创建时间 2015年6月30日
$ P/ [" k- b5 e+ Z3 F2 r6 K *
# _$ V+ k5 K, [+ X. P */: l( V; f) B( h( g- p& c) W S
@Target({ElementType.PARAMETER, ElementType.METHOD}) ; h' O- f0 R5 @8 X
@Retention(RetentionPolicy.RUNTIME) 9 w% b, p6 Y6 C
@Documented; G( H/ G* F8 _
public @interface Permission {
5 P5 w S, f/ A6 {* i, f0 A String name() default ""; //操作行为
- l1 j/ W- \/ k- ?/ f, u& D int id() default -1;//权限值, s3 e' M& n: E5 h
} 复制代码 (2)、定义切面以及切入点3 u' a- p: K$ C8 X
1.PsemissionAspect.java
% @" v5 _$ ] M; O' D! v. \9 ^$ M /**/ B* h$ B" ^9 \- G. \# T
* 权限管理
( i4 P/ l6 ^: O+ ^, _ * 创建者 张志朋+ ?* ^5 J/ y' i3 L, @$ J+ C8 h
* 创建时间 2015年7月3日7 z; E5 f$ h, x9 n5 C& i
*
t1 [0 h% ]0 J4 u */) x4 M3 u: L. k$ w# \# B
@Component
6 m( \ m2 g& K- D* O @Scope% N- C) j/ ~' G) J2 Q
@Aspect/ K3 Q i5 p" n+ t
public class PsemissionAspect {
; o: k. w1 _) `$ ^; A ( N- v0 M+ k* m$ @: \1 z
//Controller层切点 用于权限控制( P2 R% G/ Z# D1 j4 {
@Pointcut("@annotation(com.acts.web.aop.Permission)")
- L/ }, |, e8 |# j6 f public void permissionAspect() {2 U7 }( T1 u0 M" b
, i) X. o( q6 }4 z" e }
: {3 g1 o" `, _ /**
; o* x0 J, |' Y/ p, W * 用于拦截Controller层用户操作权限(环绕通知)3 t- D& d- f& Y- Q
* @Author 张志朋
. M" [$ S* w. u * @param joinPoint void1 D% E: w1 z4 f! p, \/ ]
* @Date 2015年6月3日
6 k) Z/ G0 z# J7 {4 G- O * 更新日志
# I. @% B) P7 k: N7 H& }8 L * 2015年6月3日 张志朋 首次创建& h9 |5 m, Z! n2 [1 @) |
*8 h" L: R3 R' ?: P' d, p
*/" b( b* X0 U/ W, L$ O
@Around("permissionAspect()") * z P8 t* C) e( S7 P
public Object permission(ProceedingJoinPoint joinPoint)throws Throwable { 3 H9 W" s$ ?: k Y7 L" q
Object retVal = null;
) r6 r3 `. }' v, N5 R* m* D' x int role = getControllerMethodRole(joinPoint);, h( A) f1 K: U. K- t$ d; R( l6 l
TeacherEntity user = CommonUtil.getUser();7 [# `2 Q8 N4 A( {
if((user.getSpecRole()&role)==role){//没有权限
$ E6 w6 g+ J& q4 \5 f. P retVal = joinPoint.proceed();5 ^) h- Y2 E. F4 S9 ?
}else{
! E) G& B( @+ \- u- h' h! X noAuthorization();
# P/ F. D" L+ {4 {8 u }# ~. t; Y' ?8 A, |
return retVal;
* s. }, U e5 d/ w" l- b; U }* h4 \+ T. @1 [$ Y7 U3 T* P1 P
/*** B; P: `" _! [( _
* 没有权限 实现跳转, l2 r4 e3 `; a6 f3 |% N" d% w
* @Author 张志朋
7 A, _ l- W n: J" T: s * @throws IOException void# Y. F) Q8 A9 {# U, G
* @Date 2015年7月3日
* u* ?: Q1 c) t9 J/ j/ L * 更新日志; O1 `; b+ X. H2 O
* 2015年7月3日 张志朋 首次创建
1 C) O; n0 s* D% b! i, W, w *" J% a. @# w" {; r$ W/ U
*// F- D7 y/ X ^, C& o; |" l" b
public void noAuthorization() throws IOException{
* t( e# P2 q4 m; x5 G" I, r HttpServletRequest request = ServletActionContext.getRequest();
4 ]4 L R# T' U T _ String path = request.getContextPath();3 D; t; N% [/ q+ h3 L* {2 b
HttpServletResponse response = ServletActionContext.getResponse();3 y; A- p9 \8 c0 @. j
response.sendRedirect(path+"/pages/noAuthorization.jsp");8 ~0 W# a* f6 v, A$ {/ g
}/ U' ?3 g; u' h/ ]
/**, U, v1 G7 z2 T1 D+ }7 v
* 获取注解中对方法的权限值 用于Controller层注解
$ A$ a2 s4 r/ F * @Author 张志朋
/ y* m0 U& G: U5 O( y/ ^ * @param joinPoint
0 g8 x2 R+ u/ k+ ], S * @return$ G3 T* g0 q. z8 s+ D
* @throws Exception int
. q4 a* A" M2 Q0 J- ^* L, e * @Date 2015年7月3日
0 F+ | A. q' w: P1 ^" J * 更新日志+ J: K7 z8 P4 y# B; [1 v8 Z
* 2015年7月3日 张志朋 首次创建
0 n3 g/ ]0 j$ \; D *- v6 @8 V1 H8 W0 t
*/
6 N5 @4 Z4 u. D3 q" a$ h0 m8 S* w @SuppressWarnings("rawtypes")
i( {2 X1 a' ^) z/ M public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {
$ o, u2 _1 s6 `- i) {$ N$ A String targetName = joinPoint.getTarget().getClass().getName(); ' n+ o- h, j$ A$ L7 v" }
String methodName = joinPoint.getSignature().getName(); / P( {$ ?% P4 n/ K
Object[] arguments = joinPoint.getArgs();
: o! S% i0 u# S Class targetClass = Class.forName(targetName); ' D7 p6 \! g; q& u0 R- E
Method[] methods = targetClass.getMethods(); 4 F. T+ m @# Z, ]) {; f, C) K0 H
int role = -1; 5 C% Y! P6 Y7 a' t( y
for (Method method : methods) { 4 c( k" w2 S1 ^+ I8 X
if (method.getName().equals(methodName)) { 3 ~- Z' R* K3 T( p3 O
Class[] clazzs = method.getParameterTypes();
5 q6 @2 U0 l- ]5 F) [; |$ O if (clazzs.length == arguments.length) {
4 c% A5 V1 G* _; S0 N& e4 V. t3 F role = method.getAnnotation(Permission. class).id(); 2 K3 F1 l9 x6 o
break;
8 q4 C9 ^% n, O1 | }( \2 {! {9 s' \2 J& G }
, H/ E) f! k( g8 o1 K } 5 N/ x$ k d/ S; z5 i. B% [* ^
} 1 g6 s8 W% y. @5 A" F0 N/ R
return role; 7 B% m& o4 J9 o) f8 q; L$ z; H) I
} 复制代码
4 c/ u% [2 T: O8 D 2.action层代码实现: * y5 |7 ^( @ b' |1 i
/**/ P/ e" }, n R- w0 l6 m
* 试题审核不通过
+ S& G+ a% L( ? * @Author 张志朋 void/ E- Q; G j% m& ?
* @Date 2015年5月4日8 K0 W$ e* y. C: }' r
* 更新日志* H& d+ A1 ~: s7 A& }
* 2015年5月4日 张志朋 首次创建
& m3 |; d. Z* e7 K5 e6 y ** S! l8 j/ j, `) j7 P2 ~3 t; y
*/
9 z1 g* R6 }: _5 J @Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)! v0 s* J& ]- t5 Q- c$ w' \
public void auditQuestions(){+ M3 x) f; X% E$ e. w( ^' ^
try {
8 a7 a6 ]7 N! v7 w1 F //代码实现% `1 \0 v* g+ k2 ~
} catch (Exception e) {
' V* J- i4 Z. s3 g/ k9 R e.printStackTrace();
9 W6 [! ?2 p' e }
7 G0 C' B$ q$ F" k + B# i7 V+ m+ \% U6 b
} 复制代码
) ?% G- q7 C- [4 ^( ^ ; w f6 |9 f: z2 \3 q7 o
科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关2、本站所有主题由该帖子作者发表,该帖子作者与科帮网 享有帖子相关版权3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网 的同意4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意7、科帮网 管理员和版主有权不事先通知发贴者而删除本文
JAVA爱好者①群:
JAVA爱好者②群:
JAVA爱好者③ :