如何动态代理Controller的接口并注册到SpringMVC中

本文介绍如何动态代理Controller接口并将其注册到SpringMVC中。新版本的Spring框架会自动处理,但在老版本中可能需要手动操作,如移除或注入到handlermapping。文中参考了基于FeignClient的Controller自动注入机制及其github源码。
摘要由CSDN通过智能技术生成

1、实现对Controller接口的动态代理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
   
	String value() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyControllerMethod {
   
    String value() default "";
    Class beanType() default Object.class;
    String methodName() default "";
}
@RestController
public interface TestMyController {
   

    @GetMapping("/my/hello")
    @MyControllerMethod(beanType = TestMyService.class, methodName = "hello")
    String hello();
}

@Service
public class TestMyService {
   

    String hello(){
   
        return "hello";
    }
}

/**
 * 用于Spring动态代理注入自定义接口
 * https://www.jianshu.com/p/e2e19fcf97fc
 * https://blog.csdn.net/lichuangcsdn/article/details/89694363
 * https://blog.csdn.net/liyantianmin/article/details/81047373
 * open-feign源码: FeignClientsRegistrar https://blog.csdn.net/sinat_29899265/article/details/86577997
 * 
 * 	BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 
 * 		区别在于后者需要@Import导入类,前者需要@Component
 */
@Component
@Slf4j
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {
   

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
		System.out.println("BeanDefinitionRegistryPostProcessor,postProcessBeanDefinitionRegistry");

		// 通过反射获取需要代理的接口的clazz列表
		Set<Class<?>> clazzSet = scannerPackages("io.github.ygsama.microservice");
		for (Class beanClazz : clazzSet) {
   
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
			GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            // 这里可以给该对象的属性注入对应的实例。mybatis就在这里注入了dataSource和sqlSessionFactory,
            // definition.getPropertyValues().add("interfaceType", beanClazz),BeanClass需要提供setter
            // definition.getConstructorArgumentValues(),BeanClass需要提供包含该属性的构造方法,否则会注入失败
			definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
			definition.getConstructorArgumentValues().addGenericArgumentValue(applicationContext);

			// 注意,这里的BeanClass是生成Bean实例的工厂,不是Bean本身。
			// FactoryBean是一种特殊的Bean,其返回的对象不是指定类的一个实例,
			// 其返回的是该工厂Bean的getO
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
使用CGLIB代理计算方法耗时的步骤如下: 1. 引入cglib和asm的依赖。 2. 创建一个实现MethodInterceptor接口的拦截器类,重写intercept方法,在方法前后记录时间。 3. 在Controller定义一个需要计算耗时的方法,并在方法前加上@LogTime注解。 4. 使用AspectJ切面编程,在@Before获取目标方法并判断是否有@LogTime注解,如果有则使用CGLIB代理该方法并调用,实现计算方法耗时的功能。 示例代码如下: 1. 引入依赖 ``` <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>7.1</version> </dependency> ``` 2. 编写拦截器 ``` public class TimeInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proxy.invokeSuper(obj, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " cost " + (endTime - startTime) + " ms"); return result; } } ``` 3. 定义需要计算耗时的方法 ``` @RestController public class TestController { @LogTime @GetMapping("/test") public String test() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "test"; } } ``` 4. 编写切面 ``` @Aspect @Component public class TimeAspect { @Autowired private ApplicationContext context; @Pointcut("@annotation(com.example.demo.annotation.LogTime)") public void logTime() { } @Before("logTime()") public void beforeLogTime(JoinPoint joinPoint) throws Throwable { Object target = joinPoint.getTarget(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); if (method.isAnnotationPresent(LogTime.class)) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new TimeInterceptor()); Object proxy = enhancer.create(); Method proxyMethod = proxy.getClass().getMethod(method.getName(), method.getParameterTypes()); proxyMethod.invoke(proxy, joinPoint.getArgs()); } } } ``` 5. 测试 启动Spring Boot应用,并访问http://localhost:8080/test,可以看到控制台输出以下信息: ``` test cost 1000 ms ```
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值