美文网首页
Spring 注解方式实现AOP

Spring 注解方式实现AOP

作者: 天堂宝宝_V | 来源:发表于2017-04-06 13:36 被阅读484次

    什么是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种方式

    相关文章

      网友评论

          本文标题:Spring 注解方式实现AOP

          本文链接:https://www.haomeiwen.com/subject/wuyxattx.html