本文共 5373 字,大约阅读时间需要 17 分钟。
动态代理是用InvocationHandler接口和Proxy类来实现的一种代理模式
SpringAOP即面向切面编程,它对 AOP 进行了封装,使用面向对象的思想来实现,所以AOP的底层是用动态代理实现的
java中提供了一个InvocationHandler接口,用来继承实现动态代理
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
从源码可以看出,该接口只定义了一个方法,invoke()
invoke()方法3个参数的说明:
Object proxy: 代理对象的实例(Proxy的一个动态实例) 可以通过反射机制获取代理类的信息
method: 真实对象要实现的业务方法(由Proxy实例的静态代码块得到)
args: 第二个参数 method 方法的参数
接下来以一个例子理解动态代理
(1) 创建一个类,继承InvocationHandler接口
public class MyProxyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }}
(2) 添加方法,得到生成的代理类
类中除了重写的invoke方法,其余基本是固定写法,目的是为了通过传入真实对象(要使用代理的对象)得到代理类 具体解释见代码注释
//用这个类,自动生成代理类public class MyProxyInvocationHandler implements InvocationHandler { //被代理的类 public Object object; //得到一个代理实例(代理类) public Object getProxy(Object object){ this.object = object; //通过Proxy.newProxyInstance方法创建一个代理对象 即得到代理类 // 3个参数为 要代理的对象的类加载器、接口、及实现了InvocationHandler接口的类,一般即自身 return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }}
(3) 重写invoke方法
① 在invoke方法中,可以通过 method 执行要代理的类的自身的方法
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 将代理的对象和执行参数代入执行方法 也就可以执行要代理的对象自身的方法 method.invoke(object,args); return null; }
② 在method.invoke()函数前后,可以添加额外的执行方法,也就是面向切面编程的思想(原来是在要代理的类中添加,但是这样就可以在这个方法里面添加)
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("在"+method.getName()+"前添加了一个方法"); // 将代理的对象和执行参数代入执行方法 也就可以执行代理类自身的方法 method.invoke(object,args); System.out.println("在"+method.getName()+"后添加了一个方法"); return null; }
Spring 框架对 AOP 进⾏了封装,Spring 使用面向对象的思想来实现 AOP
Spring 框架中不需要创建 InvocationHandler,只需要创建⼀个切面对象,将所有的非业务代码在切⾯对象中完成,Spring 框架底层会自动根据切面类以及目标类生成⼀个代理对象
上述概念有点繁琐,下面通过使用SpringAOP的一个例子来理解
思路:编写一个计算器类 拥有加减乘除方法,然后通过SpringAOP给计算器添加额外的打印日志方法
(1) 编写MyCalculator 接口及其实现类 并添加上@Component注解(代表交给springIOC容器管理)
public interface MyCalculator { int add(int num1,int num2); int sub(int num1,int num2); int mul(int num1,int num2); int div(int num1,int num2);}
public class MyCalculatorImpl implements MyCalculator{ public int add(int num1, int num2) { return num1+num2; } public int sub(int num1, int num2) { return num1-num2; } public int mul(int num1, int num2) { return num1*num2; } public int div(int num1, int num2) { return num1/num2; }}
(2) 编写切面日志类,使用SpringAOP,实现额外添加打印日志方法的功能
@Aspect 代表该类是一个切面类
//切面日志类 通过SpringAOP来实现功能 @Aspect注解表示该类为一个切面类@Aspect@Componentpublic class AspectLogger { }
(3)在AspectLogger类中添加方法,并使用SpringAOP注解
//切面日志类 通过SpringAOP来实现功能 @Aspect注解表示该类为一个切面类@Aspect@Componentpublic class AspectLogger { //Before 表示在value对应方法执行前执行 .*代表该类下的所有方法 (..)表示参数通配符 也可以使用.方法名()来指定一个方法 @Before(value="execution(public int MyCalculatorImpl.*(..))") // JoinPoint:切入点 public void executeBeforeMethod(JoinPoint joinPoint){ //获取⽅法名 String name = joinPoint.getSignature().getName(); //获取参数 String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name+"⽅法的参数是:"+ args); } @After(value = "execution(public int MyCalculatorImpl.*(..))") public void after(JoinPoint joinPoint){ //获取⽅法名 String name = joinPoint.getSignature().getName(); System.out.println(name+"⽅法执⾏完毕"); } //返回后执行 returning赋予的String要和方法的第二个参数Object result <- 即这个result名字对应 @AfterReturning(value = "execution(public int MyCalculatorImpl.*(..))",returning = "result") public void afterReturning(JoinPoint joinPoint,Object result){ //获取⽅法名 String name = joinPoint.getSignature().getName(); System.out.println(name+"⽅法的结果是"+result); } //抛出异常后执行 参数对应和上一个一样 @AfterThrowing(value = "execution(public int MyCalculatorImpl.*(..))",throwing = "exception") public void afterThrowing(JoinPoint joinPoint,Exception exception){ //获取⽅法名 String name = joinPoint.getSignature().getName(); System.out.println(name+"⽅法抛出异常:"+exception); }}
代码说明:
@Before @After @AfterReturning @AfterThrowing :表示方法执行的位置和时机
JoinPoint对象:JoinPoint即切入点,它封装了SpringAop中切面方法的信息 在切面方法通过JoinPoint参数,可以获取到封装了该方法信息的JoinPoint对象
JionPoint的常用方法 :
方法名 | 功能 |
---|---|
Signature getSignature() | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class信息等 |
Object[] getArgs() | 获取传入目标方法的参数对象 |
Object getTarget() | 获取被代理的对象 |
Object getThis() | 获取代理对象 |
(4) 在Test类中编写main方法进行测试
public static void main(String[] args) { // ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.ruoxi"); ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml"); MyCalculator myCalculator = (MyCalculator) applicationContext.getBean("myCalculatorImpl"); myCalculator.add(1,2); myCalculator.sub(1,5); myCalculator.mul(1,3); myCalculator.div(4,2); }}
通过输出结果可以看出,已成功通过SpringAOP的方式给MyCalulator的各个函数添加了打印日志的方法
转载地址:http://boph.baihongyu.com/