您的位置:首页 > 编程语言 > Java开发

springAOP实现的异常日志记录+异常邮件发送+权限控制

2016-03-08 17:10 597 查看
一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。

鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring
注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。

资料:java如何实现自定义注释/article/4739577.html+
l# q+ o8 e' ]2 x% ]# F

% t! x- o! A7 m& `8 q" p2 o

一、 记录日志并发送邮件通知4 y! ~7 [/ L! ?4 ]4 s; M

(1)、定义注解:

1.ServiceLog.java(各种参数类型详见上面的资料)

import java.lang.annotation.*;

/**

* 自定义注解 拦截service : ~. L- V8 v( t$ \9 ^6 V% g

* 创建者 张志朋+ x7 c Z# W; b# T- G& W( o: t

* 创建时间 2015年6月3日9 d; P1 X) r6 {6 q# u% h& p6 k6 H

*

*/1 j! l9 _+ e/ z' S

@Target({ElementType.PARAMETER, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME) 1 M; d7 P x$ ^8 x( v( E

@Documented

public @interface ServiceLog { 6 D4 w3 z2 y! A1 h

String description() default "";

}

复制代码
2.ControllerLog.java)
v7 n! A. q. a

import java.lang.annotation.*;
- y6 V9 ~& T" u6 }6 t" D' e% \2 E

/**

* 自定义注解 拦截Controller

* 创建者 张志朋 S; q: G8 h) Y' T

* 创建时间 2015年6月3日0 n- V: l' t( h2 C, Y9 i" [

*

*/

@Target({ElementType.PARAMETER, ElementType.METHOD})
% R, ]9 ?( S2 b

@Retention(RetentionPolicy.RUNTIME)

@Documented+ v% F( p2 \* s( ` _7 c

public @interface ControllerLog {( x2 \5 o$ y/ Y; G0 {, d

String description() default ""; 7 k- m. x% \7 n) O

}

复制代码
(2)、定义切面以及切入点1 M1 m4 u! b7 l

1.LogAspect.java6 w: S& Y. ^6 J

/**

* 日志记录AOP

* 创建者 张志朋

* 创建时间 2015年6月3日! {) e5 l- m* i; O: l# ]

*/ g, h) `& i* q- x+ A! o( C* R

*/

@Component

@Scope8 j0 @$ R: l% p5 I# a: u$ w4 |

@Aspect

public class LogAspect {/ P. [% F( v% ?- I2 [$ R

//Service层切点 用于记录错误日志

@Pointcut("@annotation(com.web.aop.ServiceLog)")

public void serviceAspect() {

Z' e- i4 d! C; R: v

}4 {* e) K' j! k2 c

/**( G8 V$ a D5 X1 m4 h

* 异常通知 用于拦截service层记录异常日志 3 C4 I" p- e) X* O$ F, j

* @Author 张志朋0 u% _ _, P( H) e6 {4 O

* @param joinPoint

* @param e void ~6 [" q- B, K* C& u$ H: ?( z8 i

* @Date 2015年6月3日

* 更新日志

* 2015年6月3日 张志朋 首次创建 ^9 X/ Z8 g; n6 c/ a! r; H5 J

*- X# i8 H: u) A0 `3 ]: _3 z

*/

@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
# F& ^/ ^& e1 Y. g$ f: E

public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
! n$ ~9 v0 t+ R" Z: c/ C) [

HttpServletRequest request = ServletActionContext.getRequest();# [9 ^. \( u% Z/ r1 b

TeacherEntity user = CommonUtil.getUser();

String ip = AddressUtils.getIpAddr(request);, P5 }1 B0 X. }1 D

try {

String params = "";' Z+ y6 N) J& ]: A

if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {

for (int i = 0; i < joinPoint.getArgs().length; i++) {

params += JSONUtil.toJSONstring(joinPoint.getArgs()[i]) + ";";# n& ~! N. H3 [: S" M

}6 h! K6 z F$ ?0 H' I2 c) n

}

String description = getServiceMthodDescription(joinPoint);//用户操作8 d7 r0 {! G4 S; P/ ]! C2 L* m

String exceptionCode =e.getClass().getName();//异常类型代码3 }: p" W3 I1 J

String exceptionDetail = e.getMessage();//异常详细信息; l3 u3 R& D" l

String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";//异常方法

/*==========记录数据库异常日志==========*/ &
^1 }% o# t4 S5 R' `/ \( H8 y& U# R% o

