小胖子学spring-aop
2018-06-18 02:42:58来源:未知 阅读 ()
最近小胖子把一份干了三年的工作辞掉了,满怀信心的去面试,结果大败而归...原来面试官看了他简历已有三年的工作经验,各种java基础写着扎实,ssh框架更是熟练掌握,于是就问了小胖子一个问题:说说你对spring-aop的理解。
小胖子大概思考了三秒,因为他只会使用层面,具体实现并不清楚。所以强作镇定的回答,spring-aop呢,aop主要是面向切面编程,是spring框架特色功能之一。简单说就是例如你要执行某一个方法,然后可以在这个方法执行前和执行后执行一些相关操作,例如打印些日志,事务的commit和rollback等。那对于这aop是如何实现的呢?面试官接着问道,如果我不使用spring该怎么实现?小胖子只能如实回答了,答案并非如人所愿。最终又错失了一次晋升机会。
回到问题,如果抛弃了spring那该如何实现AOP?就是说只用code Java 实现一个AOP。
小胖子于是狠下心好好学下这AOP到底是怎么实现的,通过了不懈努力终于得到了一些进展,终于也大概知道这AOP是怎么回事。原来spring是借助jdk proxy和CGlib两种技术实现的。对于jdk proxy 实现但得有个限制条件:必须要提供个接口和这个接口的实现类。对于CGlib这个就简单多了,只要实现methodinterceptor接口就可。那到底是如何实现的?小胖子回到家后二话不说,打开电脑先撸了一遍代码:
public interface TestDao { public void add(); } public class TestDaoImpl implements TestDao { @Override public void add() { System.out.println("TestDaoImpl开始执行..."); } } public class TestService { public void test() { TestDao dao = new TestDaoImpl(); System.out.println("调用testDao.add()方法前,执行些校验操作..."); dao.add(); System.out.println("调用testDao.add()方法后,执行些commit or rollback 操作..."); } }
很明显现在要做的就是在TestService里面dao.add()方法在调用前,先执行一些打印操作,和执行后也执行一些打印操作。小胖子二话不说又查起了资料,终于找到了一种设计模式叫:装饰器模式。装饰器模式主要就是实现接口并持有该接口的引用。于是小胖子把程序改进为:
public class LogDao implements TestDao { private TestDao dao ; public LogDao(TestDao testDao) { this.dao=testDao; } @Override public void add() { System.out.println("调用testDao.add()方法前,执行些校验操作..."); dao.add(); System.out.println("调用testDao.add()方法后,执行些commit or rollback 操作..."); } public static void main(String[] args) { LogDao dao = new LogDao(new TestDaoImpl()); dao.add(); } }
运行结果很明显:
调用testDao.add()方法前,执行些校验操作...
TestDaoImpl开始执行...
调用testDao.add()方法后,执行些commit or rollback 操作...
结果虽然是有点像aop了但还是不尽人意:很明显这打印的日志却不能重复利用。于是小胖子很快又找到了jdk 底层那个高大上的代理模式,主要是要实现一个叫InvocationHander的接口;于是把代码再改改:
public class TestInvocationHandler implements InvocationHandler { private Object object; public TestInvocationHandler(Object obj ) { this.object = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("add".equals(method.getName())) { System.out.println("调用testDao.add()方法前,执行些校验操作2..."); Object result = method.invoke(object, args); System.out.println("调用testDao.add()方法后,执行些commit or rollback 操作..."); return result; } return method.invoke(proxy, args); } }
这样打印的日志就可以被复用起来了,到现在aop就大概实现了,调用方法小胖子简单的写了个main:
public static void main(String[] args) { TestDao dao = new TestDaoImpl(); TestDao proxyDao = (TestDao) Proxy.newProxyInstance(TestInvocationHandler.class.getClassLoader(), new Class<?>[] {TestDao.class}, new TestInvocationHandler(dao)); proxyDao.add(); }
结果:
调用testDao.add()方法前,执行些校验操作2...
TestDaoImpl开始执行...
调用testDao.add()方法后,执行些commit or rollback 操作...
但很快小胖子又发现了一个问题,在TestInvocationHandler 里面添加了if("add".equals(method.getName())) 的判断,如果没加这个方法判断程序会一直死循环报错,所以如果要对其他方法调用前后也打印日志,也得在这里面添加判断。
还有一个问题就是jdk proxy动态代理只能针对接口来做代理,不能对类做代理,所以回到上文提到:对于jdk proxy 实现但得有个限制条件:必须要提供个接口和这个接口的实现类。
那对于CGlib那又是如何实现的呢?原来这个是针对类来实现的,只要实现了methodinterceptor接口就可以了。小胖子迫不及待上了maven库把CGlib包下了下来,导进项目再撸一把代码:
public class TestDaoProxy implements MethodInterceptor { @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { if("add".equals(arg1.getName())) { System.out.println("调用testDao.add()方法前,执行些校验操作..."); arg3.invokeSuper(arg0, arg2); System.out.println("调用testDao.add()方法后,执行些commit or rollback 操作..."); return arg0 ; } arg3.invokeSuper(arg0, arg2); return arg0; } } public static void main(String[] args) { TestDaoProxy daoProxy = new TestDaoProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TestDaoImpl.class); enhancer.setCallback(daoProxy); TestDao dao = (TestDaoImpl) enhancer.create(); dao.add(); }
发现报错了...
Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more
很明显这是缺少了相关jar包,原来是缺少了asm.jar,只要再把asm.jar引进来,结果就出来了。
但最终还是存在一个问题,无论是使用proxy还是CGlib都存在一个缺陷,代码之间的耦合还是没有解决;上面这个if("add".equals(method.getName())) 的判断还是存在,如果有新的方法也要相应添加新的判断...
小胖子又陷入了沉思,这样的话,面试能过吗,其实spring还用了一个叫AspectJ;看看时间,又要去面试啦,下回再研究...
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:JSTL_Format标记库
下一篇:docker安装
- Spring-AOP本质原理分析(七) 2019-08-16
- spring-AOP(面向切面编程)-注解方式配置 2018-10-24
- spring-AOP(面向切面编程)-xml方式配置 2018-10-24
- spring-AOP 2018-09-01
- Spring-AOP-增强 2018-06-27
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash