Spring-AOP
1 . AOP的概念:
AOP(Aspect Oriented Programming),即面向切面编程,它是对OOP(Object Oriented Programming)的补充和完善.OOP引入封装、继承和多态等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
2 . AOP相关的技术术语
- Join point :连接点,程序执行的某个位置,例如类初始化前,类的方法抛出异常后等。spring仅支持方法的连接点。
- Pointcut :切点,指定一个通知将被引发的一系列连接点的集合。类比数据库的查询来说,连接点相当与数据库中的记录,切点就相当月查询条件。
- Advice :通知,也叫增强,切面在某个连接点执行的操作(sprig中提供了五种通知:前置通知,后置通知,环绕通知,异常通知,引入通知 );
- Aspect :切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面;
- Target : 目标对象,增强逻辑的目标织入类
- Introduction : 引入,添加方法或字段到被增强的类。 Spring允许引入新的接口到任何被增强的对象。
- Weaving :织入,将增强添加到目标类具体连接点上。根据不同的实现技术,AOP有三种织入方式:
(1) 编译期织入,需特别的java编译器
(2) 类装载期织入,需特别的的ClassLoader
(3) 动态代理织入,在运行期为目标类添加增加生成子类的方式
3 . Sping AOP的原理:
- Sping AOP的底层是通过JDK动态代理和CGLib动态代理技术为目标对象织入横切逻辑。
(1) JDK动态代理:目标类必须实现接口,代理类通过实现InvocationHandler接口完成对目标类的逻辑增强。
(2)CGLib动态代理:采用底层字节码技术,为目标类创建子类,并在子类中采用方法拦截的技术拦截目标类所有方法的执行,并顺势织入横切逻辑。 - CGLIib所创建的动态代理对象的性能比JDK创建的代理对象的性能高很多(大概10倍),但CGLib在创建动态代理对象时所花费的时间比JDK动态代理多(大概8倍),所以对于singleton的代理对象或者具有实例池的代理,因为不需要频繁的创建对象,CGLib比较适合,反之JDK动态代理比较适合。需要注意的是由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final方法进行代理。
4 . 代码实现
假设现有一段逻辑如下:
1 public class UserServiceImpl { 2 public void getUserInfo() { 3 checkLogin();//权限检验代码,检查用户是否登录,这段代码在每个方法之前都需要执行 4 System.out.println("show user's infomation");//真正的业务逻辑代码 5 } 6 }
现在分别用两种AOP方式实现以上代码:
(1) 使用JDK 动态代理
首先需要一个UserService的接口
public interface UserService { public void getUserInfo(); }
然后一个UserService的实现类UserServiceImpl
public class UserServiceImpl implements UserService{ @Override public void getUserInfo() { System.out.println("show user's infomation"); } }
一个代理类UserServiceProxy
public class UserServiceProxy implements InvocationHandler { private Object targetObject; public UserServiceProxy(Object targetObject) { this.targetObject = targetObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { UserServiceImpl userService = (UserServiceImpl) targetObject; Object result = null; // 权限判断 if (checkLogin() == true)) { result = method.invoke(targetObject, args); }else{ throw new RuntimeException("you are not login,please login first"); } return result; } }
使用生成的代理类:
public static void main(String[] args) { UserServiceImpl targetObject = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(targetObject); // 生成代理对象 UserService object = (UserService) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), proxy); object.getUserInfo(); }
(2) 使用CGLib实现
生成代理类
import java.lang.reflect.Method; import net.sf.cglib.proxy.reflect.Enhancer; import net.sf.cglib.proxy.reflect.MethodInteceptor; import net.sf.cglib.proxy.reflect.MethodProxy; public class CGLibProxy implements MethodInteceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ enhancer.setSuperClass(clazz);//根据父类clazz创建代理对象 enhancer.setCallBack(this); return enhancer.create();//动态创建子类实例 } // 拦截父类所有方法的调用 public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{ Object result = null; if (checkLogin==true)) { result = proxy.invokeSuper(obj, args);// 通过代理类调用父类中的方法 }else{ throw new RuntimeException("you are not login,please login first"); } return result; } }
使用生成的代理类
public static void main(String[] args) { CGLibProxy proxy = new CGLibProxy(); UserServiceImpl userService = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class); userService.getUserInfo(); }