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()+ );
- 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{
- }