1.AOP概述
AOP是Aspect-Oriented Programming 面向切面编程的简称
在Spring AOP的实现中,使用的是java本身的语言特性,如java proxy代理类,拦截器技术来实现AOP编织,AOP与IoC结合使用,为Spring本身和应用程序开发提供了很好的帮助
spring AOP中封装AspectJ 这个一优秀的AOP解决方案
下面我们将对aop的原理进行分析
1.Advice通知
通知类型有5种,
Before
After
After-return
After-throwing
throwing
Advice通知定义在连接点做什么,为切面增强提供植入接口
有BeforeAdvice,AfterAdvice和ThrowsAdvice
BeforeAdvice的常用接口MethodBeforeAdvice前置增强接口,使用这个接口需要实现一个方法,
public void before(Method method, Object[] args, Object target)
AfterAdvice常用接口AfterReturningAdvice也需要实现一个方法
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
Object[] args, Object target) throws Throwable {
ThrowsAdvice不需要实现方法
public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {
//实现方法前置通知MethodBeforeAdvice接口的方法
public void before(Method m, Object[] args, Object target) throws Throwable {
//以目标对象方法作为参数,调用父类MethodCounter的count方法统计方法调用次数
count(m);
}
}
CountingBeforeAdvice的父类MethodCounter的源码如下:
public class MethodCounter implements Serializable {
//方法名—>方法调用次数的map集合,存储方法的调用次数
private HashMap<String, Integer> map = new HashMap<String, Integer>();
//所有方法的总调用次数
private int allCount;
//统计方法调用次数,CountingBeforeAdvice的调用入口
protected void count(Method m) {
count(m.getName());
}
//统计指定名称方法的调用次数
protected void count(String methodName) {
//从方法名—>方法调用次数集合中获取指定名称方法的调用次数
Integer i = map.get(methodName);
//如果调用次数不为null,则将调用次数加1,如果调用次数为null,则设置调用次数为1
i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);
//重新设置集合中保存的方法调用次数
map.put(methodName, i);
//所有方法总调用次数加1
++allCount;
}
//获取指定名称方法的调用次数
public int getCalls(String methodName) {
Integer i = map.get(methodName);
return (i != null ? i.intValue() : 0);
}
//获取所有方法的总调用次数
public int getCalls() {
return allCount;
}
public boolean equals(Object other) {
return (other != null && other.getClass() == this.getClass());
}
public int hashCode() {
return getClass().hashCode();
}
2.PointCut切点
切点决定Advice通知应该作用于那个连接点,通过PointCut来定义需要增强的方法集合
以正则表达式切点JdkRegexpMethodPointcut为例,来了解PointCut的工作原理
在matches(String pattern, int patternIndex);这个方法中实现了用正则表达式进行解析的过程
public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {
//要编译的正则表达式模式
private Pattern[] compiledPatterns = new Pattern[0];
//编译时要排除的正则表达式模式
private Pattern[] compiledExclusionPatterns = new Pattern[0];
//将给定的模式字符串数组初始化为编译的正则表达式模式
protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {
this.compiledPatterns = compilePatterns(patterns);
}
//将给定的模式字符串数组初始化为编译时要排除的正则表达式模式
protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {
this.compiledExclusionPatterns = compilePatterns(excludedPatterns);
}
//使用正则表达式匹配给定的名称
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
//使用要排除的正则表达式匹配给定的名称
protected boolean matchesExclusion(String candidate, int patternIndex) {
Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);
return matcher.matches();
}
//将给定的字符串数组编译为正则表达的模式
private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {
Pattern[] destination = new Pattern[source.length];
for (int i = 0; i < source.length; i++) {
destination[i] = Pattern.compile(source[i]);
}
return destination;
}
}
3.Advisor通知器
完成advice和PointCut后,需要一个对象将他们整合起来,这个就是通知器
通知器的实现在DefaultPointCutAdvisor中.
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
//默认切入点
//Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;
private Pointcut pointcut = Pointcut.TRUE;
//无参构造方法,创建一个空的通知器
public DefaultPointcutAdvisor() {
}
//创建一个匹配所有方法的通知器
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
//创建一个指定切入点和通知的通知器
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
//为通知设置切入点
public void setPointcut(Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
//获取切入点
public Pointcut getPointcut() {
return this.pointcut;
}
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}
class TruePointcut implements Pointcut, Serializable {
//INSTANCE是TruePointcut类的一个常量单件,即整个应用中只有这个一个,
//不会创建第二个实例对象,确保该实例对象的唯一性,单态模型
public static final TruePointcut INSTANCE = new TruePointcut();
//单态模式构造方法
private TruePointcut() {
}
//获取切入点的类过滤器
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
//获取切入点的方法匹配器
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
//获取单态模式对象的方法
private Object readResolve() {
return INSTANCE;
}
public String toString() {
return "Pointcut.TRUE";
}
}
2.Aop设计与实现
1.AOP中核心技术是动态代理
目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现
Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现
动态代理实例代码
package day20150810;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyDemo {
public static void main(String[] args) {
Demo demo = new Demo();
demo.run();
((Face) Proxy.newProxyInstance(Demo.class.getClassLoader(),
Demo.class.getInterfaces(), new ProxyTest(demo))).run();
}
}
interface Face{
void run();
}
class Demo implements Face{
public void run(){
System.out.println("Run~~");
}
}
class ProxyTest implements InvocationHandler{
private Object object;
public ProxyTest(Object object) {
super();
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Proxy~~");
System.out.println("Method name:"+method.getName());
Object result = null;
//调用之前
System.out.println("Before~~~~");
//调用原始对象的方法
result=method.invoke(object, args);
//调用之后
System.out.println("After~~~~");
return result;
}
}