Injecting a CDI interceptor into a beans does not work - java

I have created a CDI (WELD) interceptor that works and intercept what it is supposed to intercept.
#MyInterceptorBinding
#Interceptor
#Dependent
public class MyInterceptor implements Serializable {
private int myIntegerField;
#AroundInvoke
public Object interceptMethod(InvocationContext ctx) throws Exception {
// Do some operations and side effects on myIntegerField;
try {
Object result = ctx.proceed();
return result;
} catch (Exception e) {
throw e;
}
}
public List<Class<?>> getMyIntegerField() {
return myIntegerField;
}
}
Where MyInterceptorBinding is an interceptor binding:
#Inherited
#InterceptorBinding
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyInterceptorBinding {}
I would like to inject my interceptor into a bean class like this:
#ApplicationScoped
public class MyBean implements Serializable {
#Inject
private MyInterceptor interceptor;
public void aMethod(){
int var = interceptor.getMyIntegerField();
// use var in some way...
}
}
but this injection brings to an error:
Unsatisfied dependencies for type MyInterceptor with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private trials.MyBean.interceptor
How can i overcome this issue? Is a problem related to the fact that is an interceptor?
Should i use the CDI portable extension facility? And, if so, how?

You can't inject interceptors because they aren't beans. What you probably can do is create a helper bean and inject that in both the interceptor and the application scoped bean. That helper bean can then contain the value, the interceptor can set it and the other bean can read it.
A warning about scopes: your MyBean is application scoped, which means that there will be only once instance. If the interceptor is used in request scoped beans, what should the value of the interceptor value be? For this to work, the helper bean should most likely also be application scoped, and you should take care of thread safety.

Related

Why do we use a qualifier when we can have a name for the bean?

Why do we use qualifiers with #Bean when we can have different names for different beans of the same type (class)?
#Bean
#Qualifier("fooConfig")
public Baz method1() {
}
Isn't the following code more clean?
#Bean("fooConfig")
public Baz method1() {
}
If I create two beans of the same type with different names (using #Bean annotation), then can we inject them specifically using the #Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?
#Bean("fooConfig")
public Baz method1(){
}
#Bean("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
If the above is true, then where do we use #Qualifier (with #Bean or #Component) instead of giving the bean a name as shown below?
#Bean
#Qualifier("fooConfig")
public Baz method1(){
}
#Bean
#Qualifier("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
Beans have names. They don't have qualifiers. #Qualifier is annotation, with which you tell Spring the name of Bean to be injected.
No.
Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the #Qualifier in that case. Spring is smart enough to find itself.
For example:
#Configuration
public class MyConfiguration {
#Bean
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with #Qualifier annotation.
SIDENOTE: #Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like #Bean("otherComponent").
You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?
This is you interface:
public interface TestRepository{}
This is your implementation 1:
#Repository
public class Test1Repository implements TestRepository{}
Your implementation 2:
#Repository
public class Test2Repository implements TestRepository{}
Now you are injecting it like:
private final TestRepository testRepository;
public TestServiceImpl(TestRepository testRepository) {
this.testRepository= testRepository;
}
QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with #Qualifier which class.
private final TestRepository testRepository;
public TestServiceImpl(#Qualifier("test1Repository") TestRepository testRepository) {
this.testRepository= testRepository;
}
I Prefer different method to not using #Qualifier
Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
Extends to each service
public interface FooService extends CommonFooBar {
}
public interface BarService extends CommonFooBar {
}
Then using it to your class
#Autowired
FooService fooService;
or
#Autowired
BarService barService;
so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.
I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?
Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.
The end result of this is:
Spring will name your beans based on the method used to create them.
If you import a bean, Spring will try to match on the bean name.
If you try to import a bean that does not match the name, Spring will attempt to match the class.
If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.
Lets not over-complicate Spring.
#Bean
mqConnectionFactory() {
ConnectionFactory connectionFactory = new MQXAConnectionFactory();
return connectionFactory;
}
#Bean
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
connectionFactory.setConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(cachingConnectionFactory);
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(pooledConnectionFactory);
...
return container;
}

How to call parameterised constructor present in a class which annotated with #Component inside the another class annotated #Service

I have a requirement where I want to call a parameterised constructor of a class annotated with #Component inside another class which is annotated with #Service
feel free if you didn't get my question.
#Service
Class ServiceClass{
//here I want to create ComponentClass instance by Spring.
Result result=new ComponentClass(sending data to get result);
}
#Component
Class ComponentClass {
Component(received data){
}
}
I think you should try "Autowired" keyword. It says "Hey Spring Framework try to initialize the variable for me".
#Autowired
Result result=new ComponentClass(sending data to get result);
Whenever you define a #Servive or a #Component a bean of that type will be created (keep in mind that all beans are singletons).
A bean can be injected into any other spring managed bean by making use of this annotation:
#Service
Class ServiceClass{
#Autowired
Result result;
}
Using beans (components, services etc.) is not always needed and especially in the case, you need an non-singleton class, things can get tricky because of two reasons:
You won't be able to use annotations on that class
A non-annotated (spring managed bean) class do not support injection.
To inject a bean inside a class that is not annotated you will need to define a spring context:
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
}
and then into any class:
Class ServiceClass{
Result result = SpringContext.getBean(Result.class);
}
where result is a either component or service.

