锁定老帖子 主题:Spring AOP之代理机制
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-10-26
很早就听说spring aop,一直没有静下来学习,今天看了下《精通spring》,将我理解的spring aop做下笔记。当然,首先还是什么是AOP呢?AOP(Aspect Oriented Programming)面向方面编程,但对于spring来说理解为“面向方面编程”更确切。那么又是为什么要用AOP呢?大家都知道OOP(Oriented Object Programming)面向对象编程,主要特点是封装,继承,多态。这里主要想说一下继承,如果多个类具有相同的属性或方法,我们则考虑定义一个父类。然后让其子类继承。比如:定义一个Animal具有run(),eat()两个抽象方法的父类,然后让rabbit,horse继承Animal类重写run(), eat(),通过引用父类来消除重复代码。说了这么多就是为了引入spring aop,下面来看一段大家都属性的代码: package com.me.services.imp; import com.me.entity.User; import com.me.services.UserService; import com.me.daos.UserDAO; import com.me.util.TransationManager; import com.me.util.PerformanceMonitor; public class UserServiceImp implements UserService { private TransationManager trans = new TransationManager(); private PerformanceMonitor pmonitor = new PerformanceMonitor(); private UserDAO userDao = new UserDAO(); public void deleteUser(int userId) { pmonitor.start("com.me.services.imp.UserServiceImp.deleteUser");//1 trans.beginTransation();//2 userDao.deleteUser(userId); trans.commitTransation(); pmonitor.end(); } public void insertUser(User user) { pmonitor.start("com.me.services.imp.UserServiceImp.insertUser");//1 trans.beginTransation();//2 userDao.inserUser(user); trans.commitTransation(); pmonitor.end(); } } 这段代码就是用于监控添加用户和删除用户的性能,除了业务逻辑外,大量的事务处理和性能监控重复代码(1、2代码), 那能不能也抽象一个父类,当然不行,因为继承是纵向抽取机制,这里就是用到AOP横向切割的方式抽取一个独立的模块。 总结:AOP运用场合有,性能监控,访问控制,事务处理,日志记录。
学习Spring AOP,大家要先掌握几个AOP相关的重要术语。下面是我的理解,如有异议,大家可以相互讨论一下。 连接点(Joinpoint):程序的执行点。如:类初始化之前、后,方法调用之前、后等。 切点(Pointcut):定位某个方法,通过org.springframework.aop.Pointcut接口描述。 增加(Advice):织入到目标类连接点上的一段代码。spring提供了前置增强,后置增强,异常增强,环绕增强四种类型。 目标对象(target):业务逻辑类. 引介(Introduction):一种特殊的增强,能动态的为业务类添加一个实现接口。让业务类实现该接口。 织入(Weaving):将增强添加到目标类的一个过程。分为编译期织入,类装载期织入,动态代理织入。spring采用的就是动态代理织入,因为前两种需要特殊的java编译器和类装载器。 代理(Proxy):融合了业务逻辑类和增强的代理类。 切面(Aspect):有切点与增强组成。将在后面说到。
上面说到spring aop采用的是动态代理的方式织入的,那么什么是动态代理织入呢?就是在运行期为目标类添加增强生成子类的方式。那么动态代理又分为那些呢?JDK动态代理(通过接口创建代理),CGLib动态代理(通过类创建代理)。
JDK动态代理主要运用java.lang.reflect包中的Proxy类和InvocationHandle接口.通过实现InvocationHandle接口定义横切逻辑,并通过反射机制调用目标类中的代码(如果你对反射机制,可参看我博客反射机制的讲解),动态的将业务逻辑和横切逻辑编织在一起。而Proxy则用于生成一个目标类的代理对象。下面来看一段代码: 将代码一处1、2代码部分去掉 public void deleteUser(int userId) { //pmonitor.start("com.me.services.imp.UserServiceImp.deleteUser"); //trans.beginTransation(); userDao.deleteUser(userId); //trans.commitTransation(); //pmonitor.end(); } public void insertUser(User user) { //pmonitor.start("com.me.services.imp.UserServiceImp.insertUser"); //trans.beginTransation(); userDao.inserUser(user); //trans.commitTransation(); //pmonitor.end(); }
居然JDK动态代理是通过接口创建代理,那么我们就定义个实现InvocationHandle接口,完成业务代码与横切代码编织创建代理实例。 package com.me.util; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class PerformanceHandler implements InvocationHandler { private Object target; public PerformanceHandler(Object target){ this.target = target; } /** * @param proxy 最终生成的代理实例,一般不会用到 * @param method 业务代理中某个具体方法,发起目标实例方法的反射调用 * @param args 业务代理中方法的参数,在方法反射时调用 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //自定义的一个性能监控类 PerformanceMonitor pMonitor = new PerformanceMonitor(); //传入一个性能监控的方法的全限名,即包名+类名+方法名 pMonitor.start(target.getClass().getName()+"."+method.getName()); //通过反射方法调用业务类的目标业务,返回一个业务代理 Object obj = method.invoke(target,args); pMonitor.end(); return obj; } }
编写测试类: package com.me.test; import com.me.entity.User; import com.me.services.UserService; import com.me.services.imp.UserServiceImp; import com.me.util.PerformanceHandler; import java.lang.reflect.Proxy; public class TestUserService { public static void main(String[] args) { //通过JDK创建代理类实例 UserService target = new UserServiceImp();//希望被代理的业务类 PerformanceHandler handler = new PerformanceHandler(target); //根据业务逻辑类和实现InvocationHandler接口性能横切逻辑创建代理实例 UserService proxy = (UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); //参数一:类加载器 参数二:创建代理实例需要的接口 参数三:业务逻辑与横切逻辑编程器对象 proxy.deleteUser(1); User user = new User(); proxy.insertUser(user); } 我们知道JDK动态代理是通过接口定义业务方法类的,那么不用接口定义业务类,我们就要用到CGLib动态创建代理了。 CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截父类方法的调用,并顺势织入横切逻辑。下面来看段代码: package com.me.util; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { //这里引入一个enhancer,查了一下,它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数 private Enhancer enhancer = new Enhancer(); @SuppressWarnings("unchecked") public Object getProxy(Class clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable { PerformanceMonitor pm = new PerformanceMonitor(); pm.start(target.getClass().getName()+"."+method.getName()); //通过代理类调用父类中的方法 Object obj = proxy.invokeSuper(target,args); pm.end(); return obj; } } 测试cglib创建动态代理 package com.me.test; import com.me.entity.User; import com.me.services.UserService; import com.me.services.imp.UserServiceImp; import com.me.util.CglibProxy; public class TestUserService { public static void main(String[] args) { //cglib创建代理实例 CglibProxy proxy = new CglibProxy(); UserService service = (UserServiceImp)proxy.getProxy(UserServiceImp.class); service.deleteUser(1); service.insertUser(new User()); } }
在控制台输出我们可以看到"$$EnhancerByCGLIB$$e28373d6"这是cglib动态创建的子类。
那么两种动态代理要怎么取舍呢? cglib比jdk创建动态代理的性能高大约10倍,但是cglib在创建代理对象时间比jdk多8倍。因此,对于创建singleton或实例池对象代理用cglib方式比较合适,因为无须频繁创建代理对象,但由于cglib采用动态创建子类的方式生成代理对象,所以对目标类中的final方法进行代理。
上述代码不全,我将用到的代码类上传。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-10-26
Spring之新技术又出炉了,我都没有时间研究了,自己都没有心思去做,去看,又把博客给丢到一边了。
坚持啊! 哈哈 |
|
返回顶楼 | |
发表时间:2008-11-03
引用 cglib比jdk创建动态代理的性能高大约10倍,但是cglib在创建代理对象时间比jdk多8倍。因此,对于创建singleton或实例池对象代理用cglib方式比较合适,因为无须频繁创建代理对象,但由于cglib采用动态创建子类的方式生成代理对象,所以对目标类中的final方法进行代理。
不知道这个结论是不是经过测试得到的,我现在基本上都是用CGLIB生成代理,而实际情况是需要频繁用CGLIB生成代理对象来调用其上的方法,更重要的是没有接口可实现...-_-||| |
|
返回顶楼 | |
发表时间:2008-11-05
Joo 写道 引用 cglib比jdk创建动态代理的性能高大约10倍,但是cglib在创建代理对象时间比jdk多8倍。因此,对于创建singleton或实例池对象代理用cglib方式比较合适,因为无须频繁创建代理对象,但由于cglib采用动态创建子类的方式生成代理对象,所以对目标类中的final方法进行代理。不知道这个结论是不是经过测试得到的,我现在基本上都是用CGLIB生成代理,而实际情况是需要频繁用CGLIB生成代理对象来调用其上的方法,更重要的是没有接口可实现...-_-||| 可以自己写个示例测试一下,最近在学习spring,这个结论是我从书上看到的。 |
|
返回顶楼 | |
浏览 7105 次