动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package cn.idea360.bridge.proxy;import cn.idea360.bridge.api.UserService;import cn.idea360.bridge.api.UserServiceImpl;import cn.idea360.bridge.model.User;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;@Slf4j public class ProxyTest { UserService userService; @BeforeEach void init () throws Exception { userService = new UserServiceImpl(); } @Test void dynamicProxy () throws Exception { UserService instance = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(userService, args); } }); User user = instance.getUserById(1L ); log.info(user.toString()); } @Test void cglibProxy () throws Exception { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(userService.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept (Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return method.invoke(userService, args); } }); UserService instance = (UserService) enhancer.create(); User user = instance.getUserById(1L ); log.info(user.toString()); } }
简单注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="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 > <groupId > cn.idea360</groupId > <artifactId > http-bridge-dubbo</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > http-bridge-dubbo</name > <description > http-bridge-dubbo</description > <properties > <java.version > 1.8</java.version > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <spring-boot.version > 2.6.13</spring-boot.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > cn.idea360</groupId > <artifactId > dubbo-api</artifactId > <version > 1.0.0</version > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > ${spring-boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.1</version > <configuration > <source > 1.8</source > <target > 1.8</target > <encoding > UTF-8</encoding > </configuration > </plugin > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > <configuration > <mainClass > cn.idea360.bridge.Application</mainClass > <skip > true</skip > </configuration > <executions > <execution > <id > repackage</id > <goals > <goal > repackage</goal > </goals > </execution > </executions > </plugin > </plugins > </build > </project >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package cn.idea360.bridge.support;import cn.idea360.bridge.proxy.ProxyClient;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import org.springframework.beans.factory.support.GenericBeanDefinition;import org.springframework.context.EnvironmentAware;import org.springframework.context.ResourceLoaderAware;import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;import org.springframework.core.env.Environment;import org.springframework.core.io.ResourceLoader;import org.springframework.core.type.filter.AnnotationTypeFilter;import org.springframework.stereotype.Component;import org.springframework.util.ClassUtils;import java.util.LinkedHashSet;@Component @Slf4j public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor , ResourceLoaderAware , EnvironmentAware { private ResourceLoader resourceLoader; private Environment environment; @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { log.info("postProcessBeanDefinitionRegistry" ); LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>(); ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this .resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(ProxyClient.class)); candidateComponents.addAll(scanner.findCandidateComponents("cn.idea360" )); candidateComponents.addAll(scanner.findCandidateComponents("com.idea360" )); for (BeanDefinition candidateComponent : candidateComponents) { String className = candidateComponent.getBeanClassName(); log.info("@ProxyClient className: {}" , className); Class beanClazz = ClassUtils.resolveClassName(className, null ); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz); GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz); definition.setBeanClass(ServiceFactory.class); definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); registry.registerBeanDefinition(beanClazz.getSimpleName(), definition); } } @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { log.info("postProcessBeanFactory" ); } @Override public void setResourceLoader (ResourceLoader resourceLoader) { this .resourceLoader = resourceLoader; } protected ClassPathScanningCandidateComponentProvider getScanner () { return new ClassPathScanningCandidateComponentProvider(false , this .environment) { @Override protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false ; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { isCandidate = true ; } } return isCandidate; } }; } @Override public void setEnvironment (Environment environment) { this .environment = environment; } }
工厂bean+动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package cn.idea360.bridge.support;import cn.idea360.bridge.model.User;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.FactoryBean;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ServiceFactory <T > implements FactoryBean <T > { private Class<T> interfaceType; public ServiceFactory (Class<T> interfaceType) { this .interfaceType = interfaceType; } @Override public T getObject () throws Exception { InvocationHandler handler = new ServiceProxy<>(interfaceType); return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[] {interfaceType},handler); } @Override public Class<?> getObjectType() { return interfaceType; } @Override public boolean isSingleton () { return true ; } @Slf4j static class ServiceProxy <T > implements InvocationHandler { private final Class<T> interfaceType; public ServiceProxy (Class<T> interfaceType) { this .interfaceType = interfaceType; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this ,args); } log.info("interface: {}" , interfaceType); log.info("method: {}" , method.getName()); log.info("parameterTypes: {}" , method.getParameterTypes()); log.info("args: {}" , args); User user = User.builder().name("当我遇上你" ).password("123" ).build(); return user; } } }
单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package cn.idea360.bridge;import cn.idea360.bridge.api.Role;import cn.idea360.bridge.api.RoleService;import cn.idea360.bridge.api.UserService;import cn.idea360.bridge.model.User;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;@Slf4j @SpringBootTest class ApplicationTests { @Resource private UserService userService; @Resource private RoleService roleService; @Test void contextLoads () { log.info("app start" ); User user = userService.getUserById(1L ); log.info(user.toString()); roleService.addRole(Role.builder().name("ROLE_ADMIN" ).build()); } }
参考Feign实现
org.mybatis.spring.annotation.MapperScan 类似
maven
1 2 3 4 5 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > <version > 3.0.4</version > </dependency >
入口类
1 org.springframework.cloud.openfeign.EnableFeignClients
自定义实现
定义注解
1 2 3 4 5 6 7 8 9 10 11 12 package cn.idea360.bridge.proxy;import java.lang.annotation.*;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface ProxyClient { String value () default "" ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package cn.idea360.bridge.config;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({HttpClientsRegistrar.class}) public @interface EnableProxy { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
注册Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 package cn.idea360.bridge.config;import cn.idea360.bridge.proxy.ProxyClient;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.FactoryBean;import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.context.EnvironmentAware;import org.springframework.context.ResourceLoaderAware;import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;import org.springframework.core.env.Environment;import org.springframework.core.io.ResourceLoader;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.filter.AnnotationTypeFilter;import org.springframework.util.Assert;import org.springframework.util.ClassUtils;import org.springframework.util.StringUtils;import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Map;import java.util.Set;@Slf4j public class HttpClientsRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware , EnvironmentAware { private ResourceLoader resourceLoader; private Environment environment; @Override public void setEnvironment (Environment environment) { this .environment = environment; } @Override public void setResourceLoader (ResourceLoader resourceLoader) { this .resourceLoader = resourceLoader; } @Override public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>(); ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this .resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(ProxyClient.class)); Set<String> basePackages = getBasePackages(metadata); for (String basePackage : basePackages) { candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); } for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@ProxyClient can only be specified on an interface" ); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes(ProxyClient.class.getCanonicalName()); registerProxyClient(registry, annotationMetadata, attributes); } } } private void registerProxyClient (BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); log.info("@ProxyClient className: {}" , className); Class clazz = ClassUtils.resolveClassName(className, null ); ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) registry : null ; ProxyClientFactoryBean factoryBean = new ProxyClientFactoryBean(); factoryBean.setBeanFactory(beanFactory); factoryBean.setType(clazz); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> { return factoryBean.getObject(); }); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); definition.setLazyInit(true ); AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className); registry.registerBeanDefinition(className, beanDefinition); } protected ClassPathScanningCandidateComponentProvider getScanner () { return new ClassPathScanningCandidateComponentProvider(false , this .environment) { @Override protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false ; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { isCandidate = true ; } } return isCandidate; } }; } protected Set<String> getBasePackages (AnnotationMetadata importingClassMetadata) { Map<String, Object> attributes = importingClassMetadata .getAnnotationAttributes(EnableProxy.class.getCanonicalName()); Set<String> basePackages = new HashSet<>(); for (String pkg : (String[]) attributes.get("value" )) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : (String[]) attributes.get("basePackages" )) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses" )) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName())); } return basePackages; } }
接口工厂Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package cn.idea360.bridge.config;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.BeanFactoryAware;import org.springframework.beans.factory.FactoryBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class ProxyClientFactoryBean implements FactoryBean <Object >, InitializingBean , ApplicationContextAware , BeanFactoryAware { private Class<?> type; private BeanFactory beanFactory; private ApplicationContext applicationContext; @Override public Object getObject () { InvocationHandler handler = new ServiceProxy<>(type); return Proxy.newProxyInstance(type.getClassLoader(), new Class[] {type}, handler); } @Override public Class<?> getObjectType() { return type; } @Override public boolean isSingleton () { return true ; } public void setType (Class<?> type) { this .type = type; } public Class<?> getType() { return type; } @Override public void setBeanFactory (BeanFactory beanFactory) throws BeansException { this .beanFactory = beanFactory; } @Override public void afterPropertiesSet () throws Exception { } @Override public void setApplicationContext (ApplicationContext context) throws BeansException { applicationContext = context; beanFactory = context; } }
动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package cn.idea360.bridge.config;import cn.idea360.bridge.model.User;import lombok.extern.slf4j.Slf4j;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;@Slf4j public class ServiceProxy <T > implements InvocationHandler { private final Class<T> interfaceType; public ServiceProxy (Class<T> interfaceType) { this .interfaceType = interfaceType; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this ,args); } log.info("interface: {}" , interfaceType); log.info("method: {}" , method.getName()); log.info("parameterTypes: {}" , method.getParameterTypes()); log.info("args: {}" , args); User user = User.builder().name("当我遇上你" ).password("123" ).build(); return user; } }
接口定义
1 2 3 4 5 @ProxyClient public interface UserService { User getUserById (Long id) ; }
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package cn.idea360.bridge;import cn.idea360.bridge.config.EnableProxy;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@EnableProxy(value = {"cn.idea360", "com.idea360"}) @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
常用操作
扫描包含注解的component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Set<String> findAnnotatedClasses (Class<? extends Annotation> annotationType, String... packagesToBeScanned) { ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false ); provider.addIncludeFilter(new AnnotationTypeFilter(annotationType)); Set<String> ret = new HashSet<>(); for (String pkg : packagesToBeScanned) { Set<BeanDefinition> beanDefs = provider.findCandidateComponents(pkg); beanDefs.stream() .map(BeanDefinition::getBeanClassName) .forEach(ret::add); } return ret; }
扫描包含三方jar的class
1 2 3 4 5 <dependency > <groupId > cn.hutool</groupId > <artifactId > hutool-all</artifactId > <version > 5.5.0</version > </dependency >
1 2 3 4 5 @Test void scans () { Set<Class<?>> classes = ClassScanner.scanPackage("cn.idea360" ); log.info("@ProxyClient {}" , classes.contains(RoleService.class)); }
BeanDefinitionRegistryPostProcessor 和ImportBeanDefinitionRegistrar 有什么区别
BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 都是用于动态注册 Bean 到 Spring 容器的接口,但它们的使用场景和目的略有不同。
BeanDefinitionRegistryPostProcessor: :
作用:在 Spring 容器加载 Bean 定义之后,但在实例化 Bean 之前,允许对 BeanDefinition 进行修改或者注册新的 BeanDefinition。
使用场景:适用于在应用启动时对现有 BeanDefinition 进行修改或者添加一些新的 BeanDefinition。
例子:用于动态注册数据源、JdbcTemplate、事务管理器等。
ImportBeanDefinitionRegistrar:
作用:允许在配置类中根据条件动态注册额外的 BeanDefinition。
使用场景:适用于根据特定条件选择性地注册 BeanDefinition。通常与 @Import 注解一起使用,以实现特定的条件化配置。
例子:用于基于某些条件选择性地注册一些 Bean。@Enablexxx
选择哪个更适合注册多数据源
如果你的主要目标是在应用启动时动态注册多个数据源,那么使用 BeanDefinitionRegistryPostProcessor 更为直观和合适。
如果你需要在配置类中根据条件来动态选择性地注册一些 Bean,那么 ImportBeanDefinitionRegistrar 可能更适合,尤其是当你希望将注册逻辑放在配置类中时。
在注册多数据源的场景中,通常会使用 BeanDefinitionRegistryPostProcessor,因为你可以在这个时机处理数据源的注册,而且这是一个更自然的选择。