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(); 
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
//      5 第五步执行
        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("添加登录成功日志功能 ..."); 
     } 

//   如果执行切点方法被抛出异常,则124步执行后执行@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(..))

这里写图片描述


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


demo 下载 望多多star


相关引用

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值