spring代理模式

代理模式优势及意义:1.安全上,能够屏蔽客户端访问真是对象。2.可以在目标对象实现的基础上,增强额外的功能操作,即功能扩展(相当于明星和经纪人)。3.系统性能上,可以对真是对象进行延时加载,从而达到按需分配的设计思想。(@lazy对真正需要加载数据时才加载。这样节省资源分配;ioc管理bean对象时,都是通过代理模式来管理对象)

 

定义:给指定的目标对象提供了一种通过代理对象访问的方式。

代理模式是一个结构化的设计模式。

 

结构图:

 

      静态代理实例:

1.

package propxy;

//接口

public interface ByCar {

  void buyCar();

}

2.

package propxy;

//目标对象

public class Person implements ByCar{

    @Override

    @ask(askTr="i have 15000$")

    public void buyCar() {

        System.out.println("i want by care;");

    }

}

3.

package propxy;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

//对目标对象增强   采用注解方式

public @interface ask {

  String askTr();

}

 

4.

package propxy;

//代理对象

public class ProxyBy implements ByCar {

  // 目标对象的引用

  private ByCar bc;

  public ProxyBy(ByCar bc) {

      super();

      this.bc = bc;

  }

  @Override

  public void buyCar() {

      // 对目标对象增强

      String askTr = null;

      try {

  askTr=bc.getClass().getMethod("buyCar")

.getAnnotatio(ask.class).askTr();

      } catch (Exception e) {

          e.printStackTrace();

      }

      System.out.println(askTr);

      bc.buyCar();

  }

}

 

5.

package propxy;

public class test {

  public static void main(String[] args) {

      ByCar byCaryCar=new ProxyBy(new Person());

      byCaryCar.buyCar();

  }

}

 

静态代理常用环境:项目中的三层架构就是静态代理;controller层调用service层;service层调用dao层;就是典型的静态代理;

 

  1. JDK动态代理  2.cglib代理
    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
    2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

 

动态代理:通过字节码重组的方式动态的创建proxy类实例和proxy实例;生成的class文件就是所谓的字节码;如xx.getclass();

 

Jdk动态代理:

  1. 声明subject接口,且realsubject必须实现该接口
  2. 必须实现invocationhandler接口,且覆盖invoke方法
  3. 可以通过jdk提供的proxy.newproxyinstance静态来构造代理对象

实例:

package propxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

//实现invocationhandler 重写invoke()方法

public class DynamicProxy implements InvocationHandler{

  private ByCar bc;

  @SuppressWarnings("unchecked")

  public <T> T getProxy(ByCar bc) {

      this.bc=bc;

       //通过jdk自带静态的方法 proxy.newproxyinstance() 来生成代理对象

      //通过字节码重组的方式动态的生成一个class字节码,在通过class字节码反射的方式  创建一个对象

      return (T) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), bc.getClass().getInterfaces(), this);

  }

  @Override

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

//目标对象增强

      String askTr = bc.getClass().getMethod("buyCar").getAnnotation(ask.class).askTr();

      System.out.println(askTr);

//反射机制

      return method.invoke(bc, args);

  }

}

 

调用:

public static void main(String[] args) {

        ByCar byCar =new DynamicProxy().getProxy(new Person());

        byCar.buyCar();

}

 

Cglib动态代理:通过jvm(java虚拟机)中,通过动态的组装当前类的一个子类来实现的动态代理;

需引入cglib的依赖插件;

 

三大步骤:

 

  1. 引入cglib插件;
  2. 实现methodinterceptor  用于拦截  重写intercept方法
  3. 通过Enhancer enhancer=new Enhancer();  enhancer.create() 创建代理对象
  4. 实例:

1.

package propxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

public class DynamicCglib implements MethodInterceptor{

@SuppressWarnings("unchecked")

public <T> T GetInstances(Class<?> clazz) {

  //通过传入的字节码文件(clazz) 从新生成一个字节码文件继承于它 

  Enhancer enhancer=new Enhancer();

  enhancer.setSuperclass(clazz);

  enhancer.setCallback(this);

  return (T) enhancer.create();

}

  @Override

  public Object intercept(Object obj, Method method, Object[] objs, MethodProxy methodProxy) throws Throwable {

      System.out.println("方法前--------------操作");

      Object invokeSuper = methodProxy.invokeSuper(obj, objs);

      System.out.println("方法后--------------操作");     

return invokeSuper;

  }

}

2.

package propxy;

public class User {

    public void eat() {

        System.out.println("user:eat............");

    }

}

       调用:

package propxy;

public class test {

    public static void main(String[] args) {

        //静态

        ByCar byCaryCar=new ProxyBy(new Person());

        byCaryCar.buyCar();

        //动态jdk

        ByCar byCar =new DynamicProxy().getProxy(new Person());

        byCar.buyCar();

        //动态cglib

        User obj = new DynamicCglib().GetInstances(User.class);

        obj.eat(); 

    }

}

Jdk动态代理与cglib的区别:1.如果bean实例没有实现某一接口 就采用cglib代理模式;如果bean实例实现了某一接口,spring就会采用jdk动态代理模式;

                           2.cglib采用的是继承于原来的一个类;调用目标对象的方法,不是采用的反射机制,而是采用的缓存fast机制;而jdk代理是采用实现接口的方式,调用目标对象的方法是采用的是反射机制

 

Jdk动态代理与cglib的优劣:1.cglib的动态代理不需要目标对象实现任何接口,但是如果目标对象里的方法是final时,就不能实现动态代理;jdk动态代理需要目标对象继承某一接口,没有方法final的限制;

2.cglib动态代理采用的缓存fast机制,它为每个方法体编了一个记号,像用索引的方式去快速调用,而jdk采用的反射机制的调用该方法体;所以cglib动态代理在jvm编译生成class文件的过程中较于jdk动态代理要慢一些;但是调用方法体要快一些;jdk采用反射机制的去调用方法体 是比较耗费资源的;

 

在项目中:service接口对象往往采用的是cglib的动态代理  而mybatis里的mapper接口对象是采用的jdk动态代理的方式;

 

Spring的aop采用面向切面编程,代理模式在其中表现的淋淋尽致,比如我们放在service层方法上的注解如@transition 我们都知道是事物注解;那spring是怎么来处理它的呢? 在我们代理模式中为目标对象增强其功能,我们是不是会定义一个注解,放在目标对象方法上;在我们通过代理模式调用目标对象方法时通过方法上定义的注解 我们可以在方法前后做一些操作 就是对目标对象方法的一些增强;

比如  @transition,我们都是放在service层的方法上,而service层采用的是cglib动态代理的方式。我们在调用目标对象的方法时通过拦截器transitioninterceptor捕获到方法上有@transition的注解(transitioninterceptor肯定实现了methodinterceptor方法拦截器),那么,spring就会在目标方法前做事物开启操作,然后动态代理的方式调用目标对象方法(方法里肯定就是些mapper里相关的事物操作啦  我这里用的是mybatis框架 注意:mapper采用的是jdk的动态代理方式),方法后

如果有异常就做事物回滚操作;这些都是通过代理模式来实现的;

service层调用采用的cglib代理方式图解:代理名字都含有cglib  通过上面实例 debug模式可验证

mybatis里mapper代理模式图解:代理名字都是$proxy开头  通过上面实例 debug模式可验证

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章