SpringFramework学习-(12)AOP与代理模式

上一篇SpringFramework学习-(11)初识AOP
介绍了什么是AOP,以及使用AOP是怎么配置的,以及使用SpringAOP的结果。
本篇并不以SpringAOP进行展开,而是通过AOP模式进行展开,看一看AOP是怎么实现的。

1.JDK静态代理实现AOP

1)首先创建一个接口(JDK代理都是面向接口的);
2)然后创建具体实现类来实现这个接口;
3)再创建一个代理类同样实现这个接口,不同之处在于,具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中持有具体实现类的引用,代理类的方法中使用该引用调用对应方法。
这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。

定义接口

package com.jd.staticProxy;

public interface CaInterface {

    public int add(int a, int b);

}

创建实现类

package com.jd.staticProxy;

public class CaInterfaceImpl implements CaInterface{

    public int add(int a, int b) {
        int result = a + b;
        System.out.println("a:" + a + "加上 b:" + b + "等于" + result); 
        return result;
    }

}

创建代理类

package com.jd.staticProxy;

public class CaInterfaceProxy implements CaInterface{

    private CaInterface caInterface = new CaInterfaceImpl();

    public int add(int a, int b) {
        System.out.println("我是静态代理的前置。。。");
        caInterface.add(a, b);
        System.out.println("我是静态代理的后置。。。");
        return 0;
    }
}

测试

package com.jd.staticProxy;

public class StaticProxyTest {

    public static void main(String[] args) {
        CaInterfaceProxy cip = new CaInterfaceProxy();
        cip.add(1, 4);
    }

}

结果

我是静态代理的前置。。。
a:1加上 b:4等于:5
我是静态代理的后置。。。

再不改动具体实现类的情况下,通过一个代理实现了对具体方法的处理,达到AOP的效果。
静态代理虽然实现起来非常的简便易懂,但是有一个非常大的问题:需要为每个需要代理的类都要创建一个代理类。那么如果要被代理类的集合比较大,那么需要创建很多很多的代理类,这种方式的工作量会很大。

2.JDK动态代理实现AOP

只能对实现了接口的类生产代理,不能针对类。

1)首先创建一个接口(JDK代理都是面向接口的);
2)然后创建具体实现类来实现这个接口;
3)创建代理对象,实现java.lang.reflect.InvocationHandler接口,实现接口方法。在代理对象传入被代理对象实例。

创建接口:

package com.jd.dynamic;

public interface CaInterface {

    public int add(int a, int b);

}

创建实现类

package com.jd.dynamic;

public class CaInterfaceImpl implements CaInterface{

    public int add(int a, int b) {
        int result = a + b;
        System.out.println("a:" + a + "加上 b:" + b + "等于" + result); 
        return result;
    }

}

创建代理,看看我们在invoke()方法中做了什么。

package com.jd.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class CaInterfaceProxy implements InvocationHandler{

    //被代理的目标
    private Object target;


    //创建构造方法,传递被代理对象
    public CaInterfaceProxy(Object target) {
        this.target = target;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是静态代理的前置。。。" + "method:"+method.getName()+",参数:"+Arrays.asList(args));
        Object result=method.invoke(target, args);  
        System.out.println("我是静态代理的后置。。。"+"执行结果:" + result); 

        return result;
    }

    /**
     * @return 被代理对象的代理(目标对象的代理对象)
     */
    public Object getProxy(){  
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),   
                this.target.getClass().getInterfaces(),this);  
    }  

}

测试

package com.jd.dynamic;

import com.jd.dynamic.CaInterface;
import com.jd.dynamic.CaInterfaceImpl;
import com.jd.dynamic.CaInterfaceProxy;

public class DynamicProxyTest {

    public static void main(String[] args) {

        CaInterfaceImpl target = new CaInterfaceImpl();

        CaInterfaceProxy cip = new CaInterfaceProxy(target);

        CaInterface ci = (CaInterface) cip.getProxy();

        ci.add(1, 4);
    }
}

结果

我是静态代理的前置。。。method:add,参数:[1, 4]
a:1加上 b:4等于5
我是静态代理的后置。。。执行结果:5

当然还有一种实现方式,将InvocationHandler作为参数进行传递,这里也展示一下:

package com.jd.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class CaInterfaceProxy2 {

    //要代理的对象
    private Object target;

    public CaInterfaceProxy2(Object target) {
        this.target = target;
    }

    public Object getLoggerProxy(){

        Object proxy = null;

        ClassLoader classLoader = target.getClass().getClassLoader();


        InvocationHandler handler = new InvocationHandler() {

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("我是静态代理的前置。。。" + "method:"+method.getName()+",参数:"+Arrays.asList(args));
                Object result=method.invoke(target, args);  
                System.out.println("我是静态代理的后置。。。"+"执行结果:" + result); 

                return result;
            }
        };
        proxy = Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), handler);

        return proxy;
    }

}

