@TOCjava
BeanPostProcessor接口有2个方法:spring
Object postProcessBeforeInitialization(Object bean, String beanName) Object postProcessAfterInitialization(Object bean, String beanName)
感受Initialization颇有误导性,这里的Initialization并非指类的初始化,也不是指实例的初始化。缓存
而是指调用init-method这个初始化方法,就是相似于下面init-method指定的方法init调用前执行postProcessBeforeInitialization,调用以后执行postProcessAfterInitialization。ide
@Bean(initMethod = "init")
<bean id="id" class="Class" init-method="init"></bean>
想想也有道理,都post类实例了,不是早就执行玩类初始化和类实例初始化了么,那么就只有init-method这个初始化了。post
init-method方法是在InitializingBean接口的afterPropertiesSet方法以后执行,因此bean的属性已经设置完成了。this
经过BeanPostProcessor咱们能够实现对用户透明的代理,以前咱们介绍动态代理的时候,要本身建立代理类,感受很是不友好。代理
这里咱们介绍一个经过BeanPostProcessor结合注解与动态代理来实现对用户透明的打印方法执行时间的功能,来感觉一下BeanPostProcessor的强大。日志
首先咱们建立一个注解,用户想要打印一个方法的执行时间的时候,只须要添加该注解就能够了。code
import org.curitis.jdk.LogExeDurationInvacationHandler; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LogExeDuration { Class value() default LogExeDurationInvacationHandler.class; }
为了简化一点动态代理的逻辑,咱们这里没有使用方法注解,而是使用了类注解,就是只要类上添加了LogExeDuration,就打印这个类中方法执行时间。component
这里使用的Class属性是日志类,就是日志打印到哪里,默认是LogExeDurationInvacationHandler,还没见过,不要紧,立刻来。
import org.curitis.annotation.LogExeDuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.time.Duration; import java.time.Instant; public class LogExeDurationInvacationHandler implements InvocationHandler { private Object target; private Logger logger; public LogExeDurationInvacationHandler(Object target,LogExeDuration logExeDuration) { this.target = target; this.logger = LoggerFactory.getLogger(logExeDuration.value()); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Instant start = Instant.now(); Object result = method.invoke(target, args); Instant end = Instant.now(); logger.info(String.format("%s execution cost %d ms",method.getName(), Duration.between(start,end).toMillis())); return result; } public static Object getProxy(Object target, LogExeDuration annotation){ Class<?> clazz = target.getClass(); ClassLoader classLoader = clazz.getClassLoader(); Class<?>[] interfaces = clazz.getInterfaces(); LogExeDurationInvacationHandler h = new LogExeDurationInvacationHandler(target,annotation); return Proxy.newProxyInstance(classLoader, interfaces, h); } }
InvocationHandler咱们的老朋友了,一看到基本就能够肯定是JDK动态代理的实现逻辑部分,重点关注invoke方法,咱们看到逻辑很是简单就是在调用目标方法先后记录了一下时间,并打印。
静态方法getProxy是一个方便获取代理类的工厂方法。
接下来,请出大佬BeanPostProcessor:
import org.curitis.annotation.LogExeDuration; import org.curitis.jdk.LogExeDurationInvacationHandler; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class LogExeDurationBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class<?> clazz = bean.getClass(); LogExeDuration annotation = clazz.getAnnotation(LogExeDuration.class); if(annotation != null){ bean = LogExeDurationInvacationHandler.getProxy(bean,annotation); } return bean; } }
逻辑很简单,就是若是bean建立以后看这个bean有没有LogExeDuration注解,若是有,就把这个bean替换为一个代理类。
public interface BusinessService { String doSomething(Integer id,String name); }
import org.curitis.annotation.LogExeDuration; import org.curitis.service.BusinessService; import org.springframework.stereotype.Service; @Service("businessService") @LogExeDuration public class BusinessServiceImpl implements BusinessService{ @Override public String doSomething(Integer id, String name) { return id + " " + name; } }
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan({ "org.curitis.component", "org.curitis.service" }) public class ApplicationConfig { }
import org.curitis.config.ApplicationConfig; import org.curitis.service.BusinessService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Start { private static final Logger logger = LoggerFactory.getLogger(Start.class); public static void main(String[] args) { logger.info("start......"); ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); BusinessService service = context.getBean(BusinessService.class); String result = service.doSomething(1, "curitis"); System.out.println(result); } }
经过BeanPostProcessor、注解、动态代理来基本已经成了扩展Spring的标准套路了。
例如@Transactional注解实现事务透明扩展,经过@Cache来实现缓存透明扩展。
用户使用起来也是爽歪歪,只须要在对应的方法、类上添加相应的注解就能够了,这就对用户很是友好,友好到不少人使用了好久的@Transational注解都不知道究竟是怎么回事,固然这其中也包括我。