面向切面在英文中的单词是Aspect Oriented Programming(AOP),在spring框架中叫aop,它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。
AOP技术是建立在Java语言的反射机制与动态代理机制之上的。请看下图

以上图是利用jdk动态代理为真实对象上的每个方法上加上日志功能。从这张图中可以看出,我们是面向真实对象上的方法编程,把方法切开,在执行方法之前做了日志功能。但是在实际开发中,我们不会把日志代码写死在程序里面,我们会把切面的代码用对象的形式进行封装,以对象的形式传递参数,然后执行对象,就是执行了切面的代码,这就是在面向切面编程。
在实际开发中,这种面向切面编程通常用在做系统的日志记录,权限管理和,事物管理等功能,这样做的好处是让j2ee程序员在编写具体的业务逻辑出来方法时,只需关心具体业务逻辑的实现,既提高了工作效率,又使代码变得简洁优雅。
下面我以这样的一个例子讲解:在系统的执行过程中,要求在用户每次调用service层的每个方法时,在控制台打印出调用的方法名称以及调用该方法的次数,代码实现如下:
package cn.lmj.aop; //service层接口 public interface UserService { public void run(); public void eat(); } package cn.lmj.aop; //service层接口实现类 public class UserServiceImpl implements UserService { public void run() { System.out.println("run............."); } public void eat() { System.out.println("eat............"); } } package cn.lmj.aop; import java.lang.reflect.Method; //利用接口定义一种契约 public interface Advice { void beforeMethod(Method m); } package cn.lmj.aop; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; //把切面的代码封装成为对象 public class MyAdvice implements Advice { private Map map = new HashMap (); public void beforeMethod(Method m) { if(map.containsKey(m.getName())) { Integer i = map.get(m.getName()); i++; System.out.println(m.getName()+"被执行了"+(i)+"次"); map.put(m.getName(),i); } else { map.put(m.getName(), 1); System.out.println(m.getName()+"被执行了1次"); } } } package cn.lmj.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class BeanFactory { //接受目标和建议,产生任意类(只要该类有接口)的代理类,拦截所有的方法访问 public static Object getPerson(final Object obj,final Advice advice) { Object proxy = Proxy.newProxyInstance(BeanFactory.class.getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object arg0, Method m, Object[] arg2) throws Throwable { advice.beforeMethod(m); Object value = m.invoke(obj, arg2); return value; } }); return proxy; } } package cn.lmj.aop; public class Demo { public static void main(String[] args) { //必须保证MyAdvice的实例全局只有一份 MyAdvice advice = new MyAdvice(); UserService c = (UserService) BeanFactory.getPerson(new UserServiceImpl(),advice); c.eat(); c.eat(); c.eat(); c.eat(); c.run(); c.run(); c.run(); //执行结果: /* eat被执行了1次 eat............ eat被执行了2次 eat............ eat被执行了3次 eat............ eat被执行了4次 eat............ run被执行了1次 run............. run被执行了2次 run............. run被执行了3次 run............. */ } }
上面的代码关键之处是BeanFactory的getPerson()方法的实现,接受任意的类(只要该类有接口实现),并把切面的代码以对象的方式进行传递,这样做能够使得我们编程人员更加关心具体业务代码的实现,我们只需获取Service层对象的时候从工厂中获得代理对象,这个对象就比原来的对象功能更强大。
我们做系统在使用了spring框架之后会发现这样一种场景,我们写好了一个对象,然后往spring容器里一放,然后再取出来,结果这个对象就比以前强大很多,比如这个对象自动拥有了事物管理功能,日志记录功能等,它是怎么做到的?其实和我上面写的原理上差不多,只要上面的例子懂,spring的aop再无秘密可言。但是有一定要注意,在实际开发中我们很多对象是服务器给我们的,如果这个对象没有实现接口,那它怎么产生代理类?spring用到了CGLIB这套api,它是基于子类的方式产生代理,这里不做描述。还有一点,在开发中,如果一个类即没有实现接口,又是final的,那么它就不能产生代理类。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/226679.html原文链接:https://javaforall.net
