AOP底层原理
这里写目录标题
什么是AOP?
- AOP—Aspect Oriented Programming 面向切面编程。
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)。
- Spring AOP使用后纯Java实现,不需要专门的编译工程和类加载器,在运行期间通过代理方式向目标类组织增强代码。
- AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,SpringAOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时期提供横向代码的组织。
AOP底层原理
AOP就是代理机制,动态代理:(JDK 中使用) JDK动态代理,对实现了接口的类生成代理。
Spring中的AOP代理:一种是JDK动态代理,另一种就是CGLib代理机制(对类生成代理)。
AOP相关术语:
- Joinpoint(连接点): 所谓的连接点是指那些可以被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。即连接点指的是那些发可以被拦截。
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截。
- Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知,即增强即切面要完成的功能(它是方法级别的增强)。
- Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期间为类动态的添加一些方法或Field(变量)。它是类级别的增强,在原有类上添加一个属性或方法。
- Target(目标对象): 代理的目标对象,即被增强的对象。
- Weaving(植入): 是把增强应用到目标对象来创建新的代理对象的过程,Spring采用动态代理植入,而AspectJ采用编译期植入和类的装载期植入。
- Proxy(代理): 一个类被AOP植入增强后,就产生一个结果代理类。
- Aspect(切面): 是切入点和通知(引介)的结合。允许有多个切点和通知组合。
AOP的底层实现
JDK1.3引入动态代理技术,编写动态代理程序(java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler),如图所示:
使用JDK生成动态代理
UserDao
/**
* DAO的接口
*
*/
public interface UserDao {
public void add();
public void update();
}
UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
}
JDKProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK的动态代理机制
*
* @author liuxun
*
*/
public class JDKProxy implements InvocationHandler {
private UserDao userDao;
public JDKProxy(UserDao userDao) {
super();
this.userDao = userDao;
}
public UserDao createProxy() {
UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return proxy;
}
@Override
// 调用目标对象的任何一个方法都相当于invoke()
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("add".equals(method.getName())) {
// 记录日志
System.out.println("日志记录===================");
Object result = method.invoke(userDao, args);
return result;
}
return method.invoke(userDao, args);
}
}
SpringTest1
import org.junit.Test;
public class SpringTest1 {
@Test
public void demo1() {
UserDao userDao = new UserDaoImpl();
userDao.add();
userDao.update();
}
@Test
public void demo2() {
// 被代理对象
UserDao userDao = new UserDaoImpl();
// 创建代理对象的时候传入被代理对象
UserDao proxy = new JDKProxy(userDao).createProxy();
proxy.add();
proxy.update();
}
}
运行结果
使用CGLIB生成代理
对于不使用接口的业务类,无法使用JDK动态代理。可以采用cglib技术,cglib采用非常底层的字节码技术,可以为一个类创建子类,解决无接口代理问题。
CGLIB(code Genaration Library)是一个强大的、高质量的、高性能的Code生成类库,它可以在运行期扩展Java类与实现接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成,Hibernate生成持久化类的Javaassist。其实CGLIB生成代理机制的实质就是生成了一个真实对象的子类。
如果要使用CGLIB技术生成代理,需要下载CGLIB的jar包,但是在Spring的核心包Core中已经集成了CGLIB,可以不用直接引用CGLIB的包。
具体代码如下所示:
ProductDao
public class ProductDao {
public void add() {
System.out.println("添加商品...");
}
public void update() {
System.out.println("修改商品...");
}
}
CGLibProxy
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* 使用CGLib生成代理对象
*
*/
public class CGLibProxy implements MethodInterceptor {
private ProductDao productDao;
public CGLibProxy(ProductDao productDao) {
super();
this.productDao = productDao;
}
public ProductDao createProxy() {
// 使用CGLIB生成代理
// 1.创建核心类
Enhancer enhancer = new Enhancer();
// 2.为其设置父类
enhancer.setSuperclass(productDao.getClass());
// 3.设置回调
enhancer.setCallback(this);
// 4.创建代理
return (ProductDao) enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("add".equals(method.getName())) {
System.out.println("日志记录=================");
// Object obj=method.invoke(productDao, args);
Object obj = methodProxy.invokeSuper(proxy, args);// 代理方法中执行代理对象父类中的方法
return obj;
}
return methodProxy.invokeSuper(proxy, args);
}
}
SpringTest2
import org.junit.Test;
public class SpringTest2 {
@Test
public void demo1() {
ProductDao productDao = new ProductDao();
productDao.add();
productDao.update();
}
@Test
public void demo2() {
ProductDao productDao = new ProductDao();
ProductDao proxy=new CGLibProxy(productDao).createProxy();
proxy.add();
proxy.update();
}
}
运行结果
关于CGLIB生成代理时实现的intercept拦截方法参数说明如下:
- @param obj CGlib根据指定父类生成的代理对象
- @param method 拦截的方法
- @param args 拦截方法的参数数组
- @param proxy 方法的代理对象,用于执行父类的方法
注意:最新版本的Spring已经将CGLib开发类引入spring-core-3.2.0.RELEASE.ja
代理知识总结
Spring在运行期间,生成动态代理对象,不需要特殊的编译器。
Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向植入。选择方式如下:
- 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
- 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
程序中应当优先对接口创建代理,便于程序解耦合进行维护。
标记为final的方法,不能被代理,因为无法进行覆盖,原因如下:
- JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰。
- CGLib是针对目标类生成子类,因此类或方法不能使用final进行修饰。
Spring只支持方法连接点,不提供属性连接。
Spring中的AOP
Spring传统的AOP(增强类型)
AOP不是由spring定义,是由AOP联盟定义的。
AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
- 前置通知 org.springframework.aop.MethodBeforeAdvice 在目标方法执行前进行增强。
- 后置通知 org.springframework.aop.AfterReturningAdvice 在目标方法执行成功后实施增强。(After在方法执行前后都执行)
- 环绕通知 org.aopalliance.intercept.MethodInterceptor 在目标方法执行前后实施增强。
- 异常抛