成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringBoot 實現動態插拔的 AOP,太實用了!

開發
本文實現熱插拔AOP就在于對advice、advised、advisor、pointcut?概念的理解,這是實現熱插拔AOP的前提。

現在有這么一個需求:就是我們日志的開與關是交給使用人員來控制的,而不是由我們開發人員固定寫死的。大家都知道可以用aop來實現日志管理,但是如何動態的來實現日志管理呢?

aop源碼中的實現邏輯中有這么一個步驟,就是會依次掃描Advice的實現類,然后執行。我們要做的就是自定義一個advice的實現類然后,在用戶想要開啟日志的時候就把advice加到項目中來,關閉日志的時候就把advice剔除就行了。

前置知識

(1) Advice:

org.aopalliance.aop.Advice

“通知”,表示 Aspect 在特定的 Join point 采取的操作。包括 “around”, “before” and “after 等 Advice,大體上分為了三類:BeforeAdvice、MethodInterceptor、AfterAdvice

(2) Advisor:

org.springframework.aop.Advisor

“通知者”,它持有 Advice,是 Spring AOP 的一個基礎接口。它的子接口 PointcutAdvisor 是一個功能完善接口,它涵蓋了絕大部分的 Advisor。

(3) Advised:

org.springframework.aop.framework.Advised

AOP 代理工廠配置類接口。提供了操作和管理 Advice 和 Advisor 的能力。它的實現類 ProxyFactory 是 Spring AOP 主要用于創建 AOP 代理類的核心類。

熱插拔AOP執行核心邏輯

核心實現代碼

(1) 動態管理advice端點實現

@RestControllerEndpoint(id = "proxy")
@RequiredArgsConstructor
public class ProxyMetaDefinitionControllerEndPoint {

    private final ProxyMetaDefinitionRepository proxyMetaDefinitionRepository;


    @GetMapping("listMeta")
    public List<ProxyMetaDefinition> getProxyMetaDefinitions(){
       return proxyMetaDefinitionRepository.getProxyMetaDefinitions();
    }

    @GetMapping("{id}")
    public ProxyMetaDefinition getProxyMetaDefinition(@PathVariable("id") String proxyMetaDefinitionId){
        return proxyMetaDefinitionRepository.getProxyMetaDefinition(proxyMetaDefinitionId);
    }

    @PostMapping("save")
    public String save(@RequestBody ProxyMetaDefinition definition){

        try {
            proxyMetaDefinitionRepository.save(definition);
            return "success";
        } catch (Exception e) {

        }
        return "fail";

    }

    @PostMapping("delete/{id}")
    public String delete(@PathVariable("id")String proxyMetaDefinitionId){
        try {
            proxyMetaDefinitionRepository.delete(proxyMetaDefinitionId);
            return "success";
        } catch (Exception e) {

        }
        return "fail";
    }

}

(2) 利用事件監聽機制捕獲安裝或者卸載插件

@RequiredArgsConstructor
public class ProxyMetaDefinitionChangeListener {

    private final AopPluginFactory aopPluginFactory;

    @EventListener
    public void listener(ProxyMetaDefinitionChangeEvent proxyMetaDefinitionChangeEvent){
        ProxyMetaInfo proxyMetaInfo = aopPluginFactory.getProxyMetaInfo(proxyMetaDefinitionChangeEvent.getProxyMetaDefinition());
        switch (proxyMetaDefinitionChangeEvent.getOperateEventEnum()){
            case ADD:
                aopPluginFactory.installPlugin(proxyMetaInfo);
                break;
            case DEL:
                aopPluginFactory.uninstallPlugin(proxyMetaInfo.getId());
                break;
        }

    }
}

(3) 安裝插件

public void installPlugin(ProxyMetaInfo proxyMetaInfo){
        if(StringUtils.isEmpty(proxyMetaInfo.getId())){
            proxyMetaInfo.setId(proxyMetaInfo.getProxyUrl() + SPIILT + proxyMetaInfo.getProxyClassName());
        }
        AopUtil.registerProxy(defaultListableBeanFactory,proxyMetaInfo);
    }

(4) 安裝插件核心實現

