I have an old project that I need to integrate with Spring 2.5.x (3.0 is not possible).
I have created a bean, that have to initializate its field userSession by itself:
public class SomeBean {
UserSession userSession;
#PostContrust
public void init() {
HttpSession session = WebContext.current().getSession();
userSession = (UserSession) session.getAttribute("userSession");
}
}
Is it possible to write some kind of autowiring handler that will resolve userSession and pass it for autowiring to Spring, so my bean looks just like:
public class SomeBean {
#Autowire UserSession userSession;
}
and the handler like:
public class AutowireHanlder {
public boolean isCandidate(Class<?> type) {
return type.equals(UserSession.class);
}
public Object resolve(Class<?> type) {
HttpSession session = WebContext.current().getSession();
return (UserSession) session.getAttribute("userSession");
}
}
I would do this using a session-scoped FactoryBean:
public class UserSessionFactoryBean extends AbstractFactoryBean<UserSession> {
#Override
public Class<?> getObjectType() {
return UserSession.class;
}
#Override
protected UserSession createInstance() throws Exception {
HttpSession session = WebContext.current().getSession();
return (UserSession) session.getAttribute("userSession");
}
}
Define UserSessionFactoryBean as a bean:
<bean scope="session" class="com.xyz.UserSessionFactoryBean"/>
... and then you should then be able to autowire UserSession into any other bean.
The complexity here is that UserSessionFactoryBean has to be session-scoped (see docs on bean scopes), since it must return a new value for each HttpSession. This means that any bean it is autowired into must also be session-scoped, otherwise it'll fail. You can get around this restriction using scoped-proxies.
Related
I'm developing an external component for applications which contains functionality to inject Jersey Client filters into lazy-loaded clients. Ive implemented a BeanPostProcessor that does this:
public class ClientFilterInjector implements BeanPostProcessor, Ordered {
private ClientTraceInterceptor clientTraceInterceptor;
public ClientFilterInjector(ClientTraceInterceptor clientTraceInterceptor) {
this.clientTraceInterceptor = clientTraceInterceptor;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(bean instanceof JerseyWebTarget) {
((JerseyWebTarget) bean).register(clientTraceInterceptor);
}
return bean;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
However, Spring Boot apparently auto-scans for BeanPostProcessor implementations regardless of whether or not they're annotated or have a bean creation method. Because of this, it screws up the order for which beans are created in the application. Is there a way to defer the instantiation of a BeanPostProcessor?
Did you try to add lazy annotation?
import org.springframework.context.annotation.Lazy;
By using this annotation it will effect on the first call but later on it will exactly the same.
public class ClientFilterInjector implements BeanPostProcessor, Ordered {
private ClientTraceInterceptor clientTraceInterceptor;
public ClientFilterInjector(ClientTraceInterceptor clientTraceInterceptor) {
this.clientTraceInterceptor = clientTraceInterceptor;
}
#Override
public Object postProcessAfterInitialization(#Lazy Object bean, String beanName) {
if(bean instanceof JerseyWebTarget) {
((JerseyWebTarget) bean).register(clientTraceInterceptor);
}
return bean;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
It looks like if you implement BeanProcessor in the form of an anonymous class it will not get auto-scanned in the Spring Boot application init and you can defer its instantiation whenever you want by adding it to the bean factory of the application context.
((ConfigurableApplicationContext) appContext).getBeanFactory().addBeanPostProcessor(new BeanPostProcessor() {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof WebTarget) {
((WebTarget) bean).register(instance);
}
return bean;
}
});
Is there a standard way of configuring already created bean in Spring Boot by NOT creating providing this bean myself but instead somehow injecting this bean in a hook method in a configuration class and do additional class?
For example I would like to have Thymeleaf's TemplateResolver as created by its autoconfiguration but I would like to change one property.
What is the best way to do this (again, not by providing my own TemplateResolver ) ?
You could #Autowired the auto-configured TemplateResolver into your configuration class and then use a #PostConstruct method to set the property.
public class ExampleConfiguration {
#Autowired
private TemplateResolver templateResolver;
#PostConstruct
void customize() {
templateResolver.setFoo("bar");
}
}
I was facing a similar issue, I needed to customize an autoconfigured bean. Thanks to M. Deinum and Andy Wilkinson, I coded a BPP based on your points. Hope it can be a more general solution for this kind of issue.
public class TemplateResolverCustomizationBeanPostProcessor implements BeanPostProcessor, Ordered {
private Logger logger = LoggerFactory.getLogger(getClass());
private int order = Ordered.LOWEST_PRECEDENCE;
public TemplateResolverCustomizationBeanPostProcessor() {
logger.info("Created TemplateResolverCustomizationBeanPostProcessor instance");
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
logger.info("postProcessBeforeInitialization method invoked");
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
logger.info("postProcessAfterInitialization method invoked");
if (bean instanceof TemplateResolver) {
((TemplateResolver) bean).setFoor("bar");
}
return bean;
}
public void setOrder(int order) {
this.order = order;
}
#Override
public int getOrder() {
return order;
}
}
I have Session scope bean which behaves as request scope bean.
I'm using it in Singleton bean, and maybe that's why with second request is has null values?
Code which I used to setup the bean:
In singleton:
#Autowired
private MyBean myBean;
MyBean class:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanImpl implements MyBean, Serializable {
Configuration:
#Configuration
#ComponentScan(scopedProxy = ScopedProxyMode.INTERFACES, value = { "my.package.with.bean" })
public class ComponentsConfig {
}
MyBean is simple pojo. with getters and setters.
With first request I set values on that bean, with second request in the same class (singleton) I want to read those values, but all values are null. There is no way that something has overrdiden those values.
EDIT
How I make requests - It's just simple browser request, and code which read/writes to my session bean lays in filters.
This is singleton:
#Service
public class SingletonBean {
#Autowired
private MyBean myBean;
// here I save the data to session scoped bean
private void saveUser(LdapContext ctx, String principal) throws NamingException {
Attributes attributes = getAttributes();
myBean.setId("id");
myBean.setName("name");
myBean.setEmail("email");
myBean.setTelNum("123");
myBean.setGroups(Lists.newArrayList("one", "two"));
myBean.setAddress("address");
}
// here I try to read data from session scoped bean:
#Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
LdapContextFactory ldapContextFactory) throws NamingException {
// here userInfo will be null
List<String> userInfo = myBean.getGroups();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
for (String role : userInfo ) {
authorizationInfo.addRole(role);
}
return authorizationInfo;
}
}
When user logs in, and he is authenticated, I save his details in session bean. When he tries to open any page method queryForAuthorizationInfo is executed (after chain of filters) and values are null in that object.
Trying to add a functionality to our JSF 2 application to list active users (who have an active session) and for this I decided to use an application scoped managed bean and store the list of users, adding each by the time of a successful login. Then I would access the active user list (stored on the application scoped managed bean) from a jsf page - only if I could figure out how to resolve the following problem:
The application scoped bean : AppBean.java
#ManagedBean(name = "appBean")
#ApplicationScoped
public class AppBean implements java.io.Serializable {
private List<User> connectedUsers = new ArrayList<User>();
public AppBean() {
}
public List<User> getConnectedUsers() {
return connectedUsers;
}
public void setConnectedUsers(List<User> connectedUsers) {
this.connectedUsers = connectedUsers;
}
}
the Login Bean:
#Named(value = "loginBean")
#SessionScoped
public class LoginBean implements Serializable {
#ManagedProperty("#{appBean}")
private AppBean appBean;
private User userInSession;
public LoginBean() {
}
public String authenticate() {
if (this.authClearsOut()) {
if (appBean != null)
appBean.getConnectedUsers().add(userInSession);
else System.out.println("appBean = null !!!!");
return "/next_screen.xhtml?redirect=true";
}
else return "/login.xhtml?authentication=failed";
}
public AppBean getAppBean() {
return appBean;
}
public void setAppBean(AppBean appBean) {
this.appBean = appBean;
}
}
Now there are two problems here:
1) the appBean is null unless I change line 6 of the LoginBean.java to private AppBean appBean = new AppBean();
2) User userinSession is never added to (List) connectedUsers.
What's wrong here?
The JSF #ManagedProperty annotation works in JSF #ManagedBean only, not in CDI #Named.
Change the LoginBean to be managed by JSF #ManagedBean instead, or change the AppBean beans to be managed by CDI #Named and then use #Inject instead of #ManagedProperty.
I have a controller which is supposed to create version dependend instances (currently not implemented).
#Controller
public class ReportController {
#Autowired
private ReportCompFactory reportCompFactory;
public ModelAndView getReport() {
I_Report report = reportCompFactory.getObject();
^^^^^<- no autowiring in this instance
}
...
}
The Factory looks like this:
#Component
public class ReportCompFactory implements FactoryBean<I_Report> {
#Override
public I_Report getObject() throws BeansException {
return new ReportComp();
}
#Override
public Class<?> getObjectType() {
return I_Report.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
The created instances fields (#Autowired annotated ) are not set.
What should I do, is FactoryBean the right interface to implement?
I would prefer a solution which doesn't involve xml-configurations.
The component itself:
ReportComp implements I_Report {
#Autowired
private ReportDao reportDao;
^^^^^^^<- not set after creation
...
}
}
Spring doesn't perform autowiring if you create your objects. Here are a few options
define the bean to be of scope prototype - this will make the factory redundant (this is applicable in case you simply want instantiation in the factory)
inject the ReportDao in the factory, and set it to the ReportComp via a setter
inject ApplicationContext in the factory and do ctx.getAutowireCapableBeanFactory().autowireBean(instance)