Understanding Security Type in CDI

First of all, I must clarify that this post is not intended to criticize CDI, but to discover the thoughts and assumptions underlying the design of CDI, and this will have an obvious impact on the design of any web application that uses CDI.

One of the most distinctive features of CDI (Java EE 6) is type safety. Jboss Seam was not type safe. He uses the name to qualify any instance for injection. Like a roar:

@Name("myBean") public class MyBean implements Bean { ... } @Name("yourBean") public class YourBean implements Bean { ... } 

With the introduction of MyBean, this can be done:

  @In private Bean myBean; //myBean is injected @In private Bean yourBean; //yourBean is injected 

And earlier versions of Spring (before 3.0), this type of injection occurred as shown below:

Just define the beans configuration file in the bean:

  <bean id="myBean" class="com.example.common.MyBean"> ... </bean> <bean id="yourBean" class="com.example.common.YourBean"> ... </bean> 

And use the named qualifier, deciding which one to use:

  @Autowired @Qualifier("myBean") private Bean bean; @Autowired @Qualifier("yourBean") private Bean bean; 

But now in CDI you first need to define a Qualifier custom annotation for any particular type of object. Then use this annotation to qualify this object. At the end of the day, when you look at the source code, you see that you spent a lot of time writing a lot of custom annotations for dependency injection. The Java community is moving towards annotations, leaving XML-based XML configurations ( verbose XML ). Is there anything that could convince anyone to think of it (type of security with custom annotations) not as detailed annotations , but as an excellent and distinctive feature of CDI?

Edit:

Points advanced to highlight

  • If I use a special classifier for type safety for a service or a Tao (for each interface), then for a large application, such as 1000 or more classes of service or a Tao with several implementations, this will be messy. Then for large applications, is it possible to use a safe type injection?
  • If the answer to the above question is “No,” then what's the point of using type safety?
  • Even if it is possible to write annotations for type safety in large applications, is it really worth trying to avoid verbose xml configuration?
  • When do I really need type safety instead of a bean name classifier?

A brief discussion of the above points

  • There are not many cases where you really need a safe type injection, especially when you have one implementation of the interface, you should use @Name as a determinant. So yes, in a large application, you can use type safety when it's really needed.
  • Service type security is one of the distinguishing features of CDI, and the accepted answer has a non-exhaustive list of reasons why you can use type security.
  • As a smart programmer, you know exactly when to use type safety, so it is definitely worth the effort when it is really needed.
  • Most parts of the accepted answer really say when we need type safety and this article is also very helpful to understand.

Thanks and happy coding!

+8
java-ee dependency-injection jsf seam cdi
source share
3 answers

Is the CDI detailed? Are qualifiers needed?

  • First, you do not need qualifiers if you have only one interface implementation.
  • If you have several interface implementations, ask yourself - do you need to distinguish them after deployment?

    • If the answer is no, consider alternatives.
    • If so, you still don't need qualifiers. That's why:

      To use your own example:

       public class MyBean implements Bean { ... } public class YourBean implements Bean { ... } 

      Then you just do:

       @Inject MyBean bean; 

      or

       @Inject YourBean bean; 

      If you do not like your instance variables of a particular type and it is better to see the interface than to do this:

       private Bean bean; @Inject public void setBean(MyBean bean) { this.bean = bean; } 

      or

       private Bean bean; @Inject public void setBean(YourBean bean) { this.bean = bean; } 

      In all of the above cases, it is fully selected for free, absolutely safe in type and is definitely not detailed.

  • Then, in order to dwell on the last question - does the developer need to choose the appropriate implementation or can it be problematic?

    • If the developer chooses, then do as described above at 2.
    • If the choice can be problematic, then use the manufacturer:

       @Produces public Bean obtainTheAppropriateBean(InjectionPoint ip) { if (meetsConditionA(ip)) { return getBeanImplA(); } else if (meetsConditionB(ip)) { return getBeanImplB(); } else if (...) { ... } else { return getDefaultBeanImpl(); } } 

      Still the selector is free, safe in type, and perhaps not yet detailed (choice for automation for automation).

    See this article for an excellent extension of this point and ideas on how to use the InjectionPoint API.

Are qualifiers required?