Log log =
new
Log();

log.setDescription(description);

log.setExceptionCode(exceptionCode);8 S! X8 j! C1 h& y2 s& z

log.setExceptionDetail(exceptionDetail);

log.setMethod(method);( f) g) b7 ~: {/ j

log.setType(Constants.LOG_ERROE);// 日志类型, B0 J' P# p6 T* K/ p. m

log.setRequestIp(ip);// 请求IP& o* ?4 e- L, b& A; |6 M

log.setParams(params);//请求参数0 h2 D7 s* s0 @7 K9 ?. t

if(null!=user){, O/ l6 M7 x$ S6 K# |5 r& s! K7 q

log.setCreateUid(user.getUid());//用户ID( z0 L( j$ ~: J" K' J% v

log.setCreateName(user.getNickname());//用户昵称

}" s9 ~: U6 y- G- M' Q' }

log.setPlatFrom(Constants.SUBJECT_CODE);

/*==========记录数本地异常日志==========*/

//LogUtil.error(description, e);; T8 N! s/ l* Q

/*==========发送异常日志到邮箱==========*/

StringBuffer errorMsg =
new
StringBuffer();5 |' L( e# ~+ R% Q' r$ g( U* g

errorMsg.append("异常方法:");

errorMsg.append(method);

errorMsg.append("</br>");& E- N- u; w1 E) M+ Y6 I( L$ [

errorMsg.append("异常类型代码:");0 ^$ R5 I/ R8 {

errorMsg.append(exceptionCode);7 z. ~; m. H) c; C( I

errorMsg.append("</br>");

errorMsg.append("异常详细信息:");

errorMsg.append(exceptionDetail);- X' n: G0 Z" s" Z( o# v) r$ U7 k

errorMsg.append("</br>");

log.setErrorMsg(errorMsg.toString());3 |, C- c5 N3 s, ?/ i& m

WebServiceMathClient Client =
new
WebServiceMathClient();$ u% a& x/ n* s

Client.sendError(log);

} catch (Exception ex) {* d6 U3 M% Z7 n. W0 S

e.printStackTrace();

}* c$ B6 q6 \. N: Z3 w5 y

} 5 \( I% M- y& t) r

/**

* 获取注解中对方法的描述信息 用于service层注解 (基于反射)4 |; L( M9 d" `8 _. m! n& X5 ]

* @Author 张志朋

* @param joinPoint

* @return

* @throws Exception String

* @Date 2015年6月3日

* 更新日志

* 2015年6月3日 张志朋 首次创建

*

*/

@SuppressWarnings("rawtypes")

public static String getServiceMthodDescription(JoinPoint joinPoint)

throws Exception { 0 W* G s D7 D2 b; w. H

String targetName = joinPoint.getTarget().getClass().getName();
% F8 s+ \: Z3 ?7 \$ ]

String methodName = joinPoint.getSignature().getName();

Object[] arguments = joinPoint.getArgs();
6 v$ V7 n5 K& {) O8 x

Class targetClass = Class.forName(targetName);
% _: @/ t$ y* q$ N& j

Method[] methods =
targetClass.getMethods();

String description = "";

for (Method method : methods) { , k R# C- s7 v$ L

if (method.getName().equals(methodName)) {

Class[] clazzs = method.getParameterTypes();

if (clazzs.length == arguments.length) {
2 R# E# k2 G7 I4 c$ X) Y

description = method.getAnnotation(ServiceLog. class).description();

break;

}

} - v8 ~( _: X( Z

}

return description;

} # I/ ?! V; V' q5 o0 z

}

复制代码

这里说明一下 serviceAspect()方法 上面注释了 @Pointcut("@annotation(com.acts.web.aop.ServiceLog)") 、也就说明这是一个切入点,springAOP对其进行了封装。

doAfterThrowing()方法上面加入了@AfterThrowing(pointcut = "serviceAspect()", throwing = "e") 对切入点的所有异常信息进行处理(记录日志到数据库或者发送错误信息到指定邮箱等等等,可以做任何你想做的事情)。

* U: G8 b. E7 a

2.QuesPerServiceImpl.java(注意此类必须实现接口
默认JDK的动态代理实现是基于接口实现的 否则会报错)3 v5 C( c! m% H+ J0 w; `) g/ M" i

@ServiceLog(description="获取待审试题数量")/ \( }1 m3 c8 a

public long
getAuditQuesNum(TeacherEntity currentUser) throws Exception {7 {( q8 U+ l2 ]2 M5 i/ h1 y

return quesPerDao.getAuditQuesNum(currentUser);

}

复制代码

之所以定义description 描述 是为了更好的记录错误日志 文字总是比方法名 更容易识别。! q9 r% w- _% |0 H n3 Z! `

二、AOP实现权限控制

上面说过使用动态代理的类 必须实现接口但是我们的action并没有实现接口。 JDK 的动态代理只能对实现了接口的目标类进行代理,而不实现接口的类就不能使用 JDK 的动态代理。/ B4 `/ O. K$ L; u* ~

还好有第三方的包为我们解决了问题。项目中引入cglib.jar,CGLIB 是针对类来实现代理,当没有实现接口的类需要代理时就需要通过
CGLIB 来实现代理了,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,所以不能对
finall 类进行继承。

首先配置文件要引入这样一段配置(看注释说明):: j# ] _7 X: o/ \5 [* ~% A D$ }% k

<!--通知spring使用cglib而不是jdk的来生成代理方法
AOP可以拦截到Controller(Action)-->! V+ A3 o+ F/ c- n5 b0 U

<aop:aspectj-autoproxy proxy-target-class="true"/>

复制代码
I1 G0 ]! Z& O1 z

(1)、定义注释:

1.Permission.java

/**/ I) L0 }6 W3 p* q! W/ ?( a8 i

* 自定义权限管理

* 创建者 张志朋

* 创建时间 2015年6月30日( n: [8 B& z- F: D4 K* O' ~

*, {9 `4 ~* P- h) P& Z$ i e" J# G

*/7 M' ]! E4 r7 N* N$ z

@Target({ElementType.PARAMETER, ElementType.METHOD})
) y8 _ M9 T. X9 Y- J- R

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Permission {

String name() default ""; //操作行为

int id() default -1;//权限值

}

复制代码
(2)、定义切面以及切入点

1.PsemissionAspect.java,
X0 h4 T, c% R# u* u6 E& r" a

/**

* 权限管理

* 创建者 张志朋

* 创建时间 2015年7月3日+ w; @. ]$ l/ _) X/ f

*4 e( B" q' } B1 C* }( _; y

*/! V; M; x: ]9 X# J; V1 N& c" A2 Q

