一般我们的异常都会抛出到控制层,如果使用struts2也就是action。然后try{//正确代码实现}catch{//在里面记录错误日志},这样咋一看是不错,代码很完美。但是如果项目中有成千上万个项目怎么办?难道在每个action的catch里面都要加入异常记录代码?很显然工作量是很大的。
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.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(各种参数类型详见上面的资料)
复制代码
2.ControllerLog.java
) v7 n! A. q. a
复制代码
(2)、定义切面以及切入点1 M1 m4 u! b7 l
1.LogAspect.java 6 w: S& Y. ^6 J
复制代码
这里说明一下 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
复制代码
之所以定义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
(1)、定义注释:
1.Permission.java
复制代码
(2)、定义切面以及切入点
1.PsemissionAspect.java , X0 h4 T, c% R# u* u6 E& r" a
复制代码
0 D, F; Y& O% c/ v) p
2.action层代码实现:
复制代码
鉴于项目中配置了数据库事务,其实也是使用了AOP 详见applicationContext-service.xml配置文件。由于配置文件的局限性这里采用注释的方式实现。之所有使用注释是可以自定义参数并实现日志的详细记录。既然使用注释,现成的没有,只有自己定义来实现需求。其实spring 注释实现的注入、hibernate的model类注释实现与数据库关联以及我们最常见Override。
资料:java如何实现自定义注释http://www.cnblogs.com/peida/archive/2013/04/24/3036689.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 "";
- }
- 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
- }
1.LogAspect.java 6 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
复制代码
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;//权限值
- }
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;
- }
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();
- }
- }