代理模式笔记

1.定义

一个对象本身不做实际操作,而是通过其他对象来得到自己想要的结果

意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强

非常重要的一点就是不用修改源代码,增加代理对象就可以

2.角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现

  • 真实角色 : 被代理的角色

  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
    (可以加一个增强类,在代理类中添加功能用的)

  • 用户 : 使用代理角色来进行一些操作 

3.静态代理

(1)接口

public interface StudentService {
    void save(Student student);
    Student query(Long id);
}

(2)目标类

public class StudentServiceImp implements StudentService{
    @Override
    public void save(Student student) {
        //具体操作就是调用dao层,在数据库种保存学生信息
        System.out.println("保存学生信息");
    }

    @Override
    public Student query(Long id) {
        //具体操作就是调用dao层,根据ID查询学生信息并返回实体类
        //这里我们直接new一个Student返回就好了
        return new Student("小红",12);
    }
}

(3) 增强类(用来增加代理类的功能)

public class DaoTransaction {
    public void start() {
        System.out.println("事务开启");
    }
    public void end() {
        System.out.println("事务结束");
    }
}

 (4)代理类

public class StudentProxy implements StudentService{
    StudentService studentService;
    DaoTransaction daoTransaction;

    public StudentProxy(StudentService studentService, DaoTransaction daoTransaction) {
        this.studentService = studentService;
        this.daoTransaction = daoTransaction;
    }

    @Override
    public void save(Student student) {
        daoTransaction.start();
        studentService.save(student);
        daoTransaction.end();
    }

    @Override
    public Student query(Long id) {
        return studentService.query(id);
    }
}

(5)测试

public class proxyTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        DaoTransaction daoTransaction=new DaoTransaction();
        StudentProxy studentProxy = new StudentProxy(studentService,daoTransaction);
        studentProxy.save(new Student());
    }

静态代理问题

在程序没有运行前,代理关系已经确定

1.不利于代码的扩展 ,比如接口中新添加一个功能,所有实现类都要重新实现

2.代理对象需要创建很多,这种设计很不便
比如:我现在又要代理TeacherService类,功能与StudentSrervice类一致。那么我又要添加一个代理类。

4.动态代理

分类:分为两类,基于接口的动态代理(jdk),基于类的动态代理(cglib)

我们这次主要学习基于接口的动态代理

定义:在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理的目标类。

也就是说,我们在静态代理中写的具体代理类可以消失了,通过反射在运行中生成代理类

1.反射复习---Method类

//可以通过反射得到类的方法,getMethod中第一个参数是获取的方法名字,第二个参数是获取方法的参数
 Method save = StudentService.class.getMethod("save", Student.class);

//通过 方法.invoke 运行方法,第一个参数是运行此方法的对象,第二个参数是运行此方法传入的参数
 Object invoke = save.invoke(new StudentServiceImp(), new Student());
 Object invoke1 = save.invoke(new StudentServiceImp2(), new Student());

2.实现

(1) 接口

public interface StudentService {
    void save(Student student);
    Student query(Long id);
}

(2) 目标类

public class StudentServiceImp implements StudentService{
    @Override
    public void save(Student student) {
        //具体操作就是调用dao层,在数据库种保存学生信息
        System.out.println("保存学生信息");
    }

    @Override
    public Student query(Long id) {
        //具体操作就是调用dao层,根据ID查询学生信息并返回实体类
        //这里我们直接new一个Student返回就好了
        return new Student("小红",12);
    }
}

 (3) 增强类(用来增加代理类的功能)

public class DaoTransaction {
    public void start() {
        System.out.println("事务开启");
    }
    public void end() {
        System.out.println("事务结束");
    }
}

(4) 实现InvocationHandler接口

InvocationHandler接口是什么东西呢?

在反射包 java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
 其中 InvocationHandler 接口(调用处理器) :就一个方法 invoke() 方法 

       invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在invoke()方法中

方法原型: public Object invoke(Object proxy, Method method, Object[] args) 

  • Object proxy:jdk创建的代理对象,无需赋值。
  • Method method:目标类中的方法,jdk提供method对象的
  • Object[]args:目标类中方法的参数,jdk提供的。

