自定义starter
相当于自己根据配置信息,生成了一个默认的bean,
- 导入依赖:
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.10</version><relativePath/><!-- lookup parent from repository --></parent><groupId>com.cj</groupId><artifactId>hello-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version><name>hello-spring-boot-starter</name><description>hello-spring-boot-starter</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></plugin></plugins></build></project>将springboot启动类,测试类删掉,,将maven打包成jar的配置删掉,,
2. 自定义一个配置类,,来定义配置
packagecom.cj.hellospringbootstarter;importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix="mytest")@DatapublicclassHelloProperties{privateStringid;privateStringname;privateStringaddress;}- 通过这个配置加载一个默认的 Bean
@Data@NoArgsConstructorpublicclassHelloService{privateStringname;privateStringaddress;publicHelloService(Stringname,Stringaddress){this.name=name;this.address=address;}}@Configuration@ComponentScan@EnableConfigurationProperties(HelloProperties.class)publicclassHelloServiceAutoConfiguration{@AutowiredprivateHelloPropertieshelloProperties;@BeanpublicHelloServicehelloService(){returnnewHelloService(helloProperties.getName(),helloProperties.getAddress());}}容器中就有这个默认的 HelloService的bean,,
- 设置自动装配
springboot3是在META-INF/spring文件夹下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports这个文件,,
填入自动配置类的全限定类名
springboot2是在META-INF下面创建spring.factories
- 打包使用
<dependency><groupId>com.cj</groupId><artifactId>hello-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency>@RestController@LogpublicclassHelloController{@AutowiredHelloServicehelloService;@GetMapping("/hello")publicHelloServicegetHelloService(){returnhelloService;}}自定义一个注解,写在starter里面,动态的增强被标注这个注解的类,,,
可以用spring中BeanPostProcessor: spring完成 实例化,配置,,, 在初始化bean的前后,可以实现一些自定义的配置,有两个方法:
- postProcessBeforeInitialization : bean初始化之前调用
- postProcessAfterInitialization: bean初始化之后调用
如果想增强指定的注解标注的类,,就可以写在BeanPostProcessor中,拦截指定注解,增强:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public@interfaceLog{/** * 是否打印参数 * @return */booleanprintArgs()defaulttrue;booleanprintResult()defaulttrue;/** * 是否打印方法耗时 * @return */booleanprintCostTime()defaulttrue;}packagecom.cj.hellospringbootstarter.annotation;importorg.springframework.beans.BeansException;importorg.springframework.beans.factory.config.BeanPostProcessor;importorg.springframework.cglib.proxy.Callback;importorg.springframework.cglib.proxy.Enhancer;importorg.springframework.cglib.proxy.MethodInterceptor;importorg.springframework.cglib.proxy.MethodProxy;importorg.springframework.stereotype.Component;importjava.lang.reflect.Method;importjava.util.Arrays;/** * 对@Log 标注的 bean进行统一的 增强 * * * BeanPostProcessor : spring完成实例化,配置,初始化一个bean之后,,可以实现一些自定义的逻辑 * - postProcessBeforeInitialization : bean初始化之前调用 * - postProcessAfterInitialization : bean初始化之后调用 */@ComponentpublicclassLogBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{Class<?>beanClass=getTargetClass(bean);// 是否标记了 @Log注解if(beanClass.isAnnotationPresent(Log.class)){LoglogAnnotation=beanClass.getAnnotation(Log.class);ObjectenhanceBean=createLogEnhanceProxy(bean,beanClass,logAnnotation);returnenhanceBean;}returnbean;}privateObjectcreateLogEnhanceProxy(ObjecttargetBean,Class<?>targetClass,LoglogAnnotation){Enhancerenhancer=newEnhancer();enhancer.setSuperclass(targetClass);// MethodInterceptor : cglib的方法拦截器,,当你调任何方法的时候,都会执行这个enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{// 跳过Object的基础方法if(isObjectBaseMethod(method)){returnproxy.invokeSuper(obj,args);}try{if(logAnnotation.printArgs()){System.out.printf("[日志增强]%s.%s()入参:%s%n",targetClass.getSimpleName(),method.getName(),Arrays.toString(args));}longstartTime=System.currentTimeMillis();Objectresult=proxy.invokeSuper(obj,args);if(logAnnotation.printCostTime()){longendTime=System.currentTimeMillis();System.out.printf("[日志耗时]%s.%s() 耗时%s",targetClass.getSimpleName(),method.getName(),startTime-endTime);}returnresult;}catch(Throwablee){System.out.printf("日志增强失败 %s.%s() 异常为:%s",targetClass.getSimpleName(),method.getName(),e.getMessage());throwe;}};});ObjectenhancedBean=enhancer.create();System.out.println("增强完成"+targetClass.getName());returnenhancedBean;}/** * 获取bean的真实类型 * @param bean * @return */privateClass<?>getTargetClass(Objectbean){Class<?>clazz=bean.getClass();// 如果是CGLIB代理类,,获取父类if(clazz.getName().contains("$$EnhancerByCGLIB$$")){returnclazz.getSuperclass();}returnclazz;}privatebooleanisObjectBaseMethod(Methodmethod){// 获取声明该方法的类Class<?>declaringClass=method.getDeclaringClass();// method.getDeclaringClass()returndeclaringClass==Object.class&&(method.getName().equals("toString")||method.getName().equals("hashCode")||method.getName().equals("equals"));}}