https://github.com/segg3r/epam_presentation Why: ServiceLocator PropertiesLocator Injection Application model: public class FirstService @Autowired public Singleton singleton; @Autowired public Prototype prototype; } public class SecondService @Autowired public Singleton singleton; @Autowired public Prototype prototype; } public class Singleton { } public class Prototype { } public class FactoryBean { } public class Factory { public static FactoryBean factoryMethod() { return new FactoryBean(); } } xml-config // constructor-arg AppCtx-s: ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-10.xml"); FirstService firstService = ctx.getBean(FirstService.class); annotation-config: @Component("mixedBean") public class MixedConfigurationBean { public int value; public MixedConfigurationBean() { } public MixedConfigurationBean(int value) { this.value = value; } } In this case - XML-config wins. Flawless victory INFO: Overriding bean definition for bean 'mixedBean' with a different definition: 1 java-config: @Configuration public class ScopesConfig11 { @Bean public FirstService firstService() { return new FirstService(); } @Bean public SecondService secondService() { return new SecondService(); } @Bean public Singleton singleton() { return new Singleton(); } @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public Prototype prototype() { return new Prototype(); } } ApplicationContext ctx = new AnnotationConfigApplicationContext(ScopesConfig11.class); ... Lookup 1 (dirty) @Autowired private ApplicationContext ctx; private UserAction getUserAction(String email) { UserAction result = ctx.getBean(UserAction.class); result.email = email; return result; } Lookup 2 private UserAction getUserAction(String email) { UserAction result = buildUserAction(); result.email = email; return result; } public UserAction buildUserAction() { return null; } Lookup (java-config) ... getUserAction ... @Lookup public UserAction buildUserAction() { return null } @Config // !!! lookup methods cannot get replaced on beans, returned from factory methods where we cannot dynamically provide a sublass for them @ComponentScan("...beans.userservice") public class ScopesConfig21 { @Bean public UserService userService() { return new UserService(); } @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public UserAction userAction() { return new UserAction(); } } ... @Component public class UserService { ... } init-n: * Reading bean def-s and populating them into BeanFactory * Calling BeanFactoryPostProcessors * Instantiating bean object * Calling BeanPostProcessor::postProcessBeforeInitialization * Initializating object (init-method, @PostConstruct) * Calling BeanPostProcessor::postProcessAfterInitialization public class HelloWorldBPP implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException { System.out.println("hello pre : " + beanName + " is " + bean.getClass()); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException { System.out.println("hello post : " + beanName + " is " + bean.getClass()); return bean; } } // Procies instances of given class and intercepting its methods public class ProxyBPP implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException { System.out.println("Post : " + beanName + " is " + bean.getClass()); if (Service.class.isAssignableFrom(bean.getClass())) { return (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class [] { Service.class }, new InvocationHandler() { @Override public Object invode(Object proxy, Method method, Object [] args) throws Throwable { System.out.println("Calling method " + method.getName()); return method.invoke(bean, args); } }); } return bean; } } public class ServiceImpl implements Service { @Override public void printSomething() { System.out.println("Something"); } } @Configuration public class BPPConfig2 { @Bean public BeanPostProcessor proxyBPP() { return new ProxyBPP(); } @Bean public BeanPostProcessor helloWorldBPP() { return new HelloWorldBPP(); } @Bean public Service printService() { return new PrintServiceImpl(); } } pre: pringService is class ...beans.ServiceImpl hello pre: pringService is class ...beans.ServiceImpl Post: pringService is class ...beans.ServiceImpl hello post: pringService is class ...beans.ServiceImpl Calling method printSomething Something // Multiple annotation postprocessors public class DashboardService { @LogThisMethod @SendEmail public void saveDashboard() { } } @Aspect public class SendEmailAspect { @Before("@annotation(SendEmail)") public void logBefore(JoinPoint joinPoint) { System.out.println("Sending email"); } } public class LogThisBPP implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException { for (Method method : bean.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(LogThisMethod.class)) { ProxyFactory factory = new ProxyFactory(); factory.setSuperClass(bean.getClass()); factory.setFilter(new MethodFilter() { @Override public boolean isHandled(Method m) { return m.isAnnotationPresent(LogThisMethod.class); } }); Class clazz = factory.createClass(); MethodHandler handler = new MethodHandler() { @Override public Object invoke(Object self, Method overridden, Method forwarder, Object [] args) throws Throwable { System.out.println("Logging method"); Method beanMethod = bean.getClass().getMethod( overridden.getName(), overridden.getParameterTypes()); return beanMethod.invode(bean, args); } }; Object intance = clazz.newInstance(); ((ProxyObject) instance).setHandler(handler); return instance; } } return bean; } } // Multiple annotation postprocessors @Configuration @EnableAspectJAutoProxy public class BPPConfig4 { @Bean public SendEmailAspect sendEmailAspect() { return new SendEmailAspect(); } @Bena public LogThisBPP logThisBPP() { return new LogThisBPP(); } @Bena public DashboardService dashboardService() { return new DashboadService(); } } // output Sending email // problem Email sent, but there is no logging // Runner ApplicationContext ctx = new AnnotationConfigApplicationContext(BPPConfig4.class); DashboardService service = ctx.getBean(DashboardService.class); service.saveDashboard(); // for (Method method : bean.getClass().getDeclaredMethods()) { bean.getClass() -> ...beans.annotationprocessing.DashboardService$$EnhancerBySpringCGLIB$$.... // This method does not have annotation in proce class bean.getClass().getMethod("saveDashboard", new Class[]{}).getAnnotations() [] Hot to fix??? // Bean caching private Map beansForLogging = Maps.newHashMap(); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { for (Method method : bean.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(LogThisMethod.class)) { beansForLogging.put(beanName, bean); } } return bean; } // The fix is: Object originalBean = beansForLogging.get(beanName); // Get cached bean if (originalBean == null) return bean; ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(originalBean.getClass()); // Get original bean class factory.setFilter(new MethodFilter() { @Override public boolean isHandled(Method m) { return m.isAnnotationPresent(LogThisMethod.class); } }); Class clazz = factory.createClass(); MethodHandler handler = new MethodHandler() { @Override public Object invoke(Object self, Method originalBeanMethod, Method proxyMethod, Object [] args) throws Throwable { System.out.println("Logging method"); Method beanMethod = bean.getClass().getMethod(originalBeanMethod.getName(), originalBeanMethod.getParameterTypes()); // Call the method of proxied bean return beanMethod.invoke(bean, args); } }; Object instance = clazz.newInstance(); ((ProxyObject) instance).setHandler(handler); return instance; // Conclusion (for BeanPostProcessors) 1. BPPs are needed for postprocessing beans themselves (self-injection, proxying) 2. Only create proxies in "postProcessAfterInitialization" 3. Save original bean in "postProcessBeforeInitialization", if needed 4. Always use Class::isAssibnableFrom instead of Class::equals 5. Do not use "bean.getClass()" to parse annotations in "postProcessAfterInitialization", cache beans instead // HelloWorldBeanFactoryPostProcessor public class HelloWorldBFPP implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDefinition = beanFactoryh.getBeanDefinition(beanDefinitionName); System.out.println("BFPP : " + beanDefinitionName + "; " + beanDefinition.getClass()); } } } // BeanFactoryPostProcessor should be static in appropriate config ! // BFPP should be created very early in lifecycle of ApplicationContext @Configuration public class BFPPConfig1 { @Bean public static BeanFactoryPostProcessor helloWorldBFPP() { return new HelloWorldBFPP(); } @Bean public BeanPostProcessor helloWorldBPP() { return new HelloWorldBPP(); } @Bean public Service printService() { return new ServiceImpl(); } } // ExtensionBeanFactoryPostProcessor public class ExtensionDeployer { @Autowired private List extensions; public void deployExtensions() { extensions.forEach((extension) -> { System.out.println("Deploying " + extension.getClass()); }); } } // We want our users to provide their own implementations of Extension class public class ExtensionBFPP implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansExtension { List> extensionClasses = parseClasspath(); for (Class extensionClass : extensionClasses) { try { beanFactory.registerSingleton(extensionClass.getName(), extensionClass.newInstance()); } catch (Exception e) { System.out.println("Failed to register extension " + extensionClass.getName()); e.printStackTrace(); } } } @SuppressWarning("unchecked") private List> parseClasspath() { return Lists.newArrayList(SimpleExtension.class); } public static class SimpleExtension extends Extension { } } // Runner ApplicationContext ctx = new AnnotationConfigApplicationContext(BFPPConfig2.class) ExtensionDeployer extensionDeployer = ctx.getBean(ExtensionDeployer.class); extensionDeployer.deployExtensions() //Output Deploying class ...spring.postprocessor.ExtensionBFPP$SimpleExtension // Conclusion 1. BFPPs are needed for BeanDefinition metadata postprocessing * addmin method overrides * change bean definition properties * etc 2. BFPPs are needed for customizing list of beans before their creation !!! check scopes.txt here !!!