可以看出来这个方法的参数不用我们传,是jdk在运行中调用此方法,从而运行的。 

InvocationHandler接口:表示你的代理要干什么。
怎么用:
1.创建类实现接口InvocationHandler 
2.重写invoke()方法,把原来静态代理中代理类要完成的功能,写在这

public class TransactionHandler implements InvocationHandler {
    DaoTransaction daoTransaction;
    Object obj;

    public TransactionHandler(DaoTransaction daoTransaction, Object obj) {
        this.daoTransaction = daoTransaction;
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //proxy就是代理类
        //method就是要实现的方法
        //args是传入方法的参数
        Object ret;
        if("save".equals(method.getName())) {
            daoTransaction.start();
            ret=method.invoke(obj,args);
            daoTransaction.end();
        }else {
            ret=method.invoke(obj,args);
        }

        return ret;
    }
}

(5) 测试

public class proxyTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //增强类
        DaoTransaction daoTransaction=new DaoTransaction();
        //目标类
        StudentService studentService=new StudentServiceImp();

        TransactionHandler transactionHandler = new TransactionHandler(daoTransaction,studentService);

        StudentService o = (StudentService) Proxy.newProxyInstance(StudentServiceImp.class.getClassLoader(), StudentServiceImp.class.getInterfaces(), transactionHandler);

        o.save(new Student());
        o.query(1L);

    }
}

 在这段代码中我们看到Proxy类

Proxy类:核心的对象,创建代理对象。之前创建对象都是new类的构造方法 
现在我们是使用Proxy类的方法,代替new的使用。
方法:静态方法newProxyInstance () 
作用是:创建代理对象,等同于静态代理中的 
StudentProxy studentProxy = new StudentProxy(studentService,daoTransaction);
参数: 

  • ClassLoaderloader类加器,负责向内存中加载对象的。使用反射获取对象的classLoader类a,a.getCalss().getClassLoader(),目标对象的类加载器
  • class<?>[]interfaces:接口,目标对象实现的接口,也是反射获取的。
  • InvocationHandler h:我们自己写的,代理类要完成的功能。

返回值:就是代理对象


5.执行流程

(摘自jdk动态代理1

StudentService o = (StudentService) Proxy.newProxyInstance(StudentServiceImp.class.getClassLoader(), StudentServiceImp.class.getInterfaces(), transactionHandler);

 1.用ClassLoader加载器生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码对象。这个字节对象是怎么产生的呢?

  •  所有生成的类都有对应的class文件,在运行中由jdk动态生成的代理类也是有class文件的。
  • 这个class文件是加载器通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成代理类的字节码。ProxyGenerator这个类存放在sun.misc包下,我们可以通过OpenJDK源码来找到这个类,该类的generateProxyClass()静态方法的核心内容就是去调用generateClassFile()实例方法来生成Class文件。
  • 这个方法生成class文件的方式就是
  • 第一步:收集所有要生成的代理方法
    首先为代理类生成toString, hashCode, equals等代理方法
    遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
  • 第二步:收集所有要为Class文件生成的字段信息和方法信息。
  • 第三步:完成了上面的工作后,开始组装Class文件。
  • 具体可以看博客jdk动态代理2

​ 2.使用Proxy的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler子类传入。

​ 3.返回这个代理类实例对象,因为我们构造的代理类实现了interfaces,所以可以直接转为接口类型


可以通过下面代码得到生成的代理类的代码

 public static void saveProxyClass(String path) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        Class cl = Class.forName("java.lang.reflect.ProxyGenerator");
        Method m =cl.getDeclaredMethod("generateProxyClass",String.class,Class[].class);
        m.setAccessible(true);
        byte[] classFile = (byte[]) m.invoke(null,"$Proxy1", StudentService.class.getInterfaces());
        FileOutputStream out=null;
        out=new FileOutputStream(new File(path+"$Proxy1.class"));
        out.write(classFile);
        out.close();
    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值