I see this question arising after the examples above. The answer is yes, and here is not an exhaustive list of reasons why you can use them:

  • In one of the examples above, I mentioned the introduction of specific implementations of the interface to avoid the use of qualifiers. This is perfectly normal when the code is internal and internal developers will know what is. But what if the code is a library or a framework and you do not want to reveal any specific implementation in the public API? Then identify some qualifiers and document them well. How is this different from verbose XML? Despite the fact that you, as a library writer, can do the same verbose work, your users will not have to. Instead, they simply write one word above the injection point and will be happy that you did not force them to write any XML - I personally would be very very happy. :)
  • In the manufacturer's example above, you can cover most cases, but not all with logic in the producer method. Or maybe you just need the ability to override this logic at any particular injection point. Then hold the producer, create a qualifier and comment on a specific implementation with it. Then use the qualifier if you do not want the manufacturer logic to execute.
  • Imagine a situation where you have multiple interfaces and multiple implementations. Private implementations may have common distinguishing features, and these features are common to all of your interfaces. As an example, let's look at Framework Java Colections, in particular, the List, Set, and Map interfaces. Each of them has several implementations, but there are common features in all or some of the interfaces. For example, related nodes (fast iteration) - think LinkedList, LinkedHashSet, LinkedHashMap; sorted (ordered) - I think TreeSet, TreeMap; hash table (quick insert / delete / contains) - I think, HashSet, LinkedHashSet, HashMap, LinkedHashMap; connect at the same time; random access etc. You can now define @Linked , @Sorted and @Hash . Then we introduce:

     @Inject @Linked @Hash private Map map; @Inject @Sorted private Map map; @Inject @Hash private Set set; @Inject @Hash private Set set; 

    Now, is it worth doing for the collections framework? I would not do this, but I have a case similar to what I am describing here in my current project at work (sorry, I can’t discuss it).

  • And finally, you can use qualifiers to pass parameters to manufacturers in combination with @Nonbinding . Continuing the collection structure described above, define:

     @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Hash { @Nonbinding int capacity() default 16; @Nonbinding float loadFactor() default 0.75f; } 

    Thus, you can transfer the required capacity and load factor so that the producer returns any hash table based as follows:

     @Inject @Hash(capacity = 256, loadFactor = 0.85f) private Set set; @Inject @Hash private Set set; @Inject @Hash(capacity = 8, loadFactor = 0.65f) private Map map; 

Hope this answers your question. This, of course, is part of why I love CDI.

+14
source share

When do I need to use custom qualifiers?

Custom CDI qualifiers should only be used when: (a) your (non-annotative) code intentionally introduced ambiguity in the type used; (b) you want to enter a more specific type of object than indicated in the code; (c) you want a more flexible type selection and configuration than that provided by the predefined classifier @Named.

Qualifiers eliminate the ambiguity of the type that should be created / entered in the CDI.
(a) and (b) arise when you use high-level types in your code to provide a powerful general algorithm that can be reused and flexibly tuned. For example. it is often possible and it is recommended that you encode the entire algorithm against an interface rather than a specific class — for example, algorithms against a list, map, or queue. But often, these algorithms only work well if we capture specific implementations of the interface, such as SortedList, TreeMap, or PriorityQueue. If the CDI is to act as a factory object, performing the lifecycle management of the object, initializing the correct objects to control injections and dependencies, then he should know the full details.

Custom qualifiers are intelligent and customizable in how they disambiguate the type that CDI creates / introduces. @Named is a much lighter tool. For (c), when I mention "type selection", I mean that a developer can combine several qualifiers together to "logically select" the most suitable type without naming the exact class. @Named effectively requires an exact class nomination - multiple qualifiers will let CDI work for you. Configurability refers to the ability to change the behavior of CDI during deployment by modifying beans.xml (no need to change code / annotations). that is, it can logically configure what CDI does, and it can do this until deployment time - without touching the code or annotations.

When do I need to declare custom qualifiers?

Custom CDI qualifiers do not need to be created for each individual type that can be entered. This is the most important.

Several qualifiers can be used for both injection points and type declarations. CDI matches bean type plus SET OF qualifiers when determining input type. To reduce the number of qualifiers for an ad, it is recommended that you include types in the number of attributes / problems that describe them. Then instead of one qualifier for each type there can be one qualifier for each type attribute - even if such an attribute is multi-valued, for example, Hash / Tree / Enum (see the next paragraph).

