博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringAOP_01 动态代理
阅读量:3967 次
发布时间:2019-05-24

本文共 6128 字,大约阅读时间需要 20 分钟。


本人是个新手,写下博客用于自我复习、自我总结。

如有错误之处,请各位大佬指出。
学习资料来源于:尚硅谷


Calculator.java:

package com.guigu.inter;public interface Calculator {
public int add(int i,int j); public int sub(int i,int j); public int mul(int i,int j); public int div(int i,int j); }

LogUtils.java:

package com.guigu.utils;import java.lang.reflect.Method;import java.util.Arrays;public class LogUtils {
public static void logStart(Method method,Object...args){
System.out.println("【"+method.getName()+"】方法开始执行," + "用的参数列表【"+Arrays.asList(args)+"】"); } public static void logReturn(Method method,Object result){
System.out.println("【"+method.getName()+"】方法执行完成," + "它的计算结果是:"+result); } public static void logException(Method method, Exception e) {
System.out.println("【"+method.getName()+"】方法执行异常," +"它的异常信息是:"+e.getCause() +",这个异常已经通知测试小组。"); } public static void logEnd(Method method) {
System.out.println("【"+method.getName()+"】方法最终结束了"); }}

MyMathCalculator.java:

package com.guigu.impl;import com.guigu.inter.Calculator;import com.guigu.utils.LogUtils;public class MyMathCalculator implements Calculator{
/** * 从目前来看,如果有很多方法需要输出日志信息,一种办法是定义一个通用方法, * 如下例的LogUtils中方法。 * 另一种办法是直接用syso输出。 * 但显然这两种办法都很麻烦。如果需要更改输出信息,需要将每一个方法中的信息 * 更改;放在LogUtils中,同样麻烦。再怎么写也都是耦合在一起。 * */ /** @Override public int add(int i, int j) { LogUtils.logStart(i,j); int result = i + j ; System.out.println("【add】方法运行完成,计算结果是【"+result+"】"); return result; } @Override public int sub(int i, int j) { LogUtils.logStart(i,j); int result = i - j ; System.out.println("【sub】方法运行完成,计算结果是【"+result+"】"); return result; } @Override public int mul(int i, int j) { LogUtils.logStart(i,j); int result = i * j ; System.out.println("【mul】方法运行完成,计算结果是【"+result+"】"); return result; } @Override public int div(int i, int j) { LogUtils.logStart(i,j); int result = i / j ; System.out.println("【div】方法运行完成,计算结果是【"+result+"】"); return result; } */ /** * 解决上述问题,可以使用动态代理的办法。 * 见CalculatorProxy.java * * 之后可以对LogUtils进行完善 * */ @Override public int add(int i, int j) {
int result = i + j ; return result; } @Override public int sub(int i, int j) {
int result = i - j ; return result; } @Override public int mul(int i, int j) {
int result = i * j ; return result; } @Override public int div(int i, int j) {
int result = i / j ; return result; } }

CalculatorProxy.java:

