如果你知道Struts2的拦截器那就很好理解AOP,拦截器就是应用AOP 的思想,拦截action进行预处理或结果处理,spring的AOP是一种更通用的拦截模式,可以拦截spring管理的任何bean。
AOP即解剖开封装的对象内部,将那些影响了多个类并且与具体业务无关的公共行为封装成一个独立的模块,该模块即为切面(由连接点/时间,切入点/地点,通知/故事三部分组成)。
AOP相关基本概念:
1:通知(Advice)
通知定义了在切入点执行时间点附近需要做的工作。Spring支持五种类型的通知:
Before(前)org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后)org.springframework.aop.AfterReturningAdvice
After-throwing(抛出后)org.springframework.aop.ThrowsAdvice
Arround(周围)org.aopaliance.intercept.MethodInterceptor
Introduction(引入)org.springframework.aop.IntroductionInterceptor
2:连接点
程序能够应用通知的一个"时机",这些时机就是连接点,例如方法调用时,异常抛出时,方法返回后等等。
3:切入点
通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了故事发生的地点,例如某个类或方法的名称,spring允许用正则表达式来指定。
连接点比切入点更细致。
4:切面(在程序中,切面中连接点和切入点简单声明即可,因此切面类不包括前两者,切面类只是通知的封装,@Aspect修饰的类表示切面类。)
前三者共同组成了切面
5:引入
引入可以使现有类增加新的方法和属性(spring提供了一个方法注入的功能)
6:目标
即被通知的对象
7:代理
应用通知的对象(详见动态代理模式)
8:织入
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在:
1》类加载时:使用特殊的ClassLoader在目标类被加载到JVM前增强类的字节代码。
2》运行时:切面在某个运行的时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术。
http://blog.csdn.net/zhangliangzi/article/details/52334964例子
AOP的三种底层原理:【实现方式只有基于注解和xml配置两种方式,下面的三种方式是AOP的底层结构不是实现方式】
静态AOP:在编译期切面以字节码形式编译到目标字节码文件中。(AspectJ)
动态AOP:http://blog.csdn.net/dreamrealised/article/details/12885739
1.JDK动态代理——在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。(SpringAOP使用的JDK动态代理)
为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- 通知/目标方法被拦截前后的具体操作内容 -->
<bean id="helper" class="aop.Helper"></bean>
<!-- 定义切点(事件发生地点) -->
<bean id="cutPoint" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*targetMethod"></property>
</bean>
<!-- 关联切点和通知 -->
<bean id="advisorWithCutpoint" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="cutPoint"></property>
<property name="advice" ref="helper"></property>
</bean>
<!-- 配置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"></property>
<property name="interceptorNames" value="advisorWithCutpoint"></property>
<property name="proxyInterfaces" value="aop.TargetInterface"></property>
</bean>
<!-- 目标类/被拦截类 -->
<bean id="target" class="aop.Target">
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext appCtx = new ClassPathXmlApplicationContext("aop/aop.xml");
TargetInterface me = (TargetInterface)appCtx.getBean("proxy");
me.targetMethod();
}
2.CGLib动态代理字节生成——封装字节码生成工具Asm,运行期间目标字节码加载后,生成目标类的子类,将切面逻辑加入到子类中,所以CGLib实现AOP不需要基于接口。CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)
package aop.cglibAOP;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyCGlib implements MethodInterceptor{
private Object obj;
public Object createProxy(TargetCGlib obj){
//把传进来的代理对象赋值给obj
//this.obj=obj;
Enhancer enhancer = new Enhancer();
//需要为其实例指定一个父类,也就是我们 的目标对象,那么我们新创建出来的对象就是目标对象的子类,有目标对象的一样
enhancer.setSuperclass(TargetCGlib.class);
//除此之外,还要指定一个回调函数,这个函数就和Proxy的 invoke()类似
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
// 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
System.out.println("before-------------");
// 执行目标类add方法
arg3.invokeSuper(arg0, arg2);
// 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
System.out.println("after--------------");
return null;
}
}
public static void main(String[] args) {
ProxyCGlib proxy = new ProxyCGlib();
// base为生成的增强过的目标类
TargetCGlib base = (TargetCGlib) proxy.createProxy(new TargetCGlib());
base.targetMethod();
}
那么如何选择的使用代理机制了?
通过配置Spring的中<aop:config>标签来显示的指定使用动态代理机制 proxy-target-class=true表示使用CGLib代理,如果为false就是默认使用JDK动态代理
JDK动态代理和CGLIB的区别:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类,而CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法不能声明成final。
如何强制使用CGLIB实现AOP?
* 添加CGLIB库,SPRING_HOME/cglib/*.jar
* 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
AspectJ默认不用实现接口,但对于目标对象,默认情况下必须实现接口,如果没有实现接口必须引入CGLIB库。
可以通过advice中添加一个joinpoint参数,这个值会由spring自动传入,从joinpoint中可以获取参数值,方法名等。
AspectJ使用注解方式来代替配置xml:
1.如果目标对象实现了接口,默认情况会采用JDK的动态代理实现AOP。也可以强制使用CGLIB实现AOP。
2.如果目标对象没有实现接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。