Secondly, qualifiers should use the parameters wisely to further reduce the number of declarations required. Instead of creating 10 qualifiers, you can have one qualifier that has a parameter that is a string or, preferably, an enumeration that can take 10 values.

The combination of these two ideas (qualifiers = type attribute PLUS uses qualifier parameters) in the example for the collection library: can types be described using repespenting Sorted / Non-Sorted attributes? Hash / Tree / Array? Linked / Unlinked? If I specify a variable

 @Inject @Sorted @Organisation("hash") @Navigation("linked") Map myMap; 

then I have a strong choice of type, because each of these aspects is guaranteed to be satisfied, I have a sorted linked hash map, not knowing that this requires a special class in a particular package.

Points have not yet been addressed:

If I use some annotation with the parameterized name bean, how is it really safe?

Strong typing is guaranteed because there must be a complete match with:

  • bean type
  • set of qualifiers
  • set of parameter values

No injectable type can violate any of these things. Instead of thinking about matching types for a single string (package_name.class), consider it to match multiple strings / values ​​that are completely controlled by the developer, these same strings / values ​​are used for both declaration and injection, providing a safe match. Also, instead of thinking that you should correspond to a specific lower-level class, remember that you can correspond to higher-level classes in the inheritance hierarchy and the intelligent use of @Default qualifiers for your types (remember Java EE 6 "smart default settings ")?) will take you to your preferred specific types.

If I use a special classifier for type safety for a service or a Tao (for each interface), then for a large application, such as 1000 or more classes of service or a Tao with several implementations, this will be messy. Then for large applications, is it possible to use a safe type injection?

  • 1000 or more classes of service or tao? Indeed??! This would often mean a big design problem right away. If this is not a single application of super-mega-dimensionality-record-attempt for a complex business such as the tax department or NASA. Even then, it would be normal to break Java EE / SOA into smaller modules with much fewer classes. If this is what you have, you can take a look at simplifying your design - you can have much bigger problems than defining scroll determinants and the past ...

  • Qualifiers are needed only where type ambiguity exists - that is, many classes of ancestors / descendants in the hierarchy of the same type. It often happens that a relatively small number of service classes or DAOs are ambiguous.

  • As described above, do not use the one-qualifier-per-implementation-class template: instead, refactoring uses the qualifier-per-type attribute, and use the imprint parameters templates for each classifier

  • You can use safe type injection with large applications.

If the answer to the above question is “No,” then what's the point of using type safety?

  • The answer is yes.
  • the proposed scenario (1000+ services / DAO classes) is very rare.

Even if it's possible to write annotations for type safety in large applications, is it really worth the effort to just avoid the detailed xml configuration?

The goal of CDI is not only to “avoid a detailed xml configuration”, CDI has the following objectives:

  • Support for areas (including the new web chat area) with object lifecycle management
  • Type dependency injection mechanism, including the ability to select dependencies at the development or deployment stage without detailed configuration
  • Support for Java EE modularity and Java EE component architecture - The modular structure of a Java EE application is considered when resolving dependencies between Java EE components
  • Integration with the Unified Expression Language (EL), allowing you to use any context object directly on the JSF or JSP page
  • Ability to decorate injectable objects
  • Ability to associate interceptors with objects through interceptor type bindings
  • Event Notification Model
  • Web chat context in addition to the three standard web contexts defined by the Java Servlets specification.
  • SPI allowing portable extensions to integrate with the container

This WAY is different and more useful than the basic XML configuration file turned into annotations - even earlier basic annotations for embedding Java EE 5 resources were different and more useful than the XML version turned into annotations.

+3
source share

No need to create custom @Qualifier every time. It is enough to create only one with some parameter. CDI will treat @CustomQualifier("myBean") and @CustomQualifier("yourBean") as different qualifiers.

In addition, such a qualifier is already in the CDI package - @Named

But sometimes static classifiers are very useful, for example (see @Alternative ):

 @Alternative @Qualifier @Documented @Retention(value=RUNTIME) public @interface Mock 

and you can mark any beans as @Mock for testing use only (remember to include the alternative section in beans.xml in the test class path.

+2
source share

All Articles