AOP底层分析-代理模式

代理模式分类

  1. 静态代理
  2. 动态代理
    一、代理的基本构成:
    代理模式上,基本上有Subject角色,RealSubject角色,Proxy角色。其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的Request请求,调用realsubject 对应的request功能来实现业务功能,自己不真正做业务。
  • 抽象角色Subject:一般使用接口或抽象类解决
  • 真实角色RealSubject:被代理角色
  • 代理角色Proxy:代理真实角色,一般会增加一些附属操作
  • 客户Client:访问代理角色的人
    二、静态代理
    举例:房东租房
    抽象角色Subject
public interface Rent {
    public void Rent();
}

真实角色RealSubject

public class Host implements Rent{
    public void Rent() {
        System.out.println ("房东要出租房子");
    }
}

代理角色Proxy

public class Proxy implements Rent{
    private Host host;
    public Proxy() {
    }
    public Proxy(Host host) {
        this.host = host;
    }
    //签租赁合同-公共业务
    public void Contract()
    {
        System.out.println ("签租赁合同");
    }
    //收中介费-公共业务
    public void fee(){
        System.out.println ("收中介费");
    }
    //看房-公共业务
    public void SeeHouse(){
        System.out.println ("中介带你看房");
    }
    public void Rent() {
        host.Rent ();
        SeeHouse ();
        fee ();
        Contract ();
    }
}

客户Client

public class Client {
    public static void main(String[] args) {
        Host host = new Host ();  //房东要租房子
        Proxy proxy = new Proxy (host);   //代理  中介帮房东租房子(***代理角色一般会有一些附属操作)
        //不用面对房东直接找中介租房即可
        proxy.Rent ();
    }
}

举例:用户增删改查
抽象角色Subject

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

真实角色RealSubject

public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println ("增加了一个用户");
    }

    public void delete() {
        System.out.println ("删除了一个用户");
    }

    public void update() {
        System.out.println ("修改了一个用户");
    }

    public void query() {
        System.out.println ("查询了一个用户");
    }
}
/*改动原有逻辑代码是大忌
* */

代理角色Proxy

public class UserServiceProxy implements UserService{
    UserService userService;

    public UserServiceProxy() {

    }

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    public void add() {
        log ("add");
        userService.add ();
    }

    public void delete() {
        log ("delete");
        userService.delete ();
    }

    public void update() {
        log("update");
        userService.update ();
    }

    public void query() {
        log("query");
        userService.query ();
    }
    public void log(String message){
        System.out.println ("[DEBUG]:使用了"+message+"方法");
    }
}

客户Client

public class Client {
    public static void main(String[] args) {
        UserService userService=new UserServiceProxy (new UserServiceImpl ());
        userService.add ();
    }
}

静态代理实现步骤

  1. 编写接口
  2. 编写实现接口的真实角色
  3. 编写实现接口的代理角色,需要有一个属性可以调用真实角色
  4. 客户端访问代理角色,传入真实角色参数

静态代理优点:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共业务
  • 公共业务实现交给代理角色,实现业务的分工
  • 公共业务发生扩展时,方便集中管理
    静态代理缺点:
    一个真实角色就会产生一个代理角色,代码量翻倍,开发效率低
    解决办法->动态代理

三、Srping框架的AOP技术底层也是采用的动态代理技术,通过反射(动态加载类)动态代理
代理的方式提供了三种
1. 基于JDK的动态代理(Proxy-代理,InvocationHandler-调用处理程序)

必须是面向接口的,只有实现了具体接口的类才能生成代理对象

抽象角色Subject

public interface Rent {
    public void Rent();
}

真实角色RealSubject

public class Host implements Rent {
    public void Rent() {
        System.out.println ("房东要出租房子");
    }
}

/*改动原有逻辑代码是大忌
* */

代理角色Proxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*处理程序*/
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //处理代理实例(被代理的人)并返回结果  真正执行的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理机制就是通过反射实现
       seeHouse ();
       Object invoke = method.invoke (rent, args);//通过反射调用真实角色方法
       fee ();
       return invoke;
    }
    //生成得到代理类
    public Object getProxy()
    {
       return Proxy.newProxyInstance (this.getClass ().getClassLoader (), rent.getClass ().getInterfaces (), this);
    }
    //公共业务类
    public void seeHouse()
    {
        System.out.println ("看房子");
    }
    public void fee()
    {
        System.out.println ("收中介费");
    }
}

客户Client

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host=new Host ();
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler ();
        //通过调用程序来设置我们要调用的接口对象
        pih.setRent (host);
        Rent proxy = (Rent) pih.getProxy ();  //这里的proxy就是动态生成的,并没有写
        proxy.Rent ();
    }
}


2. 基于CGLIB动态代理

对于没有实现接口的类,也可以产生代理,产生这个类的子类的方式
为什么不直接使用Java动态代理,而要使用CGLIB呢?
CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了

在JDK动态代理中提供一个Proxy类来创建代理类,而在CGLib动态代理中也提供了一个类似的类Enhancer;使用的CGLib版本是2.2.2,不同的版本有点小差异,建议用3.x版本的
首先要导入cglib的依赖

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
</dependency>

抽象角色Subject

public interface Rent {
    public void Rent();
}

真实角色RealSubject

public class Host implements Rent {
    public void Rent() {
        System.out.println ("房东要出租房子");
    }
}

/*改动原有逻辑代码是大忌
* */

代理角色Proxy

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


public class CglibProxy implements MethodInterceptor{


    public Object getRentProxy(Class clazz){
        //Enhancer是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多
        Enhancer enhancer = new Enhancer();
        //告诉cglib,生成的子类需要继承那个类
        enhancer.setSuperclass(clazz);
        //设置回调
        enhancer.setCallback(this);
        //加载到jvm中,并返回代理对象
        return enhancer.create();
    }

    //第一个是代理对象,租房子这里是房东

    //第二个是我们要调用的房东租房子的rent方法,

    //第三个是房东租房子rent方法需要的参数

    //第四个是代理对象的方法对象,它可以调用rent的方法,因为cglib实现的动态代理,代理对象和真实对象之间是父子关系
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGlib房屋中介开始执行。。。");
        methodProxy.invokeSuper(o,objects);
        System.out.println("CGlib房屋中介结束执行。。。");
        return null;
    }
}

客户Client

public class Client {
    //java动态代理是利用反射机制生成一个实现被代理对象的接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    //cglib动态代理利用asm开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
    //执行者 CGlibAgency
    //被代理对象 Programmer
    public static void main(String[] args) {
        Host  host= ( Host) new CglibProxy ().getRentProxy(new  Host().getClass());
        host.Rent ();
    }
}

3.java字节码:javasist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

第三种不常用就不做过多介绍

posted @ 2021-11-27 17:00  一刹流云散  阅读(75)  评论(0编辑  收藏  举报