Spring的AOP
AOP Aspect Oriented Programming 面向切面编程
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP是OOP的扩展和延伸,是为了解决OOP开发遇到的问题
AOP底层实现原理
(只做了解即可,只需要知道如何通过配置生成代理对象)
动态代理
JDK动态代理 只能对实现了接口的类生成代理对象
Cglib动态代理(类似于javassist第三方代理技术[ hibernate中通过load方法获取对象,默认是使用javassist技术产生的代理对象 ]) 对没有实现接口的类产生代理对象,实质是生成子对象
使用Spring 的 AOP 技术,若使用的类实现了接口,则默认采用JDK动态代理,否则默认采用Cglib动态代理,底层会自动进行切换
JDK动态代理
//接口 public interface UserDao { public void save(); public void fimd(); public void delete(); public void update(); }
/** * 实现类 * @author SIHAI */ public class UserDaoImp implements UserDao { @Override public void save() { System.out.println("UserDaoImp-save..."); } @Override public void fimd() { System.out.println("UserDaoImp-find..."); } @Override public void delete() { System.out.println("UserDaoImp-delete..."); } @Override public void update() { System.out.println("UserDaoImp-update..."); } }
/** * 使用jdk动态代理产生代理对象 * @author SIHAI */ public class JDKProxy { //将要加强的类对象传进来 public UserDao getProxyUserDao(UserDao dao) { return (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断是不是save方法 if("save".equals(method.getName())) { //增强 System.out.println("this place is checked the data ..."); method.invoke(dao, args);//增强完继续运行原来的方法 } //如果不是,则直接运行这个方法,invoke中的参数表示 方法 是 dao的类型 中拥有 当前方法 所拥有的参数 args return method.invoke(dao, args); } }); } }
/** * 测试 * @author SIHAI */ public class Test1 { /** * 未进行动态代理 */ @Test public void train1() { UserDao dao = new UserDaoImp(); dao.save(); dao.fimd(); dao.update(); dao.delete(); } /** * 进行动态代理 */ @Test public void train2() { UserDao dao = new UserDaoImp(); UserDao daoProxy = new JDKProxy().getProxyUserDao(dao); daoProxy.save(); daoProxy.fimd(); daoProxy.update(); daoProxy.delete(); } }
实际开发中,一个项目会有许多许多的dao和dao的实现类,当需求发生改变,需要对原有功能进行加强时,传统的方式是纵向继承,但是这样会要修改每一个dao和他们的实现类,当需求再次改变时,又会是一个巨大的工作量,而Spring的AOP技术为我们解决了这个问题,采用横向抽取机制的方式,即动态代理
Cglib动态代理
Cglib 第三方开源代码生成类库,动态添加类的属性和方法
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成(现在不用这个了,现在用更强大的Javassist)
/** * * @author ysh * Customer.java * 2018年11月1日 下午12:28:05 * 被代理类,没有实现接口 */ public class Customer { public void save() { System.out.println("saving...."); } public void delete() { System.out.println("deleting...."); } public void update() { System.out.println("updating...."); } public void find() { System.out.println("finding...."); } }
/** * * @author ysh * CglibProxy.java * 2018年11月1日 下午12:29:14 * cglib动态代理生成代理类对象 */ public class CglibProxy { private Customer customer; public CglibProxy(Customer customer) { this.customer = customer; } public Customer createProxy() { //创建Cglib的核心类对象 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(customer.getClass()); //设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //查看是否为save方法 if("save".equals(method.getName())) { //增强,比如,在save方法运行前,检查客户权限 System.out.println("checking..."); //这里必须要用invokeSuper方法,不能用invoke方法,因为后者会出现死循环???? return methodProxy.invokeSuper(proxy, args); } //否则让原来的方法直接运行 /*proxy是被Customer的代理类对象,即为Customer的子类, invokeSuper方法,就是调用proxy的父类的当前方法, args用于精确找到要执行的方法,避免重载的影响*/ return methodProxy.invokeSuper(proxy, args); } }); return (Customer) enhancer.create(); } }
/** * * @author ysh * Test1.java * 2018年11月1日 下午12:30:22 * 测试类 */ public class Test1 { /** * 传统方式 */ @Test public void train1() { Customer customer = new Customer(); customer.save(); customer.delete(); customer.find(); customer.update(); } /** * cglib动态代理 */ @Test public void train2() { CglibProxy cglibProxy = new CglibProxy(new Customer()); Customer cus = cglibProxy.createProxy(); cus.save(); cus.delete(); cus.find(); cus.update(); } }
测试结果