Spring AOP之代理机制

很早就听说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,下面来看一段大家都属性的代码:

Java代码 复制代码
  1. package com.me.services.imp;   
  2.   
  3. import com.me.entity.User;   
  4. import com.me.services.UserService;   
  5. import com.me.daos.UserDAO;   
  6. import com.me.util.TransationManager;   
  7. import com.me.util.PerformanceMonitor;   
  8.   
  9. public class UserServiceImp implements UserService {   
  10.     private TransationManager trans = new TransationManager();   
  11.     private PerformanceMonitor pmonitor = new PerformanceMonitor();   
  12.     private UserDAO userDao = new UserDAO();   
  13.        
  14.     public void deleteUser(int userId) {   
  15.         <SPAN><SPAN style="COLOR: #000000"><SPAN>pmonitor.start("com.me.services.imp.UserServiceImp.deleteUser");//1</SPAN>   
  16.         <SPAN>trans.beginTransation();//2</SPAN></SPAN></SPAN>   
  17.         userDao.deleteUser(userId);   
  18.         <SPAN><SPAN style="COLOR: #000000"><SPAN>trans.commitTransation();</SPAN>   
  19.         <SPAN>pmonitor.end();</SPAN></SPAN></SPAN>   
  20.     }   
  21.   
  22.     public void insertUser(User user) {   
  23.         <SPAN style="COLOR: #000000">pmonitor.start("com.me.services.imp.UserServiceImp.insertUser");//1   
  24.         trans.beginTransation();//2</SPAN>   
  25.         userDao.inserUser(user);   
  26.         <SPAN style="COLOR: #000000">trans.commitTransation();   
  27.         pmonitor.end();</SPAN>   
  28.     }   
  29.   
  30. }  
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代码部分去掉

Java代码 复制代码
  1. public void deleteUser(int userId) {   
  2.     //pmonitor.start("com.me.services.imp.UserServiceImp.deleteUser");   
  3.     //trans.beginTransation();   
  4.     userDao.deleteUser(userId);   
  5.     //trans.commitTransation();   
  6.     //pmonitor.end();   
  7. }   
  8.   
  9. public void insertUser(User user) {   
  10.     //pmonitor.start("com.me.services.imp.UserServiceImp.insertUser");   
  11.     //trans.beginTransation();   
  12.     userDao.inserUser(user);   
  13.     //trans.commitTransation();   
  14.     //pmonitor.end();   
  15. }  
	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接口,完成业务代码与横切代码编织创建代理实例。

Java代码 复制代码
  1. package com.me.util;   
  2.   
  3. import java.lang.reflect.InvocationHandler;   
  4. import java.lang.reflect.Method;   
  5.   
  6. public class PerformanceHandler implements InvocationHandler {   
  7.   
  8.     private Object target;   
  9.        
  10.     public PerformanceHandler(Object target){   
  11.         this.target = target;   
  12.     }   
  13.     /**  
  14.      * @param proxy 最终生成的代理实例,一般不会用到  
  15.      * @param method 业务代理中某个具体方法,发起目标实例方法的反射调用  
  16.      * @param args 业务代理中方法的参数,在方法反射时调用  
  17.      */  
  18.     public Object invoke(Object proxy, Method method, Object[] args)   
  19.             throws Throwable {   
  20.         //自定义的一个性能监控类   
  21.         PerformanceMonitor pMonitor = new PerformanceMonitor();   
  22.         //传入一个性能监控的方法的全限名,即包名+类名+方法名   
  23.         pMonitor.start(target.getClass().getName()+"."+method.getName());   
  24.         //通过反射方法调用业务类的目标业务,返回一个业务代理   
  25.         Object obj = method.invoke(target,args);   
  26.         pMonitor.end();   
  27.         return obj;   
  28.     }   
  29.   
  30. }  
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;
	}

}

 

编写测试类:

Java代码 复制代码
  1. package com.me.test;   
  2.   
  3. import com.me.entity.User;   
  4. import com.me.services.UserService;   
  5. import com.me.services.imp.UserServiceImp;   
  6. import com.me.util.PerformanceHandler;   
  7. import java.lang.reflect.Proxy;   
  8.   
  9. public class TestUserService {   
  10.     
  11.     public static void main(String[] args) {   
  12.         //通过JDK创建代理类实例   
  13.         UserService target = new UserServiceImp();//希望被代理的业务类   
  14.         PerformanceHandler handler = new PerformanceHandler(target);   
  15.         //根据业务逻辑类和实现InvocationHandler接口性能横切逻辑创建代理实例   
  16.         UserService proxy = (UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);   
  17.         //参数一:类加载器 参数二:创建代理实例需要的接口 参数三:业务逻辑与横切逻辑编程器对象   
  18.         proxy.deleteUser(1);   
  19.         User user = new User();   
  20.         proxy.insertUser(user);   
  21.     }  
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采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截父类方法的调用,并顺势织入横切逻辑。下面来看段代码:

Java代码 复制代码
  1. package com.me.util;   
  2.   
  3. import java.lang.reflect.Method;   
  4.   
  5. import net.sf.cglib.proxy.Enhancer;   
  6. import net.sf.cglib.proxy.MethodInterceptor;   
  7. import net.sf.cglib.proxy.MethodProxy;   
  8.   
  9. public class CglibProxy implements MethodInterceptor {   
  10.     //这里引入一个enhancer,查了一下,它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数   
  11.     private Enhancer enhancer = new Enhancer();   
  12.        
  13.     @SuppressWarnings("unchecked")   
  14.     public Object getProxy(Class clazz){   
  15.         enhancer.setSuperclass(clazz);   
  16.         enhancer.setCallback(this);   
  17.         return enhancer.create();   
  18.     }   
  19.     public Object intercept(Object target, Method method, Object[] args,   
  20.             MethodProxy proxy) throws Throwable {   
  21.         PerformanceMonitor pm = new PerformanceMonitor();   
  22.         pm.start(target.getClass().getName()+"."+method.getName());   
  23.         //通过代理类调用父类中的方法   
  24.         Object obj = proxy.invokeSuper(target,args);   
  25.         pm.end();   
  26.         return obj;   
  27.     }   
  28.   
  29. }  
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创建动态代理

Java代码 复制代码
  1. package com.me.test;   
  2.   
  3. import com.me.entity.User;   
  4. import com.me.services.UserService;   
  5. import com.me.services.imp.UserServiceImp;   
  6. import com.me.util.CglibProxy;   
  7.   
  8. public class TestUserService {   
  9.     
  10.     public static void main(String[] args) {   
  11.         //cglib创建代理实例   
  12.         CglibProxy proxy = new CglibProxy();   
  13.         UserService service = (UserServiceImp)proxy.getProxy(UserServiceImp.class);   
  14.         service.deleteUser(1);   
  15.         service.insertUser(new User());   
  16.     }   
  17. }  
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方法进行代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值