public static void registerProxy(DefaultListableBeanFactory beanFactory,ProxyMetaInfo proxyMetaInfo){
        AspectJExpressionPointcutAdvisor advisor = getAspectJExpressionPointcutAdvisor(beanFactory, proxyMetaInfo);
        addOrDelAdvice(beanFactory,OperateEventEnum.ADD,advisor);

    }

    private static AspectJExpressionPointcutAdvisor getAspectJExpressionPointcutAdvisor(DefaultListableBeanFactory beanFactory, ProxyMetaInfo proxyMetaInfo) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) builder.getBeanDefinition();
        beanDefinition.setBeanClass(AspectJExpressionPointcutAdvisor.class);
        AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
        advisor.setExpression(proxyMetaInfo.getPointcut());
        advisor.setAdvice(Objects.requireNonNull(getMethodInterceptor(proxyMetaInfo.getProxyUrl(), proxyMetaInfo.getProxyClassName())));
        beanDefinition.setInstanceSupplier((Supplier<AspectJExpressionPointcutAdvisor>) () -> advisor);
        beanFactory.registerBeanDefinition(PROXY_PLUGIN_PREFIX + proxyMetaInfo.getId(),beanDefinition);

        return advisor;
    }

(5) 卸載插件

public void uninstallPlugin(String id){
        String beanName = PROXY_PLUGIN_PREFIX + id;
        if(defaultListableBeanFactory.containsBean(beanName)){
           AopUtil.destoryProxy(defaultListableBeanFactory,id);
        }else{
            throw new NoSuchElementException("Plugin not found: " + id);
        }
    }

(6) 卸載插件核心實現

public static void destoryProxy(DefaultListableBeanFactory beanFactory,String id){
        String beanName = PROXY_PLUGIN_PREFIX + id;
        if(beanFactory.containsBean(beanName)){
            AspectJExpressionPointcutAdvisor advisor = beanFactory.getBean(beanName,AspectJExpressionPointcutAdvisor.class);
            addOrDelAdvice(beanFactory,OperateEventEnum.DEL,advisor);
            beanFactory.destroyBean(beanFactory.getBean(beanName));
        }
    }

(7) 操作advice實現

public static void addOrDelAdvice(DefaultListableBeanFactory beanFactory, OperateEventEnum operateEventEnum,AspectJExpressionPointcutAdvisor advisor){
        AspectJExpressionPointcut pointcut = (AspectJExpressionPointcut) advisor.getPointcut();
        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            Object bean = beanFactory.getBean(beanDefinitionName);
            if(!(bean instanceof Advised)){
                if(operateEventEnum == OperateEventEnum.ADD){
                    buildCandidateAdvised(beanFactory,advisor,bean,beanDefinitionName);
                }
                continue;
            }
            Advised advisedBean = (Advised) bean;
            boolean isFindMatchAdvised = findMatchAdvised(advisedBean.getClass(),pointcut);
            if(operateEventEnum == OperateEventEnum.DEL){
                if(isFindMatchAdvised){
                    advisedBean.removeAdvice(advisor.getAdvice());
                    log.info("########################################## Remove Advice -->【{}】 For Bean -->【{}】 SUCCESS !",advisor.getAdvice().getClass().getName(),bean.getClass().getName());
                }
            }else if(operateEventEnum == OperateEventEnum.ADD){
                if(isFindMatchAdvised){
                    advisedBean.addAdvice(advisor.getAdvice());
                    log.info("########################################## Add Advice -->【{}】 For Bean -->【{}】 SUCCESS !",advisor.getAdvice().getClass().getName(),bean.getClass().getName());
                }
            }


        }
    }

熱插拔AOP演示示例

(1) 創建一個service

@Service
@Slf4j
public class HelloService implements BeanNameAware, BeanFactoryAware {
    private BeanFactory beanFactory;

    private String beanName;

    @SneakyThrows
    public String sayHello(String message) {
        Object bean = beanFactory.getBean(beanName);
        log.info("============================ {} is Advised : {}",bean, bean instanceof Advised);
        TimeUnit.SECONDS.sleep(new Random().nextInt(3));
        log.info("============================ hello:{}",message);

        return "hello:" + message;

    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

(2) 創建一個controller

@RestController
@RequestMapping("hello")
@RequiredArgsConstructor
public class HelloController {

    private final HelloService helloService;

    @GetMapping("{message}")
    public String sayHello(@PathVariable("message")String message){
        return helloService.sayHello(message);
    }
}

(3) 準備一個日志切面jar

切面內容為

@Slf4j
public class LogMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object result;
        try {
            result = invocation.proceed();
        } finally {
           log.info(">>>>>>>>>>>>>>>>>>>>>>>>TargetClass:【{}】,method:【{}】,args:【{}】",invocation.getThis().getClass().getName(),invocation.getMethod().getName(), Arrays.toString(invocation.getArguments()));
        }

        return result;
    }
}

(4) 測試

場景一:未添加切面時 瀏覽器訪問:http://localhost:8080/hello/zhangsan 觀察控制臺

場景二:通過postman動態操作代理

① 新增代理

觀察控制臺:

########################################## BuildCandidateAdvised -->【com.github.lybgeek.aop.test.hello.service.HelloService】 With Advice -->【com.github.lybgeek.interceptor.LogMethodInterceptor】 SUCCESS !

此時瀏覽器訪問:http://localhost:8080/hello/zhangsan

再次觀察控制臺:

出現了切面日志信息,說明代理生效。

② 刪除代理

觀察控制臺:

########################################## Remove Advice -->【com.github.lybgeek.interceptor.LogMethodInterceptor】 For Bean -->【com.github.lybgeek.aop.test.hello.service.HelloService$$EnhancerBySpringCGLIB$$7bc75aa3】 SUCCESS !

此時瀏覽器訪問:http://localhost:8080/hello/zhangsan

再次觀察控制臺:

此時沒有出現切面日志信息,說明代理刪除成功

總結

本文實現熱插拔AOP就在于對advice、advised、advisor、pointcut概念的理解,這是實現熱插拔AOP的前提,其次就是對自定義classloader也需要有一定的了解,因為我們jar不一定從classpath底下加載,也有可能來源其他地方,比如遠程鏈接啥的,最后就是把原先spring自動幫我們實現aop,我們利用相關的api,自己手動實現一遍,示例代碼的api只是利用spring api其中一種實現方式,它還有多種實現方式,比如可以利用TargetSource,感興趣的朋友,也可以自己實現一把。

至于那個代理增刪改查端點contoller,是我之前看springcloud gateway的路由定位器端點源碼,一直沒找到機會實現一下,就把他搬來這個示例實現一把,加深一下印象。

責任編輯:趙寧寧 來源: 碼猿技術專欄
相關推薦

2021-05-19 15:06:44

MySQL數據庫命令

2022-04-19 13:07:25

SchedulePython

2022-03-18 09:25:49

Python圖片PDF

2024-12-27 08:43:17

2022-12-12 09:01:03

2020-06-29 11:05:26

GitHub代碼開發者

2015-08-05 13:24:15

2020-09-04 09:32:54

蘇寧數據治理

2022-09-14 07:36:34

PowerToys微軟

2023-12-22 09:14:48

EDA數據分析探索性數據分析

2022-06-14 08:50:18

Python交互式儀表板代碼

2023-09-27 08:50:57

Serverles編寫運維

2009-06-22 15:10:00

java 編程AOP

2024-08-09 08:46:00

Springjar 包YAML

2018-01-31 10:24:45

熱插拔原理服務器

2023-05-06 16:26:28

??Vue??UI組件

2023-03-30 07:48:46

接口鑒權SpringBoot

2017-10-26 21:08:15

Tomcat可插拔SCI

2024-09-02 00:27:51

SpringAOP自定義

2025-05-21 01:00:55

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久国产精品视频 | 国产精品欧美一区二区 | 超碰操| 亚洲国产精品suv | 一区二区视频 | 综合国产 | 精品丝袜在线 | 91资源在线 | 亚洲欧美在线观看 | 午夜男人的天堂 | 青青久在线视频 | 色综合久久久 | 久久午夜精品 | 国产日韩一区二区三区 | 欧美激情一区二区 | 一区 | 在线视频国产一区 | 一区二区成人 | 国产二区精品视频 | 欧洲精品久久久久毛片完整版 | 天堂男人av | 成年人免费在线视频 | 国产亚洲精品久久19p | 一区二区在线观看av | 最近免费日本视频在线 | 亚洲精品99久久久久久 | 性高朝久久久久久久3小时 av一区二区三区四区 | 午夜tv免费观看 | 亚洲国产精品第一区二区 | www.狠狠干| 国产免费观看久久黄av片涩av | 亚洲精品永久免费 | 91久久久久久久久久久久久 | 99久久国产精 | 97人人干| 欧美国产日韩一区二区三区 | 精品成人在线 | 日韩中文字幕一区二区 | 精品一区二区三区四区外站 | 噜久寡妇噜噜久久寡妇 | 日韩在线视频免费观看 |