很难出错的spring5(六)—— AOP预备知识:静态代理和动态代理

一、静态代理

        在学习多线程的时候我们学过静态代理,new Thread(new 线程类()).start()的原理就是静态代理。在线程类的run方法里面,我们只写了和线程无任何关系的简单逻辑,new一个对象丢给Thread对象,就能跑起来了!如何创建的线程,如何实现CPU调度线程转为执行状态,都是Thread对象帮我们做的。

https://blog.csdn.net/qq_44886213/article/details/127648846

        代理模式除了简单地理解为“婚庆公司”、“租房中介”,“经纪人”担当黑脸角色替白脸做事,还有什么作用呢?看下图,代理模式可以在不改变原有代码的基础上增加打印日志等所有模块都需要的、统一的、辅助性的功能,这也是AOP的底层实现,在AOP底层到处能看到以代理的方式扩充程序功能。

        怎么理解可以用代理模式实现“在不改变原有代码的基础上扩充程序的功能” 呢?还是看我们之前写的结婚例子。原本的程序只有新娘和新郎的行为,而且这个程序已经上线被用户使用了,是万万不能改动的,那如果想要添加一段代码实现“婚礼前打一段日志”,可以写一个代理类,把添加的代码写到代理类中(不知道应该这样理解,还是说有了代理以后,增加功能直接在代理里写就行了,详见下面动态代理增加log的例子)。

缺点:一个真实对象就要产生一个代理对象,例如要开3个线程,就要new出来3个Thread对象

二、动态代理

首先,静态代理的优点动态代理全都有,并且修正了其缺点(从后面的例子中,还是一个真实对象就要产生一个代理对象呀???)。一个代理对象直接代理一个接口,可以代理多个类,只要类实现了这个接口就行。

1. springAOP的机制是动态代理,动态代理的机制是反射,这也是动态代理难以理解的地方。

2. 代理类是Proxy.newProxyInstance() 动态生成的,不是我们直接写好的

3. 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口: JDK 原生动态代理(我们学这个)
  • 基于类:cglib
  • java字节码实现:javasist

需要了解两个类

  • java.lang.reflect.Proxy(类)
    • 静态方法Proxy.newProxyInstance() 动态生成一个代理实例!
  • java.lang.reflect.InvocationHandler(接口):调用处理程序
    • 要重写invoke()方法

2.1 例子

 

 接口:定义增删改查4个抽象方法

package com.kuang.service;

/**
 * 业务接口
 */
public interface IService {
    void add();
    void delete();
    void modify();
    void search();
}

 接口实现类(一):增删改查春晚小品的业务类

package com.kuang.service;

/**
 * 增删改查春晚小品的业务类
 */
public class ComedyServiceImpl implements IService{
    public void add() {
        System.out.println("增加一个春晚小品");
    }

    public void delete() {
        System.out.println("删除一个春晚小品");
    }

    public void modify() {
        System.out.println("修改一个春晚小品");
    }

    public void search() {
        System.out.println("查找一个春晚小品");
    }
}

 接口实现类(二):增删改查春晚歌曲的业务类

package com.kuang.service;

/**
 * 增删改查春晚歌曲的业务类
 */
public class SongServiceImpl implements IService{
    public void add() {
        System.out.println("增加一个春晚歌曲");
    }

    public void delete() {
        System.out.println("删除一个春晚歌曲");
    }

    public void modify() {
        System.out.println("修改一个春晚歌曲");
    }

    public void search() {
        System.out.println("查找一个春晚歌曲");
    }
}

InvocationHandler的实现类:可以看作动态代理的模板

package com.kuang.service;

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

/**
 * 处理类的【模板】
 */
public class MyInvocationHandler implements InvocationHandler {
    //1.被代理的角色的接口
    IService target;
    public void setService(IService target) {
        this.target = target;
    }

    //2.动态生成代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //3.处理代理对象,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        return result;
    }

    //4.代理自己要干的一系列事情,写在这里
}

客户端:

package com.kuang.service;

import java.lang.reflect.Method;

public class Client {
    public static void main(String[] args) {
        ComedyServiceImpl comedyService = new ComedyServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler();
        //设置被代理的真实角色
        handler.setService(comedyService);
        //动态生成代理对象
        IService proxy = (IService) handler.getProxy();
        proxy.add();  //--->调用invoke
        proxy.delete();
        proxy.modify();
        proxy.search();

        System.out.println("------------------");

        SongServiceImpl songService = new SongServiceImpl();
        //设置被代理的真实角色
        handler.setService(songService);
        //动态生成代理对象
        proxy = (IService) handler.getProxy();  //这不也是重新new了一个代理对象吗??
        proxy.add();
        proxy.delete();
        proxy.modify();
        proxy.search();
    }
}

 考虑一个问题,使用动态代理,如果要增加一个打印日志业务,怎么加呢?

 应该把增加的业务写在InvocationHandler实现类MyInvocationHandler的invoke方法里。

我们依然调用add()方法,但是多做了两件事情。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值