Autowiring class with non SpringBoot managed constructor arguments

As the question suggests, how do you Autowire a class with non SpringBoot managed class as constructor args.
The following is a code block illustrating this:
#Component
class Prototype
{
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype(NonSpringBootManagedBean bean)
{
this.bean = bean;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(NonSpringBootManagedBean bean)
{
// This throws an error saying no bean of type NonSpringBootManangedBean found
Prototype prototype = context.getBean(Prototype.class, bean);
}
}
The reason I am using ApplicationContext to obtain an instance of Prototype instead of using #Autowired is because I need a new instance of Prototype within the method createNewPrototype() every time it's invoked and not a singleton instance (Also, please advise if this way obtaining a new instance is incorrect).
Update:
As others have stated to move my creation of bean to a Java configuration class and adding method annotated by #Bean and instantiating the NonSpringBootManagedBean in the #Bean method. But I think this is not possible as this NonSpringBootManagedBean is passed by caller of PrototypeClient.createNewPrototype().
Update
I have updated my above code example with a more clarity. Please refer this now.
#Component
class Prototype
{
#Autowired
private Repository repository;
// Here Session is part of javx.websocket package and cannot be added as part of
// Java configuration class with a #Bean annotation
// In this case how can I use constructor injection?
private Session session;
Prototype(Session session)
{
this.session = session;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(Session session)
{
Prototype prototype = context.getBean(Prototype.class, session);
}
}
#ServerEndpoint(value = "/resources")
class WebSocketController
{
private PrototypeClient client = ApplicationContext.getBean(PrototypeClient.class);
#OnMessage
void handleMessage(Session session, String message)
{
client.createNewPrototype(session);
}
}
Did you know that you can change your bean scope to be a prototype reference instead of a singleton. That way you can scope a single bean definition to any number of object instances.
https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
private NonSpringBootManagedBean bean = new NonSpringBootManagedBean();
#Bean
public Prototype getPrototype(){
return new Prototype(bean);
}
Spring can not Autowire an Object if it is not aware of it. Some where there need to be #Component or #Bean or some other annotation like #Service etc to tell spring to manage the instance .
Also it is suggested that if you are using a private variable in Autowire it should be part of constructor(for constructor injection ) or a setter method must be provided(setter injection)
To solve your error : you can create a java config class and place it in you base pkg (same as #SpringBootApplication or add #ComponentScan("pkg in which config is present") on class with #SpringBootApplication)
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
#Configuration
public class myconfig {
#Bean
public NonSpringBootManagedBean nonSpringBootManagedBean()
{
return new NonSpringBootManagedBean();
}
}
Define a bean with scope prototype
That is each time injected as new instance.
In SpringBoot you can use the annotation #Scope("prototype") to your bean class Prototype.
#Component
#Scope("prototype")
class Prototype {
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype() {
// you can only modify this 'NonSpringBootManagedBean' later
// because Spring calls constructor without knowing NonSpringBootManagedBean
this.bean = new NonSpringBootManagedBean();
// do something with 'repository' because its defined
}
public void setNonSpringBootManagedBean(NonSpringBootManagedBean bean) {
this.bean = bean;
}
}
Use instances of this bean
Via injection (e.g. #Autowired to constructor) you can use different instances of this prototypical bean within other beans.
#Component
class PrototypeClient {
// ApplicationContext still used?
#Autowired
private ApplicationContext context;
private Prototype prototypeInstance;
#Autowired // injects the new instance of Prototype
public PrototypeClient(Prototype p)
this.prototypeInstance = p;
// here you can change the NSBMB
modifyPrototype();
}
private void modifyPrototype(NonSpringBootManagedBean bean) {
this.prototypeInstance.setNonSpringBootManagedBean( new NonSpringBootManagedBean() );
}
}
Why is your exception thrown?
no bean of type NonSpringBootManangedBean found
Spring complains when trying to instantiate the bean of type Prototype
Prototype prototype = context.getBean(Prototype.class, bean);
because for calling its constructor it needs to pass an argument of type NonSpringBootManagedBean. Since all this bean-instantiating is done internally by Spring(Boot), you can not intercept and tell Spring: "Hey, use this bean of class NonSpringBootManagedBean" like you tried in method createNewPrototype(NonSpringBootManagedBean bean).
Why could'nt the NonSpringBootManagedBean be managed as bean by Spring(Boot)?
Autowiring in SpringBoot is a way of dependency-injection. This means a bean has been previously instantiated by SpringBoot, automatically at startup (when Spring boots). And this bean is now injected as dependency into another bean, etc. because this other bean depends on it.
If you tell us some more background, we could possibly bring light into your situation. This can be some answers to:
What is NonSpringBootManagedBean and why is it no managed bean?
What is Prototype and for which purpose does it use NonSpringBootManagedBean?
What is PrototypeClient and why does it create its own Prototype ?
I am not sure if I have understood the relationship and purpose between your objects/classes.

Can #PostConstruct be used as a substitute of #Bean/#Produces?

I was reading this post when I thought about the possibility of substituting any #Bean (Spring DI) or #Produces (CDI) with a simple #PostConstructor as in the following CDI example:
Replace:
public class MyClassFactory {
#Produces
#RequestScoped
public MyClass createMyClass() {
MyClass myClass = new MyClass();
myClass.setA(1);
return myClass;
}
}
public class MyClass {
private int a;
private void setA(int a) {
this.a = a;
}
}
With:
public class MyClass {
#PostConstruct
public void init() {
this.setA(1);
}
private int a;
private void setA(int a) {
this.a = a;
}
}
Is that correct? What are the differences between those options?
No, #PostConstruct defines an interceptor for the initialization of a bean. It cannot be used to define the instantiation of a bean.
I dont see this as an incorrect question.
#PostConstruct method is invoked after the default constructor invocation and dependency injection (if any) are finished. You can initialize instance variables of a bean using the within a #PostConstruct annotated method. In this case, the difference b/w using #PostConstruct and CDI is that, the instance returned by CDI is a contextual one and since it is #RequestScoped, the lifetime of the 'produced' bean is confined to one HTTP request. A new instance of the bean would be produced once the existing HTTP request is complete. In case of #PostConstruct, its an #Dependent scoped bean (by default) and its life cycle would 'depend' upon the bean which injects it (using CDI #Inject)

how to use an Object injected with #Inject within a server endpoints #OnOpen method

I have a Bean, which creates an instance of an object, which i need to inject into other beans. Everything works fine, i can print a property of the injected object in the #PostConstruct method, but if i try to call a method from that injected object inside the #OnOpen method of a ServerEndpoint it gives me a NullPointerException.
Here is my ServerEndpoint
#Named
#ApplicationScoped
#ServerEndpoint(value = "/websocket")
public class BeanThree {
private String message = "test";
#Inject private User user;
#PostConstruct
public void init() { System.out.println(user.getUserName()); } <-- displayed in the console correctly
public String getMessage() { return this.message; }
#OnOpen
public void onOpen(Session session) {
System.out.println("onOpen");
System.out.println(user.getUserName()); <-- causes NullPointerException
}
}
Is it possible to fix this?
Edit1:
Im using cdi 1.2, jetty 9.1, jsf 2.2, java-ee7 and websockets from java-ee7
The problem is that BeanThree is declared both a CDI bean and an endpoint at the same time.
It has to be split into two different beans:
#ServerEndpoint("/endpoint")
public class BeanThree {
#Inject
ApplicationScopedBean bean;
#OnOpen
public String onOpen(Session s) { System.out.println(bean); }
#OnMessage
public String onMessage(String message) { System.out.println(bean); }
}
#ApplicationScoped
public class ApplicationScopedBean { ... }
But there's another issue.
CDI / Websocket integration is very limited: out of the box you can inject #ApplicationScoped and probably #Dependent beans only.
From your snippet it seems you intend to use #SessionScoped User bean withing a Websocket session. That's not going to work because Websocket and HTTP sessions differ.
You'll have to manage Websocket sessions and session-bound data by yourself. Here's an example.
One way to do this is to allow CDI to instantiate it as a CDI bean.
Then subclasses the following classes: ServerEndpointConfig.Configurator
#ServerEndpoint(..., configurator=MyCustomConfigurator.class)
#SessionScoped
#Named("myMessageHandler")
public class MyMessageHandler{
#Inject
private MyInjectable instance;
...
}
public class MyCustomConfigurator extends ServerEndpointConfig.Configurator{
public <T extends Object> getEndpointInstance(Class<T> endpointClass) throws InstantiationException{
//do cdi lookup for endpoint using the simple name.
}
To have a reference to the cdi BeanManager, have a look at this thread:
http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/
In your case, you dont have a reference to FacesContext, so use the ServletContext

Categories

Resources