什么是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(..))