Spring 注解方式实现AOP

什么是AOP

AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表;而动态代理则以 Spring AOP 为代表。


Spring AOP与 AspectJ的异同

Spring AOP 同样需要对目标类进行增强,也就是生成新的 AOP 代理类;与 AspectJ 不同的是,Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。
Spring 允许使用 AspectJ Annotation 用于定义方面(Aspect)、切入点(Pointcut)和增强处理(Advice),Spring 框架则可识别并根据这些 Annotation 来生成 AOP 代理。Spring 只是使用了和 AspectJ 5 一样的注解,但并没有使用 AspectJ 的编译器或者织入器(Weaver),底层依然使用的是 Spring AOP,依然是在运行时动态生成 AOP 代理,并不依赖于 AspectJ 的编译器或者织入器。
简单地说,Spring 依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要 AspectJ 的织入器支持;而 AspectJ 在采用编译时增强,所以 AspectJ 需要使用自己的编译器来编译 Java 文件,还需要织入器。


写本教程的目的

对于初次了解spring aop的小白来说,spring AOP 基于注解方式的实现,在网上搜索了很久都没有发现合适的一个讲解教程,并且AOP不是很常用,为了以后找笔记方便,并且避免初始配置时犯了许多错,因此才有了本教程。


面向切面的编程步骤
  • 定义切面 : 也就是声明注解@Aspect
  • 定义切点 : 也就是声明注解@Pointcut
  • 定义增强方法: 也就是声明注解@Before,@After,@Around,@AfterReturning,@AfterThrowing 的增强处理方法代码

demo 测试需求

在jsp界面登录的时候,实现在登录之前,之后,出现异常的时候做一些处理,假设说我们现在的登录逻辑已经很复杂了,不允许在登录界面继续添加复杂的处理逻辑,这个时候使用AOP就能达到这样的效果。


创建一个java web 工程命名为 SpringAop-test 整个工程的目录如下

工程目录
第三方库目录

web.xml 文件配置如下:
<?xml version="1.0" encoding="UTF-8"?>
    
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
                 version="3.0">
    <!-- 配置启动spring -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>
                org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/xml/ibatis-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  
  <!-- 添加编码过滤器 测试demo可不配置 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
</web-app>


ibatis-config.xml 配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    ">
    <context:component-scan base-package="com.spring" />
    <!-- 支持aspectj aop注解 -->
    <aop:aspectj-autoproxy />
    
            
</beans>

切面类代码如下:
package com.spring.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserAspect {
    @Pointcut("execution(* com.spring.service.I_UserService.checkUser(..))")
    public void loginAspect(){}
    
    @Before("loginAspect()")
    public void beforeLogin(JoinPoint joinPoint){
//      1 第一步执行
        System.out.println("beforeLogin action---");
    }
    
    @After("loginAspect()")
    public void afterLogin(JoinPoint joinPoint){
//      4 第四步执行
        System.out.println("afterLogin action---");
    }
    
    @Around("loginAspect()")
    public Object aroundLogin(ProceedingJoinPoint joinPoint)
            throws java.lang.Throwable {
//      2 第二步执行
        System.out.println("执行登录方法之前,模拟开始事务 ..."); 
        Object rvt = joinPoint.proceed(); 
//      5 第五步执行
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("参数:"+method.getTypeParameters()+";切入方法:"+method.getName()+";返回值:"+method.getReturnType());
        System.out.println("执行目标方法之后,模拟结束事务 ..."); 
        return rvt; 
    }
    
     @AfterReturning(returning="rvt", pointcut="loginAspect()")
     public void afterReturning(Object rvt){ 
//      3  第三步执行
         System.out.println("获取登陆结果返回值 :" + rvt); 
         System.out.println("添加登录成功日志功能 ..."); 
     } 
     
//   如果执行切点方法被抛出异常,则1,2,4步执行后执行@AfterThrowing增强处理。 @AfterReturning将不再执行
     @AfterThrowing(pointcut="loginAspect()",throwing="ex")
     public void afterThrowingSayHello(Exception ex){
         System.out.println("After Throwing : "+ex.getMessage());
     }
        
}


控制器类代码如下:
package com.spring.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.spring.service.I_UserService;

@Controller
@RequestMapping("/user")
public class UserController {

    @Resource 
    I_UserService userService;
    
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public String loginAction(String userName,String userPsw) {
        boolean checkOK = userService.checkUser(userName,userPsw);
        System.out.println("________________"+checkOK + "___________");
        return "/index.jsp";
    }
}


接口类代码如下:
package com.spring.service;

public interface I_UserService {

    boolean checkUser(String userName, String userPsw);
    
}


实现类代码如下:
package com.spring.service;

import org.springframework.stereotype.Service;

@Service("userService")
public class UserService implements I_UserService{

    @Override
    public boolean checkUser(String userName, String userPsw) {
        if (userName != null && userName.length() > 0 
                && userPsw != null && userPsw.length() > 0) {
            return true;
        }else {
            throw new IllegalArgumentException("账号密码不能有一个为空");  
        }
    }
    
}

index.jsp界面比较简单就不在贴代码了。


运行结果如下
正常情况

异常捕获输出

注意事项:
  • UseAspect.java 一定要是用注解@Component 否则spring将不会自动加载此类,直接将导致注入失败,增强代码一直不会执行;因为作为切面类需要Spring管理起来,所以在初始化时就需要将这个类初 始化加入Spring的管理;
  • ibatis-config.xml 需要加入 支持aspectj aop注解。

相关报错
严重: Allocate exception for servlet springmvc java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting ')' at character position 11 exection(* com.spring.service.I_UserService.checkUser(..))

Paste_Image.png


最后发现原因是execution 单词拼写错误,浪费了我好长时间才发现,细心是多么重要。

demo 下载 望多多star


相关引用

Spring AOP 实现原理与 CGLIB 应用
Spring实现AOP的4种方式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 本章内容: 面向切面编程的基本原理 通过POJO创建切面 使用@AspectJ注解 为AspectJ切面注入依赖 ...
    谢随安阅读 3,122评论 0 9
  • AOP实现可分为两类(按AOP框架修改源代码的时机): 静态AOP实现:AOP框架在编译阶段对程序进行修改,即实现...
    数独题阅读 2,294评论 0 22
  • 马上就要参加2017年执业/助理医师考试医学综合笔试了,有一部分同学轻信“包过、内部试题”等等的信息,但是中公医考...
    6d88a97a0fdc阅读 191评论 0 0
  • 我有一个哥哥,小时后梦想是嫁给哥哥 我有一个哥哥,哥哥有一个朋友 我们三个形影不离 后来,哥哥去了天堂 哥哥的朋友...
    31a2d21e1a3c阅读 234评论 0 1