AOP
面向切面编程
AspectJ
Spring AOP Design
AOP面向切面编程是通过代理对象来实现的,和拦截器是不是有点像,最主要的区别就是PointCut,来看看接口设计:
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
}
Spring 整合AspectJ,使用了AspectJ对切面的定义,同时支持自己的xml space定义切面等信息,AspectJ还是支持weaving(compiling,loading)阶段。
Spring的整合方式是提供spring自己的weaving方式,动态代理,运行时刻,并协同spring ioc进行工作,满足绝大多数企业应用场景。 当然spring也支持独立使用AspectJ,使用原生的weaving方式。
spring的设计哲学,包容并可扩展。 spring aop
Spring AOP Proxy
Spring AOP 的实现方式是为目标对象生成代理对象。 Spring AOP 框架对 AOP 代理类的处理原则是:如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心,CGLIB 底层使用ASM。
Proxy CGLIB
// 拦截器类
public class AroundAdvice implements MethodInterceptor
{
public Object intercept(Object target, Method method , Object[] args, MethodProxy proxy)
throws java.lang.Throwable
{
System.out.println("执行目标方法之前,模拟开始事务 ...");
// 执行目标方法,并保存目标方法执行后的返回值
Object rvt = proxy.invokeSuper(target, new String[]{"被改变的参数"});
System.out.println("执行目标方法之后,模拟结束事务 ...");
return rvt + " 新增的内容";
}
}
//接下来程序提供一个 ChineseProxyFactory 类,这个 ChineseProxyFactory 类会通过 CGLIB 来为 Chinese 生成代理类
public class ChineseProxyFactory
{
public static Chinese getAuthInstance()
{
Enhancer en = new Enhancer();
// 设置要代理的目标类
en.setSuperclass(Chinese.class);
// 设置要代理的拦截器
en.setCallback(new AroundAdvice());
// 生成代理类的实例
return (Chinese)en.create();
}
}
Proxy Jdk
Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理,如果目标类实现了接口,Spring AOP 则无需 CGLIB 的支持,直接使用 JDK 提供的 Proxy 和 InvocationHandler 来生成 AOP 代理即可
//Proxy 编程是面向接口的。下面我们会看到,Proxy 并不负责实例化对象,和 Decorator 模式一样,要把 Account定义成一个接口,然后在 AccountImpl里实现 Account接口,接着实现一个 InvocationHandlerAccount方法被调用的时候,虚拟机都会实际调用这个 InvocationHandler的 invoke方法
class SecurityProxyInvocationHandler implements InvocationHandler {
private Object proxyedObject;
public SecurityProxyInvocationHandler(Object o) {
proxyedObject = o;
}
public Object invoke(Object object, Method method, Object[] arguments)
throws Throwable {
if (object instanceof Account && method.getName().equals("opertaion")) {
SecurityChecker.checkSecurity();
}
return method.invoke(proxyedObject, arguments);
}
}
//在应用程序中指定 InvocationHandler生成代理对象
public static void main(String[] args) {
Account account = (Account) Proxy.newProxyInstance(
Account.class.getClassLoader(),
new Class[] { Account.class },
new SecurityProxyInvocationHandler(new AccountImpl())
);
account.function();
}
关于Spring Aop 的具体实现,这里有源码解析spring aop 源码解析
面试的时候常会被问及的一个问题,如果一个类里面的两个方法都被增强了,method1 调用 method2 的时候,method2 会被增强吗? 其实这个问题很简单,AOP只是在使用proxy类对method1做了增强,然后会调用target的method1方法,然后target.method1 invoke target.method2. 所以method2是没有被增强的。
为什么private方法不能使用AOP?
因为两种实现方式都不能代理private方法,cglib通过继承父类的方式,不能访问父类private方法。
Spring Transaction
我们知道Spring 的@Transaction 是通过切面实现的,然后在事务嵌套过程中会有上面我们提到的类似的问题,那事务里面是怎么解决的呢。
通过属性propagation 来控制事务的传播,所以需要理解这个才不会迷惑.
参考
https://www.ibm.com/developerworks/cn/java/j-lo-asm30/
https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html