Spring进阶(一)SpringBoot替换上下文中的Bean

时间:2019-05-10作者:klpeng分类:IT综合浏览:9378评论:0

业务场景

已经写好业务入库的代码,现在要在数据库添加了一个公共字段,需要所有在业务处理的时候为实体类为这个字段赋值。

我们的业务场景:

  • 所有的实体类都继承一个BasicBean
  • 业务类都是通过Spring进行管理的

解决方案有多种,现在列举两种:

一、使用AOP为使用到方法添加自定义注解

这种对代码的侵入性大,需要业务方配合,再说了这样做需要业务代码人员配合

二、在系统启动时直接替换

使用SpringBoot的启动事件监听机制,通过修改注册的Bean,这样业务方可以无感知的实用

实现方式

这里简化了实现的业务,提供一个简单demo,在方法执行前执行一段代码(不是通过判断参数是否时BasicBean来做了)

思路

Spring进阶(一)SpringBoot替换上下文中的Bean

  • 监听器创建
    通过实现接口ApplicationListener来创建监听器
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;

import java.util.Map;

public class DemoApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {

    /**
     * The default order for the LoggingApplicationListener.
     */
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            System.out.println("==========ContextRefreshedEvent==========");
            //处理逻辑
           test((ContextRefreshedEvent)event);
        }

    }

    @Override
    public int getOrder() {
        return DEFAULT_ORDER;
    }

其中test方法就是具体的处理逻辑实现

  • 指定Bean获取
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext)e.getApplicationContext();

Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(DemoAnnotaion.class);

DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)applicationContext.getBeanFactory();

  • 新对象创建
			Object obj = beansWithAnnotation.get(key);

            System.out.println(beansWithAnnotation.keySet());

            // 创建一个代理类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(obj.getClass());
            // 设置回调方法
            enhancer.setCallback(new DemoMethodInterceptor());
            
            Object o = enhancer.create();

这里动态代理使用cglib来实现

  • 替换老对象
 			// 消除原先的bean
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(key);

            beanFactory.removeBeanDefinition(key);

            beanDefinition.setBeanClassName(o.getClass().getCanonicalName());

            beanFactory.registerBeanDefinition(key, beanDefinition);

            beanFactory.registerSingleton(key, o);
  • 监听器注册
    在resources目录下创建META-INF/spring.factories
    Spring进阶(一)SpringBoot替换上下文中的Bean
    spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
com.example.demo.frame.DemoApplicationListener

具体代码

目录结构

Spring进阶(一)SpringBoot替换上下文中的Bean

DemoAnnotaion

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DemoAnnotaion {
    String[] value();
}

DemoApplicationListener

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;

import java.util.Map;

public class DemoApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {

    /**
     * The default order for the LoggingApplicationListener.
     */
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            System.out.println("==========ContextRefreshedEvent==========");
           test((ContextRefreshedEvent)event);
        }

    }

    @Override
    public int getOrder() {
        return DEFAULT_ORDER;
    }

    private void test(ContextRefreshedEvent e){
        ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext)e.getApplicationContext();

        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(DemoAnnotaion.class);

        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)applicationContext.getBeanFactory();

        for(String key : beansWithAnnotation.keySet()) {

            Object obj = beansWithAnnotation.get(key);

            System.out.println(beansWithAnnotation.keySet());

            // 创建一个代理类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(obj.getClass());
            // 设置回调方法
            enhancer.setCallback(new DemoMethodInterceptor());

            Object o = enhancer.create();

            // 消除原先的bean
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(key);

            beanFactory.removeBeanDefinition(key);

            beanDefinition.setBeanClassName(o.getClass().getCanonicalName());

            beanFactory.registerBeanDefinition(key, beanDefinition);

            beanFactory.registerSingleton(key, o);
        }
    }
}

DemoMethodInterceptor

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


public class DemoMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("******cglib======");
        Object result = methodProxy.invokeSuper(o, args);
        return result;
    }
}

TestService

import org.springframework.stereotype.Component;

@Component
@DemoAnnotaion("save")
public class TestService {

    public void test(){
        System.out.println("test");
    }

    public void save(){
        System.out.println("save");
    }

}

DemoController

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @Autowired
    private TestService testService;

    @RequestMapping("/test")
    public Object test(){
        testService.save();
        testService.test();
        return testService.getClass();
    }
}

DemoApplication

import org.springframework.boot.SpringApplication;

public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

不要忘记配置监听器
在resources目录下创建META-INF/spring.factories
Spring进阶(一)SpringBoot替换上下文中的Bean

  • 也可以在类上直接加@Component注解,这样有些springBoot的启动事件就没法使用了,例如:ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent等
  • spring.factories 可以监听的事件范围更广

启动测试

启动项目直接运行DemoApplication .main

在浏览器上输入http://127.0.0.1:8080/test

返回"com.example.demo.frame.TestService$ E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBxxxxx"

控制台输出
Spring进阶(一)SpringBoot替换上下文中的Bean

完成了!!!!!

打赏
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
相关推荐

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

猜你喜欢