Spring 切面编程的目的是实现代码的业务逻辑的解耦。切面编程用于诸如日志记录,事务处理,等非业务性的逻辑操作。目前Spring的Aop只能应用于方法层级上,无法在类、成员字段等层级上操作。以下是Srping的Aop编程分为注解方式和xml配置方式。以下过程详细说明了通过注解方式实现AOP编程的过程。
第一步:自定义注解
-
/**
-
* 定义自定义管理员注解
-
*
-
*/
-
public
@interface RoleAdmin {
-
String values() default "";
-
-
String descript() defalut "自定义管理员注解";
-
}
-
第二步:编写AOP切面类
-
/*声明为组件,让spring 自动管理*/
-
@Component
-
/*声明该类为切面bean*/
-
@Aspect
-
public
class RequestInterceptor {
-
private
final Logger logger = LoggerFactory.getLogger(RequestInterceptor.class);
-
-
/* 拦截管理员访问的请求,(定义切点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点)*/
-
@Pointcut(
"@annotation(com.frame.annotation.RoleAdmin)")
-
public void adminRequired() {}
-
-
/*定义前置advice,同时接受JoinPoint切入点对象,可以没有该参数
-
在环绕通知里面proceedingJoinPoint参数是必须的,其他情况JoinPoint 并不是必须的
-
*/
-
@Before(
"adminRequired()")
-
public void adminCommon(JoinPoint point) {
-
HttpServletRequest request = getRequest();
-
LoginUser user = (LoginUser) request.getSession().getAttribute(Constants.SESSION_USER_KEY);
-
if ( user ==
null ) {
-
setForward(request);
-
throw
new NoAdminException();
-
//throw new NotLoginException();
-
}
-
if ( user.getMemberType() != Constants.MEMBER_TYPE_ADMIN ) {
-
throw
new NoAdminException();
-
//throw new NoAuthException();
-
}
-
String mac = (String)request.getSession().getAttribute(AdminUtil.ADMIN_MAC_ATTR_NAME);
-
if(UtilString.isEmpty(mac)){
-
throw
new NoAdminException();
-
}
-
}
-
-
private void setForward(HttpServletRequest request) {
-
if (
"get".equalsIgnoreCase(request.getMethod()) ) {
-
String fw = request.getRequestURI().substring(request.getContextPath().length()+
1);
-
String qs = request.getQueryString();
-
if ( ! UtilString.isEmpty(qs) ) {
-
fw +=
"?" + qs;
-
}
-
-
logger.debug(
"After login will forward to : {}", fw);
-
request.getSession().setAttribute(Constants.URL_FORWARD_KEY, fw);
-
}
-
}
-
-
/** * 在切面中获取http请求 * @return */
-
private HttpServletRequest getRequest() {
-
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
-
}
-
}
-
第三步:在application.xml中配置aop代理
需要在application.xml中添加AOP代理。AOP代理分为JDK代理和cglib代理
如果配置proxy-target-class="true",则表示是cglib代理。如下:
-
<!-- 激活自动代理功能 -->
-
<aop:aspectj-autoproxy proxy-target-class="true"/>
如果采用JDK代理,则配置如下:
<aop:aspectj-autoproxy/>
-
第四步: 定义切面的引入点
/*在需要切面的方法上定义JoinCut点*/ @RoleAdmin(descript="在这里定义") @ResponseBody @RequestMapping(value="admin/activity/updateComment") public boolean updateCommentIsDelete(HttpServletRequest request,CommentPlan commentPlan){ return false; }
切入点匹配多个连接点的两种方式
- 通过@annotation精确切入到某个自定义标签中
@Pointcut("@annotation(com.frame.annotation.RoleAdmin)")
- 通过exrcution模糊匹配
@Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern:方法的操作权限
- ret-type-pattern:返回值
- declaring-type-pattern:方法所在的包
- name-pattern:方法名
- parm-pattern:参数名
- throws-pattern:异常
其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
AOP获取注解和Target对象
-
/*定义前置advice*/
-
@Before(
"execution(* cn.ysh.studio.spring.aop.service..*(..))&&@annotation(roleAdmin)")
-
public void adminCommon(JoinPoint point, RoleAdmin roleAdmin) {
-
/*输出注解描述内容*/
-
System.out.println(roleAdmin.descript());
-
-
/*利用反射显示目标对象的相关信息*/
-
String targetName = joinPoint.getTarget().getClass().getName();
-
String methodName = joinPoint.getSignature().getName();
-
Object[] arguments = joinPoint.getArgs();
-
Class targetClass = Class.forName(targetName);
-
Method[] methods = targetClass.getMethods();
-
String description =
"";
-
for(Method method : methods) {
-
if(method.getName().equals(methodName)) {
-
Class[] clazzs = method.getParameterTypes();
-
if (clazzs.length == arguments.length) {
-
description = method.getAnnotation(SystemControllerLog.class).description();
-
break;
-
}
-
}
-
}
-
在Spring中,任何通知(Advice)方法都可以将第一个参数定义为 org.aspectj.lang.JoinPoint类型用以接受当前连接点对象。JoinPoint接口提供了一系列有用的方法, 比如 getArgs() (返回方法参数)、getThis() (返回代理对象)、getTarget() (返回目标)、getSignature() (返回正在被通知的方法相关信息)和 toString() (打印出正在被通知的方法的有用信息)。
1、如果要设置多个切点,则使用||进行拼接
@Pointcut("execution(* aop.annotation.*.*(..))|| execution(*com.action.admin.*.*update*(..))")
2、环绕通知的方法中一定要有ProceedingJoinPoint 参数,与Filter中的doFilter方法类似)
-
@Around(
"execution(* aop.annotation.*.*(..))")
-
public Object doAround(ProceedingJoinPoint pjp) throws{
-
-
}