Spring-aop(一)

早过忘川 提交于 2020-03-06 15:08:51

写一个计算类,计算前后需要打印日志。

interface ArithmeticCalculator {
    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);
}

 

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        // TODO Auto-generated method stub
        System.out.println("The method add begins with[" + i + "," + j + "]");
        int result = i + j;
        System.out.println("The method add ends with " + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        // TODO Auto-generated method stub
        System.out.println("The method sub begins with[" + i + "," + j + "]");
        int result = i - j;
        System.out.println("The method sub ends with " + result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        // TODO Auto-generated method stub
        System.out.println("The method mul begins with[" + i + "," + j + "]");
        int result = i * j;
        System.out.println("The method mul ends with " + result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        // TODO Auto-generated method stub
        System.out.println("The method div begins with[" + i + "," + j + "]");
        int result = i / j;
        System.out.println("The method div ends with " + result);
        return result;
    }

}

从上面代码可以看出,四个方法的日志有重复的部分。

 

Main.java

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArithmeticCalculator arithmeticCalculator = null;
        arithmeticCalculator = new ArithmeticCalculatorImpl();
        
        int result = arithmeticCalculator.add(1, 2);
        System.out.println("-->" + result);
        
        result = arithmeticCalculator.div(4, 2);
        System.out.println("-->" + result);
    }

}

 

打印结果:

The method add begins with[1,2]
The method add ends with 3
-->3
The method div begins with[4,2]
The method div ends with 2
-->2

结果没什么问题,可是在加入日志后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还要兼顾其他多个关注点,并且在日志需求发生变化时,必须修改所有模块。

 

接下来,我们使用动态代理解决上述问题

代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        // TODO Auto-generated method stub
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        // TODO Auto-generated method stub
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        // TODO Auto-generated method stub
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        // TODO Auto-generated method stub
        int result = i / j;
        return result;
    }

}

 

public class ArithmeticCalculatorLoggingProxy {
    
    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
        super();
        this.target = target;
    }

    public ArithmeticCalculator getLoggingProxy() {
        ArithmeticCalculator proxy = null;
        //代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型,即其中有哪些方法
        Class [] interfaces = new Class[]{ArithmeticCalculator.class};
        //当调用代理对象其中的方法时,该执行的代码
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy: 正在返回的那个代理对象,一般情况下,在invoke方法中都不使用对象
             * method: 正在被调用的方法
             * args: 调用方法时,传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // TODO Auto-generated method stub
                String methodName = method.getName();
                //日志
                System.out.println("The methods " + methodName + " begins with" + Arrays.asList(args));
                //执行方法
                Object result = method.invoke(target, args);
                //日志
                System.out.println("The methods " + methodName + " ends with" + result);
                return result; 
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

Main.java

     ArithmeticCalculator target = new ArithmeticCalculatorImpl();
        ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
        
        int result = proxy.add(1, 2);
        System.out.println("-->" + result);
        
        result = proxy.div(4, 2);
        System.out.println("-->" + result);

打印结果:

The methods add begins with[1, 2]
The methods add ends with3
-->3
The methods div begins with[4, 2]
The methods div ends with2
-->2

 

用了动态代理方法,就会发现ArithmeticCalculatorImpl实现类变得清洁很多。并且日志被封装在invoke方法中,修改十分方便。

 

aop术语:

切面:横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知:切面必须要完成的工作

目标:被通知的对象

代理:向目标对象应用通知之后创建的对象

连接点:程序执行的某个特定位置,如类某个方法调用前,调用后,方法抛出异常后

由两个信息确定:方法表示的程序执行点,想对点表示的方位。

切点:每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。

AOP通过切点定位到特定的连接点:类比:连接点相当于数据库中的记录,切点相当于查询条件。

 

前置通知:

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!