@Component* s" l. ?' U( r( n# x

@Scope% U( P, l! q+ [1 f

@Aspect, q* ~8 `$ G+ `/ N7 M& w

public class PsemissionAspect {

" E& c, n; x G

//Controller层切点 用于权限控制. G$ L# x% l- a9 ^) o5 ^

@Pointcut("@annotation(com.acts.web.aop.Permission)")

public void permissionAspect() {

}! k G' ?, J4 J5 k

/**

* 用于拦截Controller层用户操作权限(环绕通知)

* @Author 张志朋

* @param joinPoint void3 r# p: b" b$ n3 Z, U

* @Date 2015年6月3日/ b/ C* y+ ^6 \. z' \

* 更新日志

* 2015年6月3日 张志朋 首次创建! o6 d- C3 V: k/ J6 H, ~

** j! O( p+ a6 e& ?

*/, K! V. a, w( D/ G

@Around("permissionAspect()")

public Object permission(ProceedingJoinPoint joinPoint)throws Throwable {

Object retVal = null;+ Z" m9 g" Z# ~3 K' I/ p( |. j

int role = getControllerMethodRole(joinPoint);8 d% J# e o9 s% m2 K" C) X7 E }5 o7 N

TeacherEntity user = CommonUtil.getUser();

if((user.getSpecRole()&role)==role){//没有权限8 K! v4 t$ u1 n9 q, n3 D

retVal = joinPoint.proceed();# \+ Z {' Z/ H/ x8 A8 |4 r( q

}else{

noAuthorization();1 g4 F7 d& _: r

}

return retVal;3 a+ q7 g- Z0 r, S& z& C- X

}: ?$ _1 T2 Q. V& u7 p# R/ ~2 j

/**

* 没有权限 实现跳转4 d7 n' q. c* h5 i0 H& Z3 j& {. m

* @Author 张志朋

* @throws IOException void

* @Date 2015年7月3日

* 更新日志% p2 f, w# [) S, J6 F/ {

* 2015年7月3日 张志朋 首次创建$ v3 H) E- V4 {; S! H9 Q9 `

*, R* K; p) X4 K& ^, ^

*/) ~% Q- M- Y* J$ H: x$ v

public void noAuthorization() throws IOException{

HttpServletRequest request = ServletActionContext.getRequest();

String path = request.getContextPath();

HttpServletResponse response = ServletActionContext.getResponse();0 h- n9 \4 P- n% G8 _7 ~

response.sendRedirect(path+"/pages/noAuthorization.jsp");# v7 O# |# p; a9 B

}+ e: h' w9 b9 r: G

/**- D" C9 P; ~$ x$ k1 y ^

* 获取注解中对方法的权限值 用于Controller层注解 & B" T K! |' @, b; f$ ]

* @Author 张志朋' C3 ^) ?" r. {/ T

* @param joinPoint- G1 M4 T0 O2 h, }2 k; S! ]7 N

* @return2 T; ?7 t. Z; f) T7 g B) G

* @throws Exception int

* @Date 2015年7月3日# d3 v. j9 |6 h# Y- f

* 更新日志) x; M. l9 z( s* I Z; V3 w( b; h

* 2015年7月3日 张志朋 首次创建6 C1 J8 ~7 `; I

*

*/ g* c8 _5 h' f

@SuppressWarnings("rawtypes")

public static int getControllerMethodRole(JoinPoint joinPoint) throws Exception {/ Z5 }$ A' O4 a! c# l

String targetName = joinPoint.getTarget().getClass().getName();
: r. D2 X) ~" t7 j: i% L

String methodName = joinPoint.getSignature().getName();
( ]1 ?! v4 ?% b. s/ W/ o! P

Object[] arguments = joinPoint.getArgs();

Class targetClass = Class.forName(targetName);
9 _- m; s* s6 {9 r+ R O2 [8 Y

Method[] methods =
targetClass.getMethods();
6 w0 q$ D% A4 U+ l

int role = -1;

for (Method method : methods) { " B+ C7 I. \2 m5 i) w R

if (method.getName().equals(methodName)) {
& N) M. w }4 u: j* p v

Class[] clazzs = method.getParameterTypes();

if (clazzs.length == arguments.length) {

role = method.getAnnotation(Permission. class).id();

break;

}

}

} 7 _* H- x2 b1 H

return role;

}

复制代码
0 D, F; Y& O% c/ v) p

2.action层代码实现:

/**

* 试题审核不通过

* @Author 张志朋 void* u/ t3 p Y/ N8 r7 z: e) P

* @Date 2015年5月4日1 H9 N! ^2 T( ^

* 更新日志

* 2015年5月4日 张志朋 首次创建

*

*// C: H: Y, x( b' J

@Permission(name="审核试题权限(审核不通过)",id=Constants.ROLE_QUES_AUDIT)$ C- I" p0 R) S, I! ]

public void auditQuestions(){. o; ?# b6 Y# E% o; z4 r! x! u

try {

//代码实现3 ^9 a2 U3 R' b

} catch (Exception e) {8 q7 {2 Z! Z/ K# R

e.printStackTrace();

}

}

复制代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: