用自己的电脑做服务器弄网站,wordpress网站主修改,个人网站认证,洛阳网站改版自定义 spring-boot-starter 暴露钩子 1、前置工作#xff1a;自定义一个 spring-boot-starter1.1、pom文件1.2、starter 封装的接口1.3、starter 的配置类1.4、starter 的 spring.factories 2、方法一#xff1a;ApplicationContext 实现2.1、MyService的实现类2.2、事件类及… 自定义 spring-boot-starter 暴露钩子 1、前置工作自定义一个 spring-boot-starter1.1、pom文件1.2、starter 封装的接口1.3、starter 的配置类1.4、starter 的 spring.factories 2、方法一ApplicationContext 实现2.1、MyService的实现类2.2、事件类及泛型实体2.3、使用钩子 3、方法二观察者模式 ApplicationListener 实现3.1、定义监听者接口类3.2、MyService 的实现类3.3、定义 ApplicationListener3.4、MyListener 加入 spring.factories 文件3.5、使用钩子 最近看了Springboot 相关的源码正好项目上有需求需要对自定义的 spring-boot-starter 封装的方法暴露出钩子。对封装的方法做一些前置或后置的扩展所以简单写个demo 记录一下。 这里用两种方法实现上面的需求一种是使用 ApplicationContext 的事件发布机制实现。另一种是自己用 观察者模式 ApplicationListener 实现。话不多说直接上代码。 1、前置工作自定义一个 spring-boot-starter 1.1、pom文件 ?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.demo/groupIdartifactIdmy-spring-boot-starter/artifactIdversion1.0-SNAPSHOT/versionpackagingjar/packagingpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetjava.version1.8/java.versionproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncoding/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactIdversion2.3.5.RELEASE/version/dependency!--包含自动配置的代码--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-autoconfigure/artifactIdversion2.1.5.RELEASE/version/dependency!--非必须编写配置文件时会有提示--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactIdversion2.6.8/versionoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactIdversion2.3.5.RELEASE/version/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion1.18.16/versionoptionaltrue/optional/dependency/dependencies/project1.2、starter 封装的接口 package com.demo.server;public interface MyService {// 该方法采用ApplicationContext 实现钩子暴露public void methodOne();public void methodTwo();} 1.3、starter 的配置类 package com.demo;import com.demo.server.impl.MyServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class MyStarterConfig {BeanConditionalOnMissingBean(MyServiceImpl.class)public MyServiceImpl myServiceImpl() {return new MyServiceImpl();}} 1.4、starter 的 spring.factories spring.factories 文件在 resources\META-INF 目录下
org.springframework.boot.autoconfigure.EnableAutoConfiguration\
com.demo.MyStarterConfig2、方法一ApplicationContext 实现 2.1、MyService的实现类 package com.demo.server.impl;import com.demo.entity.MethodOneAfter;
import com.demo.entity.MethodOneBefore;
import com.demo.event.PostHandleEvent;
import com.demo.server.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;Slf4j
public class MyServiceImpl implements MyService {Autowiredprivate ApplicationContext applicationContext;Overridepublic void methodOne() {MethodOneBefore before new MethodOneBefore();applicationContext.publishEvent(new PostHandleEventMethodOneBefore(before));log.info(执行 - MyServiceImpl.methodOne());MethodOneAfter after new MethodOneAfter();applicationContext.publishEvent(new PostHandleEventMethodOneAfter(after));}} 2.2、事件类及泛型实体 package com.demo.event;import org.springframework.context.ApplicationEvent;// 订阅一个事件类
public class PostHandleEventTEntity extends ApplicationEvent {private TEntity event;public PostHandleEvent(Object source) {super(source);this.event (TEntity) source;}public TEntity getEvent() {return this.event;}} package com.demo.entity;import lombok.Data;
// 前置钩子泛型实体
Data
public class MethodOneAfter {private String name my name is MethodOneAfter;
}package com.demo.entity;import lombok.Data;
// 后置钩子泛型实体
Data
public class MethodOneBefore {private String name my name is MethodOneBefore;
}2.3、使用钩子 先在项目的pom文件中引入自定义 starter 包的 依赖 dependencygroupIdcom.demo/groupIdartifactIdmy-spring-boot-starter/artifactIdversion1.0-SNAPSHOT/version/dependency然后监听 ApplicationContext 发布的事件即可
package com.demo.handle;import com.demo.entity.MethodOneAfter;
import com.demo.entity.MethodOneBefore;
import com.demo.event.PostHandleEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;Slf4j
Component
public class MyServiceHandle {EventListener// Async 不加这个注解就是同步的默认同步。加上Async代表异步执行public void methodOneBefore(PostHandleEventObject postHandleEvent) {if (postHandleEvent.getEvent() instanceof MethodOneBefore) {MethodOneBefore methodOneBefore (MethodOneBefore) postHandleEvent.getEvent();log.info(执行 - PostHandleEvent.methodOneBefore, name {},methodOneBefore.getName());} else if (postHandleEvent.getEvent() instanceof MethodOneAfter) {MethodOneAfter methodOneAfter (MethodOneAfter) postHandleEvent.getEvent();log.info(执行 - PostHandleEvent.methodOneAfter, name {},methodOneAfter.getName());}}} 调用 自定义 starter 中的 methodOne() 方法
package com.demo.controller;import com.demo.server.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;RestController
public class TestController {Autowiredprivate MyService myService;GetMapping(/methodOne)public void methodOne() {myService.methodOne();} 执行结果
2023-09-10 19:01:35.455 MyServiceHandle - 执行 - PostHandleEvent.methodOneBefore, name my name is MethodOneBefore
2023-09-10 19:01:35.456 impl.MyServiceImpl - 执行 - MyServiceImpl.methodOne()
2023-09-10 19:01:35.458 MyServiceHandle - 执行 - PostHandleEvent.methodOneAfter, name my name is MethodOneAfter3、方法二观察者模式 ApplicationListener 实现 这种方法只需要在项目中实现 MyServiceListener 接口即可达到 调用钩子的效果。并且可以选择性的实现 钩子方法。需要注意的是实现 MyServiceListener 接口的实现类需要添加 Component 注解。
3.1、定义监听者接口类 package com.demo.listener;// MyService类的监听类用来实现监听者模式
public interface MyServiceListener {public default void methodTwoBefore(String name) {}public default void methodTwoAfter(String name) {}}3.2、MyService 的实现类 package com.demo.server.impl;import com.demo.listener.MyServiceListener;
import com.demo.server.MyService;
import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;Slf4j
public class MyServiceImpl implements MyService {// 监听者集合private ListMyServiceListener listeners new ArrayList();// 添加监听者public void addListener(MyServiceListener myServiceListener) {this.listeners.add(myServiceListener);}Overridepublic void methodTwo() {String name my name is methodTwo();methodTwoBefore(name);log.info(执行 - MyServiceImpl.methodTwo());methodTwoAfter(name);}/*** 通知所有观察者* param name*/public void methodTwoBefore(String name) {for (MyServiceListener myServiceListener : this.listeners) {myServiceListener.methodTwoBefore(name);}}/*** 通知所有观察者* param name*/public void methodTwoAfter(String name) {for (MyServiceListener myServiceListener : this.listeners) {myServiceListener.methodTwoAfter(name);}}} 3.3、定义 ApplicationListener 这里监听 ContextRefreshedEvent 节点在服务启动的 ContextRefreshedEvent 节点将所有 实现 MyServiceListener 接口的实现类加到 MyServiceImpl 业务实现类。
package com.demo.listener;import com.demo.server.impl.MyServiceImpl;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;import java.util.Map;public class MyListener implements ApplicationListenerContextRefreshedEvent {Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {ApplicationContext applicationContext contextRefreshedEvent.getApplicationContext();// 获取 BeanFactory 实例ListableBeanFactory beanFactory (ListableBeanFactory ) applicationContext.getAutowireCapableBeanFactory();// 获取接口 A 的所有实现类MapString, MyServiceListener beansOfType beanFactory.getBeansOfType(MyServiceListener.class);MyServiceImpl myService beanFactory.getBean(MyServiceImpl.class);// 遍历监听接口的实现类,将监听者放到MyService业务实现类中for (Map.EntryString, MyServiceListener entry : beansOfType.entrySet()) {myService.addListener(entry.getValue());}}} 3.4、MyListener 加入 spring.factories 文件 org.springframework.boot.autoconfigure.EnableAutoConfiguration\
com.demo.MyStarterConfigorg.springframework.context.ApplicationListener\
com.demo.listener.MyListener3.5、使用钩子 先在项目的pom文件中引入自定义 starter 包的 依赖 dependencygroupIdcom.demo/groupIdartifactIdmy-spring-boot-starter/artifactIdversion1.0-SNAPSHOT/version/dependency然后实现 MyServiceListener 接口
package com.demo.listener;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;Slf4j
Component
public class MethodTwoListener implements MyServiceListener{// 这里可以选择性实现因为接口方法是 default Overridepublic void methodTwoBefore(String name) {log.info(执行 - MethodTwoListener.methodTwoBefore name {}, name);}Overridepublic void methodTwoAfter(String name) {log.info(执行 - MethodTwoListener.methodTwoAfter name {}, name);}
}调用 自定义 starter 中的 methodTwo() 方法
package com.demo.controller;import com.demo.server.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;RestController
public class TestController {Autowiredprivate MyService myService;GetMapping(/methodTwo)public void methodTwo() {myService.methodTwo();} 执行结果
2023-09-10 19:18:27.961 MethodTwoListener - 执行 - MethodTwoListener.methodTwoBefore name my name is methodTwo()
2023-09-10 19:18:27.962 MyServiceImpl - 执行 - MyServiceImpl.methodTwo()
2023-09-10 19:18:27.962 MethodTwoListener - 执行 - MethodTwoListener.methodTwoAfter name my name is methodTwo() .