spring AOP详解二
AOP实例(通过Proxy代理模式)
Spring AOP使用纯java实现,不需要专门的编译过程和类装载器,它在运行期间通过代理方式向目标类织入增强代码,它更侧重于提供一种和Spring IoC容器整合的AOP实现,在Spring中,我们可以无缝的将AOP,IoC,AspectJ整合在一起。
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,一种是基于CGLib的动态代理;
JDK1.3以后,java提供了动态代理技术,允许开发者在运行期间动态的创建接口的代理实例,JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler,其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
下面我们来看一个JDK动态代理的例子:
1.业务接口UserService.java
package spring.aop.demo1; public interface UserService { void removeUser(int userId); }
2.横切逻辑代理监视代码PerformanceMonitor.java
package spring.aop.demo1; public class MethodPerformance { private long begin; private long end; private String serviceMethod; public MethodPerformance(String serviceMethod) { this.serviceMethod = serviceMethod; this.begin = System.currentTimeMillis(); } public void printPerformance() { this.end = System.currentTimeMillis(); long elapse = end - begin; System.out.println(serviceMethod + "花费" + elapse + "毫秒"); } }
package spring.aop.demo1; public class PerformanceMonitor { // 通过一个ThreadLocal保存调用线程相关的性能监视信息 private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>(); // 启动对一目标方法的性能监视 public static void begin(String method) { System.out.println("begin monitor..."); MethodPerformance mp = new MethodPerformance(method); performanceRecord.set(mp); } public static void end() { System.out.println("end monitor..."); MethodPerformance mp = performanceRecord.get(); mp.printPerformance(); } }
3.横切逻辑代理代码PerformanceHandler.java
package spring.aop.demo1; 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; } @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { PerformanceMonitor.begin(target.getClass().getName() + "." + arg1.getName()); Object obj = arg1.invoke(target, arg2);// 通过反射机制调用目标对象的方法 PerformanceMonitor.end(); return obj; } }
首先,我们实现InvocationHandler接口,该接口定义了一个invoke方法,proxy最是最终生成的一个代理实例,一般不会用到,参数arg1是被代理目标实例的某个具体的方法,通过它可以发起目标实例方法的反射调用;参数arg2是通过被代理实例某一个方法的入参,在方法反射调用时候使用,通过代理将横切逻辑代码和业务类的代码编织到了一起。
我们在构造函数里通过target传入希望被代理的目标对象,将目标实例产地个method.inoke(),调用目标实例的方法。
4.通过Proxy结合PerformanceHandler创建UserService接口的代理实例:
package spring.aop.demo1; import java.lang.reflect.Proxy; public class UserServiceImpl implements UserService { @Override public void removeUser(int userId) { System.out.println("模拟删除用户:" + userId); try { Thread.currentThread().sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { UserService userService = new UserServiceImpl(); // 将目标业务类和横切代码编织到一起 PerformanceHandler handler = new PerformanceHandler(userService); // 根据编织了目标业务类逻辑和性能监视横切逻辑的InvocationHandler实例创建代理实例 UserService proxy = (UserService) Proxy.newProxyInstance(userService .getClass().getClassLoader(), userService.getClass() .getInterfaces(), handler); proxy.removeUser(3); } }
输出:
begin monitor...
模拟删除用户:3
end monitor...
spring.aop.demo1.UserServiceImpl.removeUser花费203毫秒
说明:上面的代码完成业务类代码和横切代码的编制工作,并生成了代理实例,newProxyInstance方法的第一个参数为类加载器,第二个参数为目标类所实现的一组接口,第三个参数是整合了业务逻辑和横切逻辑的编织器对象。使用JDK代理模式有一个限制,即它只能为接口创建代理实例,这一点我们可以从Proxy.newProxyInstance的方法签名中就可以看的很清楚,第二个参数interfaces就是需要代理实例实现的接口列表。