一、准备:
- 引入spring 4.0版本基础jar包
- 使用aop需额外引入aspectj支持及cglib代理支持
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
</dependency>
3、在spring配置文件中添加对aop注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
">
<!-- 启动@aspectj的自动代理支持-->
<aop:aspectj-autoproxy />
</beans>
上述非完整配置文件,若aop标签报错,请检查scheme中是否包含Aop支持
二、编码
1、编写一个java bean,配置成spring bean
import org.springframework.stereotype.Component;
import cn.com.guo.annotation.AopJoinPointTest;
@Component
public class Kingnight {
private String name;
private String age;
public Kingnight() {
this.age = "88";
this.name = "king";
}
public void sayHello(){
System.out.println("My name is " + name + "!");
System.out.println("My age is " + age + "!");
}
@AopJoinPointTest(name="Kingnight:say good bye")
public void sayGoodbye(){
System.out.println("Nice to meeting you! Good bye!");
}
}
2、切面编写
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class KingnightAopTest {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(* cn.com.guo.beans.Kingnight.sayHello())")
public void kingnightJoinPoint(){
}
@After(value="kingnightJoinPoint()")
public void doAfter(){
logger.info("KingnightAopTest============>>");
}
@Before("within(@cn.com.guo.annotation.AopJoinPointTest *)")
public void doBefore(){
System.out.println("Fight well and often!s");
}
}
@Pointcut定义横切关注点,在增强实现(advice)中可引入其定义的关注点,以将advice以给定方式织入对应连接点中。
@After、@Before为增强实现,可通过:1>@Pointcut定义的切点;2>AspectJ指示器 找到对应的切点,并将方法织入其中。
上例中within表达式指定使用@AopJoinPointTest 自定义注解的方法,自定义注解代码:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopJoinPointTest {
/**
* 引入说明
* @return
*/
String name() default "";
}
3、调用(我在spring Controller中调用,未编写Junit测试):
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.com.guo.beans.Kingnight;
/**
* 测试spring配置
* @author GuoZhinan
*/
@Controller
@ResponseBody
public class BaseController {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private Kingnight kingnight;
@RequestMapping("/*.do")
public void doTest(HttpServletRequest req, HttpServletResponse resp){
logger.info("--start--");
kingnight.sayHello();
logger.info("--the work is over--");
kingnight.sayGoodbye();
}
}
测试结果:
14:00:05.976 [http-8080-2] INFO cn.com.guo.controller.BaseController - --start--
My name is king!
My age is 88!
14:00:06.251 [http-8080-2] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'kingnightAopTest'
14:00:06.252 [http-8080-2] INFO cn.com.guo.aop.KingnightAopTest - KingnightAopTest============>>
14:00:06.252 [http-8080-2] INFO cn.com.guo.controller.BaseController - --the work is over--
Nice to meeting you! Good bye!
三、Api整理
1、@Pointcut 定义横切关注点
(1) 类型匹配语法:
*:匹配任何数量字符;
..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
java.lang.String 匹配String类型
java.*.String 匹配java包下的任何“一级子包”下的String类型,如匹配java.lang.String,但不匹配java.lang.XXX.String
java..* 匹配java包及任何子包下的任何类型, 如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型
java.lang.Number+ 匹配java.lang包下的任何Number的自类型,如匹配java.lang.Integer,也匹配java.math.BigInteger
注意:使用 且(&&)、或(||)、非(!)来组合切入点表达式
(2) Spring兼容的AspectJ指示器:
AspectJ指示器 | 描述 |
---|---|
args() | 限定连接点匹配参数为指定类型的执行方法 |
@args() | 限定连接点匹配参数由指定注解标注的执行方法 |
execution() | 用于匹配连接点执行的方法 |
within() | 限定匹配连接点指定的类型 |
@within() | 限定匹配连接点指定注解所标注的类型(当使用Spring AOP时,方法定义在指定的注解所标注的类里) |
@annotation | 限定匹配带有特定注解的连接点 |
target() | 限定连接点匹配目标对象为指定类型的类 |
this() | 限定连接点匹配AOP代理的类型bean引用为指定类型的类 |
以上是spring Aop兼容的AspectJ指示器(若有错漏,还请指正),若要使用AspectJ其它指示器,需使用spring集成AspectJ。
2、增强实现定义
Advice类型 | 定义 |
@Before | 前置通知:在切入点运行前执行,不会影响切入点的逻辑 |
@After | 后置通知:在切入点正常运行结束后执行,如果切入点抛出异常,则在抛出异常前执行 |
@AfterThrowing | 异常通知:在切入点抛出异常前执行,如果切入点正常运行(未抛出异常),则不执行 |
@AfterReturning | 返回通知:在切入点正常运行结束后执行,如果切入点抛出异常,则不执行 |
@Around | 环绕通知:在切入点执行前后自定义一些操作。需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行 |
(1)JoinPoint
用于获取连接点的相关信息
import org.aspectj.lang.reflect.SourceLocation;
public interface JoinPoint {
String toString(); //连接点所在位置的相关信息
String toShortString(); //连接点所在位置的简短相关信息
String toLongString(); //连接点所在位置的全部相关信息
Object getThis(); //返回AOP代理对象
Object getTarget(); //返回目标对象
Object[] getArgs(); //返回被通知方法参数列表
Signature getSignature(); //返回当前连接点签名
SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置
String getKind(); //连接点类型
StaticPart getStaticPart(); //返回连接点静态部分
}
(2)ProceedingJoinPoint
用于在环绕通知中执行被通知方法(下为接口源码):
package org.aspectj.lang;
import org.aspectj.runtime.internal.AroundClosure;
public interface ProceedingJoinPoint extends JoinPoint {
void set$AroundClosure(AroundClosure arc);
public Object proceed() throws Throwable;
public Object proceed(Object[] args) throws Throwable;
}
(3)在advice中获取连接点方法传入参数
可使用args指示器将参数自动传递给方法:
@Before(value="execution(* test(*)) && args(param)", argNames="param")
public void doBefore(String param)