码迷,mamicode.com
首页 > 编程语言 > 详细

spring AOP 代理(静态与动态)

时间:2018-12-17 21:34:15      阅读:265      评论:0      收藏:0      [点我收藏+]

标签:spring   img   opened   gif   功能   关联   src   throws   pre   

一、没有代理模式

缺点:

1、工作量特别大,如果项目中有多个类,多个方法,则要修改多次。

2、违背了设计原则:开闭原则(OCP),对扩展开放,对修改关闭,而为了增加功能把每个方法都修改了,也不便于维护。

3、违背了设计原则:单一职责(SRP),每个方法除了要完成自己本身的功能,还要计算耗时、延时;每一个方法引起它变化的原因就有多种。

4、违背了设计原则:依赖倒转(DIP),抽象不应该依赖细节,两者都应该依赖抽象。而在Test类中,Test与Math都是细节。

假设需实现一个计算的类Math、完成加、减、乘、除功能,如下所示:

Math类:

技术分享图片
public class Math {
    //
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }
    
    
    //
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }
    
    //
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }
    
    //
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}
View Code

现在需求发生了变化,要求项目中所有的类在执行方法时输出执行耗时。最直接的办法是修改源代码,如下所示:

技术分享图片
public class Math {
    //
    public int add(int n1,int n2){
        //开始时间
        long start = getTime();
        delay();
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }
    //
    public int sub(int n1,int n2){
        //开始时间
        long start = getTime();
        delay();
        int result=n1*n2;
        System.out.println(n1+"-"+n2+"="+result);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    //
    public int mut(int n1,int n2){
        //开始时间
        long start = getTime();
        delay();
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    //
    public int div(int n1,int n2){
        //开始时间
        long start = getTime();
        delay();
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }
    public static long getTime() {
        return System.currentTimeMillis();
    }
    public static void delay(){
        try {
            int n = (int) new Random().nextInt(500);
            Thread.sleep(n);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
View Code

测试运行:

 Math math = new Math();
        int n=100;
        int m=5;
        math.add(n,m);
        math.sub(n,m);
        math.mut(n,m);
        math.div(n,m);

 

技术分享图片

二、静态代理

 1、定义抽象主题接口。

IMath:
public interface IMath {
    int add(int n1,int n2);
    int sub(int n1,int n2);
    int mut(int n1,int n2);
    int div(int n1,int n2);
}

技术分享图片

2、实现接口:

MathImpl
技术分享图片
public class MathImpl implements IMath{
    //
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }

    //
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }

    //
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }

    //
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}
View Code

3、代理类

MathProxy
技术分享图片
public class MathProxy implements IMath {
    IMath math = new MathImpl();

    @Override
    public int add(int n1, int n2) {
        //开始时间
        long start = getTime();
        delay();
        int result = math.add(n1, n2);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    @Override
    public int sub(int n1, int n2) {
        //开始时间
        long start = getTime();
        delay();
        int result = math.sub(n1, n2);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    @Override
    public int mut(int n1, int n2) {
        //开始时间
        long start = getTime();
        delay();
        int result = math.mut(n1, n2);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    @Override
    public int div(int n1, int n2) {
        //开始时间
        long start = getTime();
        delay();
        int result = math.div(n1, n2);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }

    public static long getTime() {
        return System.currentTimeMillis();
    }
    public static void delay(){
        try {
            int n = (int) new Random().nextInt(500);
            Thread.sleep(n);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
View Code

测试运行:

技术分享图片
  IMath math = new MathProxy();
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
View Code

技术分享图片

通过静态代理,是否完全解决了上述的4个问题:

已解决:

1、解决了“开闭原则(OCP)”的问题,因为并没有修改Math类,而扩展出了MathProxy类。

2、解决了“依赖倒转(DIP)”的问题,通过引入接口。

3、解决了“单一职责(SRP)”的问题,Math类不再需要去计算耗时与延时操作,但从某些方面讲MathProxy还是存在该问题。

未解决:

4、如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。

如果要解决上面的问题,可以使用动态代理。

三、动态代理

1、接口不变:

技术分享图片

2、实现类接口不变

技术分享图片

3、修改代理类,实现接口InvocationHandler

DynamicProxy

 

package com.wbg.springAOP.springdynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
/**
 * 动态代理类
 */
public class DynamicProxy implements InvocationHandler {
    IMath math = new MathImpl();
    //目标代理对象
    Object targetObject;
    public DynamicProxy(Object target){
        this.targetObject=target;
    }
    public DynamicProxy(){

    }
    /**
     * 获得被代理后的对象
     * @param object 被代理的对象
     * @return 代理后的对象
     */
    public Object getProxyObject(Object object){
        this.targetObject=object;
        return Proxy.newProxyInstance(
                targetObject.getClass().getClassLoader(), //类加载器
                targetObject.getClass().getInterfaces(),  //获得被代理对象的所有接口
                this);  //InvocationHandler对象
        //loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来生成代理对象进行加载
        //interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
        //h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上,间接通过invoke来执行
    }


    /**
     * 获取时间
     * @return
     */
    public static long getTime() {
        return System.currentTimeMillis();
    }

    /**
     * 延迟
     */
    public static void delay(){
        try {
            int n = (int) new Random().nextInt(500);
            Thread.sleep(n);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
     * proxy 被代理后的对象
     * method 将要被执行的方法信息(反射)
     * args 执行方法时需要的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start=getTime();
        delay();
        Object result=method.invoke(targetObject,args);
        System.out.println("共用时:" + (getTime() - start));
        return result;
    }
}

 

4、测试:

方式一:

 DynamicProxy dynamicProx = new DynamicProxy(new MathImpl());
        IMath math = (IMath) Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{IMath.class},dynamicProx);
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);

方式二:

  //实例化一个MathProxy代理对象
      //通过getProxyObject方法获得被代理后的对象
      IMath math=(IMath)new DynamicProxy().getProxyObject(new MathImpl());
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);

运行结果

技术分享图片

JDK内置的Proxy动态代理可以在运行时动态生成字节码,而没必要针对每个类编写代理类。中间主要使用到了一个接口InvocationHandler与Proxy.newProxyInstance静态方法,参数说明如下:

 使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。

如果项目中有些类没有实现接口,则不应该为了实现动态代理而刻意去抽出一些没有实例意义的接口,通过cglib可以解决该问题。

 



 

spring AOP 代理(静态与动态)

标签:spring   img   opened   gif   功能   关联   src   throws   pre   

原文地址:https://www.cnblogs.com/weibanggang/p/10133965.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!