package com.guigu.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;import com.guigu.inter.Calculator;import com.guigu.utils.LogUtils;/** * 帮Calculator.java生成代理对象的类 * Object newProxyInstance * (ClassLoader loader,Class
[] interfaces, InvocationHandler h) * * */public class CalculatorProxy {
/** * 为传入的参数对象创建一个动态代理对象 * * Calculator calculator: 被代理对象 * 返回的是 代理人 * */ public static Calculator getProxy(final Calculator calculator) {
// TODO Auto-generated method stub //InvocationHandler:方法执行器,帮我们目标对象执行目标方法。 //有了它,动态代理才有意义 InvocationHandler h=new InvocationHandler(){
/** * Object proxy:代理对象,给jdk使用,任何时候我们都不要动这个对象 * Method method:当前将要执行的目标对象 * Object[] args:这个方法调用时,外界传入的参数值 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//利用反射执行目标方法 //目标方法执行后的返回值 Object result=null; try {
LogUtils.logStart(method, args); result = method.invoke(calculator,args); LogUtils.logReturn(method, result); } catch (Exception e) {
LogUtils.logException(method,e); }finally{
LogUtils.logEnd(method); } //返回值必须返回出外界,才能拿到真正执行后的返回值 return result; } }; //interfaces:查询calculator实现了的接口 Class
[] interfaces=calculator.getClass().getInterfaces(); //loader:类加载器 ClassLoader loader=calculator.getClass().getClassLoader(); //Proxy为目标对象创建代理对象: Object proxy=Proxy.newProxyInstance(loader, interfaces, h); return (Calculator) proxy; }}

AOPTest.java:

package com.guigu.test;import static org.junit.Assert.*;import org.junit.Test;import com.guigu.impl.MyMathCalculator;import com.guigu.inter.Calculator;import com.guigu.proxy.CalculatorProxy;public class AOPTest {
/** * AOP:(Aspect Oriented Programming)面向切面编程; OOP:(Object Oriented Programming)面向对象编程; AOP:基于OOP基础之上新的编程思想; 面向切面编程是指:在程序运行期间,将某段代码动态的切入到指定方法的指定位置 进行运行的这种编程方式。 场景:计算器运行计算方法的时候,进行日志记录; 加日志记录: 1)直接编写在方法内部;(不推荐。修改维护麻烦) 日志记录:系统的辅助功能; 业务逻辑:(核心功能) 耦合 2)我们希望的是: 业务逻辑和日志记录模块,能在核心功能运行期间, 自己动态的加上; * * 有了动态代理之后,日志记录可以做的非常强大。 * 而且与业务逻辑解耦。 * * 而且使用动态代理之后,可以将日志代码动态的在目标方法前后进行执行。 * 见LogUtils.java和CalculatorProxy.java * * 动态代理也有两大问题: * 1)写起来很复杂 * 2)jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为它创建代理 * 对象的。 * * 那么动态代理是怎么实现的呢? * 你会发现,proxy的类是:com.sun.proxy.$Proxy2 * 代理对象和被代理对象唯一能产生的关联就是实现了同一个接口。 * 也就是说,com.sun.proxy.$Proxy2也是实现了Calculator接口。 * * 动态代理很难。 * 所以Spring实现了AOP功能,底层就是动态代理。 * 可以利用Spring一句代码都不写的去创建动态代理,实现很简单, * 而且没有强制要求目标对象必须实现接口。 * * 所以在现在来看这句话: * 将某段代码(日志)动态的切入(不把日志代码写死在业务逻辑方法中) * 到指定方法(该例:加减乘除)的指定位置(方法的开始、结束、异常。。。) * 进行运行的这种编程方式(Spring简化了面向切面编程) * * 为之后的AOP做准备:AOP专业术语: * * 本例中,要实现Calculator中的加减乘除方法, * 并在方法开始、方法返回、方法异常、方法结束的四个时间节点上, * 去输出了相对应的话,即调用了LogUtils中的四个方法。 * * 这里的LogUtils叫做切面类,四个方法叫做通知方法, * 调用这四个方法的时间节点(就是什么时候调用日志记录)叫做横切关注点。 * 上述的这个时间节点就叫做连接点 * (即:每一个方法的每一个位置都是一个连接点,该例有16个连接点) * * 比如:我只需要在div方法异常、mul方法返回、add方法结束,三个连接点上进行日志记录 * 这种我们真正需要执行的日志记录的地方,叫做切入点。 * 即:切入点只是在众多连接点中,我们感兴趣的一部分 * * 而我们怎么选出这些切入点呢? * 就是运用切入点表达式。 * 即切入点是用切入点表达式帮我们根据要求在众多连接点中选出来的。 * */ @Test public void test() {
Calculator calculator = new MyMathCalculator(); calculator.add(1, 2); calculator.div(2, 1); System.out.println("[===========]"); //如果是拿到了这个对象的代理对象,再用代理对象执行加减乘除 Calculator proxy = CalculatorProxy.getProxy(calculator); proxy.add(2,1); proxy.div(2, 1); }}

转载地址:http://rxyki.baihongyu.com/

你可能感兴趣的文章
BaseAnimation是基于开源的APP,致力于收集各种动画效果(最新版本1.3)
查看>>
安卓开发者必备的42个链接
查看>>
为GridView添加HeaderView
查看>>
我的Android进阶之旅------>经典的大牛博客推荐(排名不分先后)!!
查看>>
Android时间获取与使用
查看>>
Android WebRTC 音视频开发总结(一)
查看>>
用Gradle 构建你的android程序
查看>>
Android监听应用程序安装和卸载实现程序
查看>>
Android 监听apk安装替换卸载广播的实现代码
查看>>
Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚
查看>>
Android下拉刷新上拉加载控件,对所有View通用!
查看>>
Android自定义控件实战——仿多看阅读平移翻页
查看>>
Android自定义控件实战——仿淘宝商品浏览界面
查看>>
Android自定义控件实战——水流波动效果的实现WaveView
查看>>
Android自定义控件实战——水波纹标签云TagCloud
查看>>
Android自定义控件实战——滚动选择器PickerView
查看>>
Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout
查看>>
Android事件分发、View事件Listener全解析
查看>>
Eclipse下使用Ant多渠道批量打包
查看>>
Eclipse下Ant自动打包,混淆和签名
查看>>