从零开始写javaweb框架笔记18-使框架具备AOP特性

发布时间:2017-6-29 10:05:23编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"从零开始写javaweb框架笔记18-使框架具备AOP特性",主要涉及到从零开始写javaweb框架笔记18-使框架具备AOP特性方面的内容,对于从零开始写javaweb框架笔记18-使框架具备AOP特性感兴趣的同学可以参考一下。

       代理,或者称为Proxy。意思就是你不需要去做,别人代替你去处理。它在程序开发中起到了非常重要的作用,比如传说中的AOP,就是针对代理的一种应用。此外,在设计模式中,还有一个代理模式。在公司里要上外网,要在浏览器设置一个HTTP代理,可见代理无处不在。

      一:简单的代理

     学习由浅到深,先看一个简单的HelloWorld,首先写一个接口:

    

package org.smart4j.framework.test;

/**
 * Created by jack on 2017/6/27.
 */
public interface Hello {
    void sayHello(String name);
}
     上面是一个接口,下面是实现类:

package org.smart4j.framework.test;

/**
 * Created by liangboqun on 2017/6/27.
 */
public class HelloImp implements Hello{
    public void sayHello(String name) {
        System.out.println("Hello ! "+name);
    }
}

       如果要在println方法前面和后面分别需要处理一些逻辑,该怎么做了?把这些逻辑写死在方法里面吗?这样代码肯定不优雅,这时候我们就可以使用代理模式,写一个HelloProxy类,让它去调用HelloImp的sayHello方法,在调用的前后分别进行逻辑处理,代码如下:

     

package org.smart4j.framework.test;

/**
 * Created by liangboqun on 2017/6/27.
 */
public class HelloProxy implements Hello{
    private Hello hello;
    public void sayHello(String name) {
        before();
        hello.sayHello(name);
        after();
    }

    public HelloProxy() {
        this.hello = new HelloImp();
    }
    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }
}

    用HelloProxy类实现了Hello接口,并且在构造方法中new出一个HelloImp类的实例,这样一来,我们就可以在HelloProxy的sayHello方法去调用HelloImp的sayHello方法了。更重要的是,我们还可以在调用的前后分别加上before与after方法,在这两个方法里去实现那些前后逻辑。

      下面是一个测试类,代码如下:

package org.smart4j.framework.test;

/**
 * Created by liangboqun on 2017/6/27.
 */
public class MainTest1 {
    public static void main(String [] args){
        Hello helloProxy = new HelloProxy();
        helloProxy.sayHello("JACK");
    }
}

    运行测试程序,输出如下:


    看是不是很简单,其实这只是入门,并且是静态代理,真正的代理远远没这么简单。


    二:JDK动态代理

   下面是使用JDK提供的动态代理方案,代码如下:

package org.smart4j.framework.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by jack on 2017/6/27.
 * 使用JDK实现动态代理
 */
public class DynamicProxy implements InvocationHandler{
    private Object target;

    public DynamicProxy(Object target) {
        //保存真实的对象
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //通过反射调用函数
        Object result = method.invoke(target,args);
        after();
        //返回函数调用的返回结果
        return result;
    }

    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }
}

      在DynamicProxy类中,定义了一个Object类型的target变量,它就是被代理的目标对象,通过构造函数来初始化。DynamicProxy实现了InvocationHandler接口,那么必须实现该接口的invoke方法,参数是JRE给我们传递的,直接通过反射区invoke method,在调用前后,分别处理before与after,最后将result返回。

     下面是测试代码:

package org.smart4j.framework.test;

import java.lang.reflect.Proxy;

/**
 * Created by jack on 2017/6/27.
 */
public class MainTest1 {
    public static void main(String[] args) {
       /* Hello helloProxy = new HelloProxy();
        helloProxy.sayHello("JACK");*/
       //测试JDK实现的动态代理
        Hello hello = new HelloImp();
        DynamicProxy dynamicProxy = new DynamicProxy(hello);
        Hello helloProxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), dynamicProxy);
        helloProxy.sayHello("jack");
    }
}

    上面代码的意思就是用这个通用的DynamicProxy类去包装HelloImp实例,然后再调用JDK给我们提供的Proxy类的工厂方法new ProxyInstance去动态地创建一个Hello接口的代理类,最后调用这个代理类的sayHello方法。

    运行结果如下:



    注意看测试代码,Proxy.newProxyInstance方法的参数是不是醉啦

    参数1:ClassLoader

    参数2:该实现类的所有接口

    参数3:动态代理对象

    调用完了还要来一个强制类型转换一下。

   这一块需要想办法封装一下,避免再次出现到处是Proxy.newProxyInstance方法的情况。于是将这个DynamicProxy重构一下,代码如下:

