一.SpringAOP的概述。java
AOP(Aspect Oriented Programming),面向切面编程,经过预编译方式和运行期间动态代理实现程序的功能的统一维护的技术。AOP是OOP(面向对象编程)的扩展和延伸。举个例子,让你们对AOP印象更加深入点。编程
好比权限校验。实际开发中,咱们知道不是全部人均可以进行增删改查的操做,只有管理员才能够,因此咱们须要在进行增删改查以前都须要进行权限校验。传统纵向继承(面向对象的特征)就是定义一个BaseDao,里面含有一个权限校验的方法,让Dao直接继承ide
BaseDao,这样就能够直接调用Dao父类中的权限校验方法。而AOP则采用的是横向抽取机制替代了传统的纵向继承,也就是经过生成代理的方式来解决。这就是对方法进行扩展的两个方法的思想。性能
AOP应用:权限校验,日志记录,性能监控(运用代理,分别在代码先后插入记录时间),事务控制this
二.Spring底层实现AOP的原理。spa
底层用到了两种代理机制:代理
JDK动态代理:针对实现了接口的类的代理(java基础的代理)。日志
Cglib的动态代理:针对没有实现接口的类产生的代理,底层用的是字节码加强技术,经过生成当前类的子对象来产生代理。code
被代理类对象
public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("用户增长...."); } @Override public void remove() { System.out.println("用户移除...."); } }
JDK动态代理:
public class MyJDKProxy implements InvocationHandler { // 定义属性,传入实现类对象,也能够不用,可是后面就要用类名,而不是引用 private UserDaoImpl userDao; public MyJDKProxy(UserDaoImpl userDao) { this.userDao = userDao; } // 建立UserDao动态代理 public UserDao createUserProxy() { /* * 第一个参数。告诉虚拟机用哪一个字节码加载器加载内存建立字节码文件。 * 第二个参数。告诉虚拟机内存中建立的字节码文件中应该有哪一个方法(这些方法方法体为空)。获取类的接口(类的方法可能增长,但接口的方法是固定的) * 第三个参数。告诉虚拟机底层正在建立的字节码上的各个方法如何处理。 * */ UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(MyJDKProxy.class.getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equalsIgnoreCase("insert")) { System.out.println("权限校验"); } // 执行被代理类原有的方法 return method.invoke(userDao, args); } }
运行结构
权限校验
用户增长....
Cglib的父类:
public class CustDao { public void remove() { System.out.println("顾客移除"); } }
Cglib动态代理:
public class MyCglibProxy { public CustDao createProxy() { // 建立cglib和心类 Enhancer enhancer = new Enhancer(); // 设置父类 enhancer.setSuperclass(CustDao.class); // 设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("remove".equalsIgnoreCase(method.getName())) { // 执行被代理父类的方法 Object obj = methodProxy.invokeSuper(proxy, args); System.out.println("日志记录"); return obj; } return methodProxy.invokeSuper(proxy, args); } }); // 生成代理 CustDao custDao = (CustDao) enhancer.create(); return custDao; } }
运行结果:
权限校验
用户增长....
顾客移除
日志记录