测试

public static void main(String[] args) {

        CaInterface target = new CaInterfaceImpl();

        CaInterface ci = (CaInterface) new CaInterfaceProxy2(target).getLoggerProxy();

        ci.add(4, 2);

    }

结果

我是静态代理的前置。。。method:add,参数:[4, 2]
a:4加上 b:2等于6
我是静态代理的后置。。。执行结果:6

动态代理可以动态的传递被代理对象,然后对被代理对象进行处理。
但是:
从Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
this.target.getClass().getInterfaces(),this);
方法中我们看到其中有一个参数要求我们必须是实现接口才可以使用的,那么我们将接口去掉,直接使用实现类,结果就会报错。

Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.jd.dynamicProxy.CaInterface
at com.jd.dynamicProxy.DynamicProxyTest.main(DynamicProxyTest.java:15)

所以这也是jdk动态代理的一个特征。

3.CGLIB动态代理实现AOP

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB是一个强大的高性能的代码生成包。被广泛的许多AOP框架使用,如Spring的AOP和dynaop,为他们提供方法的interceptor(拦截),最流行的是OR Mapping工具Hibernate也是使用cglib来代理单端的single-ended(多对一和一对一)关联(对集合的延迟抓取是采用其他机制实现)。EsayMock和jMock是通过模仿(moke)对象来测试java代码的包。他们都是通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
我们先通过demo来快速了解cglib的使用示例。

创建类

package com.jd.proxyStaticcglib;

public class CaInterfaceImpl{

    public int add(int a, int b) {
        int result = a + b;
        System.out.println("a:" + a + "加上 b:" + b + "等于" + result); 
        return result;
    }

}

创建代理

package com.jd.proxyStaticcglib;

import java.lang.reflect.Method;
import java.util.Arrays;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibProxy implements MethodInterceptor{

      private Object target;

        /**  
         * 创建代理对象  
         */    
        public Object getInstance(Object target) {    
            this.target = target;    
            Enhancer enhancer = new Enhancer();    
            enhancer.setSuperclass(this.target.getClass());    
            enhancer.setCallback(this);//回调方法
            return enhancer.create();//创建代理对象  
        }  


        /**
         * 回调方法    
         */
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {    
            System.out.println("我是静态代理的前置。。。" + "method:"+method.getName()+",参数:"+Arrays.asList(args));
            proxy.invokeSuper(obj, args);    
            System.out.println("我是静态代理的后置。。。"); 
            return null;    

        } 
}

测试

package com.jd.proxyStaticcglib;

public class CGLibProxyTest {

    public static void main(String[] args) {

        CGLibProxy cp = new CGLibProxy();

        CaInterfaceImpl ci= (CaInterfaceImpl)cp.getInstance(new CaInterfaceImpl());

        ci.add(1,5);
    }

}

结果

我是静态代理的前置。。。method:add,参数:[1, 5]
a:1加上 b:5等于6
我是静态代理的后置。。。

上面我们通过动态代理实现了AOP的效果。那么SpringAOP使用的是哪种代理来实现AOP的效果呢?
看一下官方文档怎么说:

|—————————————————————————————————————————————————————
If the class of a target object that is to be proxied (hereafter simply referred to as the target class) doesn’t implement any interfaces, then a CGLIB-based proxy will be created. This is the easiest scenario, because JDK proxies are interface based, and no interfaces means JDK proxying isn’t even possible. One simply plugs in the target bean, and specifies the list of interceptors via the interceptorNames property. Note that a CGLIB-based proxy will be created even if the proxyTargetClass property of the ProxyFactoryBean has been set to false. (Obviously this makes no sense, and is best removed from the bean definition because it is at best redundant, and at worst confusing.)
如果要代理的目标对象的类(以下简称为目标类)不实现任何接口,那么将创建基于cglib的代理。这是最简单的场景,因为JDK代理是基于接口的,而且没有接口意味着不可能使用JDK代理。一个简单地插入目标bean,并通过拦截名称属性指定拦截器列表。注意,即使ProxyFactoryBean的proxyTargetClass属性设置为false,也会创建基于cglib的代理。(显然,这毫无意义,最好从bean定义中删除,因为它至多是冗余的,最糟糕的情况是混乱。)
—————————————————————————————————————————————————————|
Spring使用了JDK和CGLib两种动态代理来实现AOP。
即:
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP ;
2)如果目标对象实现了接口,也可以强制使用CGLIB实现AOP;
3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值