基础篇——代理模式之SpringAOP
Posted 不浪小生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础篇——代理模式之SpringAOP相关的知识,希望对你有一定的参考价值。
一、AOP概述
AOP思想的实现一般都是基于代理模式,JDK动态代理模式只能代理接口,而不能代理类。但是SpringAOP同时支持CGLIB、JDK、ASPECTJ这三种代理模式,因此,如果目标的对象实现了接口,SpringAOP将会采用JDK动态代理,如果目标对象没有实现接口,SpringAOP切换成CGLIB动态代理。
在实际情况下,我们通常需要对一个接口实现多个切面,比如日志打印、分布式锁、权限校验等等。这时候我们就会面临一个优先级的问题,这么多的切面如何告知Spring执行顺序呢?这就需要我们定义每个切面的优先级,我们可以使用注解@Order(i)来表示切面的优先级,i的值越小,优先级越高。假设我们现在一共有两个切面,一个日志处理切面,设置其@Order(100);一个分布式锁,设置其@Order(99),那么分布式锁的优先级是高于日志处理的,对于通知@Before来说,先执行分布式锁,再执行日志处理;对于通知@After和@AfterReturning来说,先执行日志处理,再执行分布式锁,这样就可以完成一个首尾的对应,也可以理解为先进后出原则。
二、静态代理
接口类:
interface Person { void speak();}
实体类:
class Actor implements Person { private String content; public Actor(String content) { this.content = content;} public void speak(){ System.out.println(this.content); } }
代理类:
class Agent implements Person { private Actor actor; private String before; private String after; public Agent(Actor actor, String before, String after){ this.actor = actor; this.before = before; this.after = after; } public void speak(){ System.out.println("前置通知:" + this.before); this.actor.speak(); System.out.println("后置通知:" + this.after); } }
测试类:
public class StaticProxyTest{ public static void main(String[] args){ Actor actor = new Actor("实体类的真正内容"); Agent agent = new Agent(actor, "前置内容", "后置内容"); agent.speak(); } }
二、动态代理
1、JDK自带动态代理
最核心的一个接口和方法,java.lang.reflect包下的InvocationHandler接口:
public interface InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
proxy:被代理的类的实例
method:调用被代理的类的方法
args:该方法需要的参数
我们实现该接口,在invoke方法中调用实体类的方法,并在其前后进行动态操作,从而实现动态代理。另外一个很重要的静态方法是java.lang.reflect包中的Proxy类的newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException;
loader:被代理类的类加载器
interfaces:被代理类的接口数组
h:调用处理器类的对象实例
该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。
Person接口:
public interface Person{ public void show(); }
Actor实体类:
public class Actor implements Person{ public void show(){ System.out.println("真正实现的方法"); } }
Agent代理类:
public class DynamicAgent{ static class Agent implements InvocationHandler{ private Object proxy; public Agent(Object proxy){ this.proxy = proxy; } public Object invoke(Object object, Method method, Object[] args) throws Throwable { System.out.println("自己添加的前置通知"); //真正调用的方法 Object res = method.invoke(this.proxy, args); System.out.println("自己添加的后置通知"); return res; } public static Object agent(Class interfaceClazz, Object proxy){ return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, new Agent(proxy)); } } }
测试类ReflectTest:
public class ReflectTest{ public static void main(String[] args) throws InvocationTargetException, IllegralAccessException { Person person = (Person)DynamicAgent.agent(Person.class, new Actor()); person.show(); } }
这样以后对于不同的实现类来说,可以用同一个动态代理类来进行代理,但是这种方法有个缺点,就是被代理的类一定要是实现了某个接口的。
2、CGLIB动态代理
同样的CGLIB也有两个重要的方法,在MethodInterceptor接口中的getInstance(Object proxy)和intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)方法 。
CGLIB代理类CglibAgent:
public class CglibAgent implements MethodInterceptor { private Object proxy; public Object getInstance(Object proxy){ this.proxy = proxy; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.proxy.getClass); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] object, MethodProxy methodProxy) throws Throwable{ System.out.println("自己添加的前置通知"); Object res = methodProxy.invokeSuper(this.proxy, args); System.out.println("自己添加的后置通知"); return res; } //测试 public static void main(String[] args){ CglibAgent cglibAgent = new CglibAgent(); Actor actor = (Actor)cglibAgent.getInstance(new Actor()); actor.show(); } }
使用CGLIB处理之后,可以直接对实体类进行动态代理。
以上是关于基础篇——代理模式之SpringAOP的主要内容,如果未能解决你的问题,请参考以下文章