所有分类
  • 所有分类
  • 未分类

Spring-BeanPostProcessor-使用/实例

简介

说明

本文用实例介绍Spring的BeanPostProcessor的应用。

所有的Bean都会走到BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法。

Bean的生命周期流程见:Spring-Bean生命周期-流程/原理 – 自学精灵

简单示例

代码

package com.example.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class TestProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization==> " + "bean:" + bean + "; " + "beanName:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization==> " + "bean:" + bean + "; " + "beanName:" + beanName);
        return bean;
    }
}

运行结果(太长,只贴一部分) 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2021-03-04 23:40:59.246  INFO 5820 --- [           main] com.example.DemoApplication              : Starting DemoApplication on DESKTOP-QI6B9ME with PID 5820 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot)
2021-03-04 23:40:59.248  INFO 5820 --- [           main] com.example.DemoApplication              : No active profile set, falling back to default profiles: default
postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1136b469; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat@1136b469; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
postProcessBeforeInitialization==> bean:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory@512d4583; beanName:tomcatServletWebServerFactory
postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@2abc224d; beanName:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration@2abc224d; beanName:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer@2b97cc1f; beanName:websocketServletWebServerCustomizer
postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer@2b97cc1f; beanName:websocketServletWebServerCustomizer
postProcessBeforeInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration@64f555e7; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
postProcessAfterInitialization==> bean:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration@64f555e7; beanName:org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

高级应用:注入属性

简介

Spring开发时,会遇到同一个接口有多个实现类,通常有如下几种方法:

  1. 通过@Autowired+@Qualify引入接口:@Autowired @Qualify(“helloServiceImpl2”) private HelloService helloService;
  2. 通过@Autowired+@Primary:在某个实现类上标注@Primary
  3. 在具体调用的地方通过ApplicationContext根据业务的需要来选择不同的接口实现类,虽然可以在抽象出一个工厂方法,但是还是感觉不够优雅。

其实上边这些方法已经能满足开发需求了,而且推荐使用法1。本处,使用BeanPostProcessor+自定义注解来达到同样的作用。当然,这么写是重复造轮子,本质上是毫无意义的。但是,我们可以从本例子中学到如下几点:

  1. 给使用了自定义注解的属性赋值的方法
  2. 代理工厂类的用法
  3. BeanPostProcessor的用法

接口

package com.example.service;

public interface HelloService {
    void sayHello();
}

实现类

package com.example.service.impl;

import com.example.service.HelloService;
import org.springframework.stereotype.Service;

@Service
public class HelloServiceImpl1 implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("我是HelloServiceImpl1");
    }
}
package com.example.service.impl;

import com.example.service.HelloService;
import org.springframework.stereotype.Service;

@Service
public class HelloServiceImpl2 implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("我是HelloServiceImpl2");
    }
}

自定义注解

package com.example.annotation;

import org.springframework.stereotype.Component;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RountingInjected {
    String value() default "helloServiceImpl1";
}

自定义BeanPostProcessor实现类

package com.example.processor;

import com.example.annotation.RountingInjected;
import com.example.factory.RoutingBeanProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Map;

@Component
public class HelloServiceInjectProcessor implements BeanPostProcessor {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetCls = bean.getClass();
        Field[] targetFld = targetCls.getDeclaredFields();
        for (Field field : targetFld) {
            //找到指定目标的注解类
            if (field.isAnnotationPresent(RountingInjected.class)) {
                if (!field.getType().isInterface()) {
                    throw new BeanCreationException("RoutingInjected field must be declared as an interface:" 
                                                     + field.getName() + " @Class " + targetCls.getName());
                }
                try {
                    this.handleRoutingInjected(field, bean, field.getType());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException {
        Map<String, Object> candidates = this.applicationContext.getBeansOfType(type);
        field.setAccessible(true);
        if (candidates.size() == 1) {
            field.set(bean, candidates.values().iterator().next());
        } else if (candidates.size() == 2) {
            String injectVal = field.getAnnotation(RountingInjected.class).value();
            Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates);
            field.set(bean, proxy);
        } else {
            throw new IllegalArgumentException("Find more than 2 beans for type: " + type);
        }
    }
}

代理实现类

package com.example.factory;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;

import java.util.Map;

public class RoutingBeanProxyFactory {
 
    private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1";
 
    public static Object createProxy(String name, Class type, Map<String, Object> candidates) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(type);
        proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates));
        return proxyFactory.getProxy();
    }
 
    static class VersionRoutingMethodInterceptor implements MethodInterceptor {
        private Object targetObject;
 
        public VersionRoutingMethodInterceptor(String name, Map<String, Object> beans) {
            this.targetObject = beans.get(name);
            if (this.targetObject == null) {
                this.targetObject = beans.get(DEFAULT_BEAN_NAME);
            }
        }
 
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            return invocation.getMethod().invoke(this.targetObject, invocation.getArguments());
        }
    }
}

测试类

package com.example.controller;

import com.example.annotation.RountingInjected;
import com.example.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RountingInjected(value = "helloServiceImpl2")
    private HelloService helloService;

    @GetMapping("/test1")
    public String test1() {
        helloService.sayHello();
        return "test1 success";
    }
}

测试

访问:http://localhost:8080/test1

后端结果:

我是HelloServiceImpl2
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录