小胖子学spring-aop

2018-06-18 02:42:58来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

最近小胖子把一份干了三年的工作辞掉了,满怀信心的去面试,结果大败而归...原来面试官看了他简历已有三年的工作经验,各种java基础写着扎实,ssh框架更是熟练掌握,于是就问了小胖子一个问题:说说你对spring-aop的理解。

小胖子大概思考了三秒,因为他只会使用层面,具体实现并不清楚。所以强作镇定的回答,spring-aop呢,aop主要是面向切面编程,是spring框架特色功能之一。简单说就是例如你要执行某一个方法,然后可以在这个方法执行前和执行后执行一些相关操作,例如打印些日志,事务的commitrollback等。那对于这aop是如何实现的呢?面试官接着问道,如果我不使用spring该怎么实现?小胖子只能如实回答了,答案并非如人所愿。最终又错失了一次晋升机会。

回到问题,如果抛弃了spring那该如何实现AOP?就是说只用code Java 实现一个AOP

小胖子于是狠下心好好学下这AOP到底是怎么实现的,通过了不懈努力终于得到了一些进展,终于也大概知道这AOP是怎么回事。原来spring是借助jdk proxyCGlib两种技术实现的。对于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安装