package org.smart4j.framework.test;

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

/**
 * Created by jack on 2017/6/27.
 * 使用JDK实现动态代理
 */
public class DynamicProxy implements InvocationHandler{
    private Object target;

    public DynamicProxy(Object target) {
        //保存真实的对象
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //通过反射调用函数
        Object result = method.invoke(target,args);
        after();
        //返回函数调用的返回结果
        return result;
    }

    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

    public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

     在DynamicProxy里添加了一个getProxy方法,无需传入任何参数,将刚才所说的那一块代码放在这个方法中,并且该方法返回一个泛型类型,就不会强制转换类型了。修改测试代码,如下:

package org.smart4j.framework.test;

import java.lang.reflect.Proxy;

/**
 * Created by jack on 2017/6/27.
 */
public class MainTest1 {
    public static void main(String[] args) {
       /* Hello helloProxy = new HelloProxy();
        helloProxy.sayHello("JACK");*/
       //测试JDK实现的动态代理
       /* Hello hello = new HelloImp();
        DynamicProxy dynamicProxy = new DynamicProxy(hello);
        Hello helloProxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), dynamicProxy);
        helloProxy.sayHello("jack");*/
        DynamicProxy dynamicProxy = new DynamicProxy(new HelloImp());
        Hello helloProxy = dynamicProxy.getProxy();
        helloProxy.sayHello("jack");
    }
}

     重构之后,使用简单的几行代码就实现了动态代理了。


     三:CGlib动态代理

     用了这个DynamicProxy以后,还是非常好的,好的地方是接口变了,这个动态代理类不用动。而静态代理就不一样了,接口变了,代理类也要动。但动态代理并不是万灵丹,它也有搞不定的时候,比如要代理一个没有任何接口的类,它就没有用武之地了。这个时候就只能使用CGlib这个类库。在spring和hibernate中都有使用CGlib实现动态代理。它是在一个运行期间动态生成字节码的工具,也就是动态生成代理类了,下面使用CGlib实现动态代理,写一个CGLibProxy:

       

package org.smart4j.framework.test;


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

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by jack on 2017/6/27.
 * 使用CGlib实现动态代理
 */
public class CGLibProxy implements MethodInterceptor{

    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(object,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }
}

        注意:需要引入cglib类库,代码如下:

       

<!--添加cglib类库,实现动态代理-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

     需要实现CGLib给我们提供的MethodInterceptor实现类,并填充intercept方法。方法中最后一个MethodProxy类型的参数proxy需要注意。CGLlib给我们提供的是方法级别的代理,也可以理解为对方法的拦截。这个供暖对我们程序员而言,如同雪中送炭。我们直接调用proxy的invokeSuper方法,将代理的对象obj以及方法参数args传入其中即可。

       与DynamicProxy类似,在CGlibProxy中也添加了一个getProxy方法,便于我们可以快速地获取自动生成的代理对象。下面是测试代码:

package org.smart4j.framework.test;

/**
 * Created by jack on 2017/6/27.
 * CGlib动态代理测试
 *
 */
public class MainTest2 {
    public static void main(String[] args) {
      CGLibProxy cgLibProxy = new CGLibProxy();
      Hello helloProxy = cgLibProxy.getProxy(HelloImp.class);
      helloProxy.sayHello("jack");
    }
}

      通过2行代码就可以返回代理对象,与JDK动态代理不同的是,这里不需要任何的接口信息,对谁都可以生成动态代理对象。

      用2行代码返回代理对象还是有一些多余,不行总是取new这个CGLibProxy对象,最后new一次,以后随时拿随时用,于是我们可以采用单例模式,代码如下:

package org.smart4j.framework.test;


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

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by jack on 2017/6/27.
 * 使用CGlib实现动态代理
 */
public class CGLibProxy implements MethodInterceptor{

    private static CGLibProxy instance = new CGLibProxy();
    public CGLibProxy() {
    }

    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(object,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("Before");
    }
    private void after(){
        System.out.println("After");
    }

    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }

    public static CGLibProxy getInstance(){
        return instance;
    }
}

     测试代码如下:

     

package org.smart4j.framework.test;

/**
 * Created by jack on 2017/6/27.
 * CGlib动态代理测试
 *
 */
public class MainTest2 {
    public static void main(String[] args) {
      Hello helloProxy = CGLibProxy.getInstance().getProxy(HelloImp.class);
      helloProxy.sayHello("jack");
    }
}

     只需一行代码就可以获取代理对象。测试结果如下:

 




上一篇:关于几个统计用的C++方法
下一篇:不同风格样式的文字能够借助差异

相关文章

相关评论

本站评论功能暂时取消,后续此功能例行通知。

一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!

二、互相尊重,对自己的言论和行为负责。

好贷网好贷款