I'm following the spring tutorial.
In section "3.2. Add some classes for business logic" an interface ProductManager is created:
package springapp.service;
import java.io.Serializable;
import java.util.List;
import springapp.domain.Product;
public interface ProductManager extends Serializable{
public void increasePrice(int percentage);
public List<Product> getProducts();
}
Then a SimpleProductManager implementation class is created:
package springapp.service;
import java.util.List;
import springapp.domain.Product;
public class SimpleProductManager implements ProductManager {
public List<Product> getProducts() {
throw new UnsupportedOperationException();
}
public void increasePrice(int percentage) {
throw new UnsupportedOperationException();
}
public void setProducts(List<Product> products) {
throw new UnsupportedOperationException();
}
}
The implementation class adds an extra method setProducts(). Should the interface ProductManager not also have a setProducts method to allow classes which use setProducts to instantiate SimpleProductManager polymorphically. Currently this is not possible -
ProductManager p = new SimpleProductManager();
p.setProducts();
The interface does not include setProducts because the clients of that interface (probably an MVC controller) are not supposed to call it. The interface defines only those operations that clients are supposed to use, rather than defining all of the methods that the implementation may have.
The setProducts method will be accessible to the beans configuration (e.g. using <property name="products">), which allows the products to be statically configured at start up. After that, client code refers to the bean via its restricted interface.
Your p.setProducts() example should never be called in this example, since the products are only configured in the beans config, not by business logic.
Related
I am playing with an idea to use a similar approach that #Configuration classes are able to do, that they can lazily create beans with calls to #Bean methods and return the existing objects if already called. This is done through some magic with CGLib proxies.
One particular interesting thing is that it works even when calling the method on itself:
#Configuration
class Config {
#Bean ClassA beanA() {
return new ClassA(beanB());
}
#Bean ClassB beanB() {
return new ClassB();
}
}
Now, in my use case, not concerning Spring configuration, I want to use this ability to lazily create arbitrary object graphs (which should not be Spring Beans) by calling a method of a Builder bean that would create the objects if not yet called, and returning existing objects if already called. And as well I want to leverage the ability to self-invoke methods on the same instance. So far, I wasn't able to do this.
How can I create and enhance Spring Beans (as CGLib proxies) so that they are able to self-invoke methods, similarly the #Configuration classes do, but with my own custom advice handling the laziness and caching?
EDIT : more detail
The result, in the end, should look similar to the configuration example above, but it would be a normal Spring singleton bean:
#Component
#MyBuilder // or some other custom annotation
class MyObjectGraphBuilder {
#Builder ClassA objectA() {
return new ClassA(objectB());
}
#Builder ClassB objectB() {
return new ClassB();
}
}
With the added capability to only call the original method once, and caching the result for any subsequent call (including especially the self-invocation). The above is just an example, there may be many such builder beans, and they can be complex with cross-dependencies between them.
The method call result caching is simple (could be done by AOP), but what I want is the self-invocation capability, which is normally not supported by Spring unless it's a #Configuration class.
I figured that Spring is doing this by enhancing the #Configuration bean classes with their own CGlib proxies. However, it involves a lot of copying and customizing (e.g. ConfigurationClassEnhancer, ConfigurationClassPostProcessor, etc), and so far I had no luck of actually making it work with my custom Post Processor and Enhancer (the code is too long, but it's basically a copy of the mentioned classes and writing my custom method interceptors). So I'm trying to find if there exists any other way.
The simple answer concerning AOP and self-invocation is: You cannot use Spring AOP, you have to use full AspectJ. The good news is that you don't require any proxies for that solution. The Spring manual describes how to use AspectJ from Spring via LTW (load-time weaving). Don't worry, if configured correctly you can use AspectJ alongside other aspects implemented via Spring AOP. Besides, if you don't like LTW, you can also use compile-time weaving via AspectJ Maven plugin.
Now here is a little caching example in pure Java + AspectJ (no Spring involved) for demonstration:
Builder annotation:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface Builder {}
Sample classes:
package de.scrum_master.app;
public class ClassB {
#Override
public String toString() {
return "ClassB#" + hashCode();
}
}
package de.scrum_master.app;
public class ClassA {
private ClassB objectB;
public ClassA(ClassB objectB) {
this.objectB = objectB;
}
#Override
public String toString() {
return "ClassA#" +hashCode() + "(" + objectB + ")";
}
}
Driver application with annotated factory methods:
package de.scrum_master.app;
public class MyObjectGraphBuilder {
#Builder
ClassA objectA() {
return new ClassA(objectB());
}
#Builder
ClassB objectB() {
return new ClassB();
}
public static void main(String[] args) {
MyObjectGraphBuilder builder = new MyObjectGraphBuilder();
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
}
}
Console log without caching aspect:
ClassB#1829164700
ClassA#2018699554(ClassB#1311053135)
ClassB#118352462
ClassA#1550089733(ClassB#865113938)
ClassB#1442407170
ClassA#1028566121(ClassB#1118140819)
So far, so predictable. This is the normal behaviour, no caching at all.
Caching aspect:
Now this aspect is really simple. There is no thread-safety, no way to create multiple named beans of the same class or anything similar, but I guess you can take it from here, the principle stays the same.
package de.scrum_master.app;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
#Aspect
public class BuilderCacheAspect {
private Map<Class<?>, Object> cachedObjects = new HashMap<>();
#Around("#annotation(de.scrum_master.app.Builder) && execution(* *(..))")
public Object findOrCreateObject(ProceedingJoinPoint thisJoinPoint) throws Throwable {
//System.out.println(thisJoinPoint);
Class<?> returnType = ((MethodSignature) thisJoinPoint.getSignature()).getReturnType();
Object cachedObject = cachedObjects.get(returnType);
if (cachedObject == null) {
cachedObject = thisJoinPoint.proceed();
cachedObjects.put(returnType, cachedObject);
}
return cachedObject;
}
}
Console log with caching aspect:
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
Tadaa! There is our simple object cache. Enjoy.
I have a Spring bean defined like this:
package org.behrang.sample;
import foo.AbstractThirdPartyClass;
#Component
public class SampleBean extends AbstractThirdPartyClass<Input, Output> {
#Override
public Optional<Output> process(Input input) {
}
}
The AbstractThirdPartyClass class is defined in a third-party library named foo.
I want to implement an advice that applies to all methods in the
org.behrang.sample package, so I have implemented something like this:
#Aspect
#Component
public class SampleAspect {
#Before("execution(public * org.behrang.sample..*.*(..))")
public void sampleBefore(JoinPoint joinPoint) {
}
}
However this is not advising SampleBean::process(Input). If I remove the extends AbstractThirdPartyClass<Input, Output>
part, then the process method is advised.
Is there an elegant way to workaround this problem? For example, I can define an interface in org.behrang.sample with one method like this:
public interface Sampler<I, O> {
public Optional<O> process(I input);
}
And make the SampleBean implement it too. But this is way too ugly and anti-DRY.
Also I have enabled AOP using #EnableAspectJAutoProxy(proxyTargetClass = true) as manu beans defined in this project are not implementing any interfaces.
I am not so experienced in EJBs, especially EJB 3.0 and thus, I faced out with a question I would like to resolve. A similar issue I have found here, but the suggested solution did not help.
I have a remote stateless EJB with its business methods declared in interface and the bean which implements those methods has also other methods which are not declared in interface.
As an example here is the business interface:
public interface BusinessLogic {
Object create();
void delete(Object x);
}
A BusinessLogicBean which implements the business logic:
#Stateless
#Remote(BusinessLogic.class)
public class BusinessLogicBean implements BusinessLogic {
/** implemented method */
public Object create() {
Object x = new SomeDBMappedObject();
// create an object in DB and return wrapper class
...
return x;
}
/** implemented method */
public void delete(Object x) {
// deleting object from DB
...
}
/** The method that performs some extra logic */
public void aMethod() {
// do extra logic
}
}
I need to write unit tests for that EJB using Arquillian framework including for the bean methods which are not declared in the business interface.
Example:
#RunWith(Arquillian.class)
public class BusinessLogicTest {
/** will be injected during the test run */
#EJB
private BusinessLogic businessLogic;
#Deployment
public static Archive createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
// add needed libraries and classes
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
return war;
}
#Test
public void aMethodTest() {
businessLogic.aMethod();
// Write appropriate assertions
}
}
My questions are:
how can I call aMethod() in my test?
I cannot call it like businessLogic.aMethod();, since it will result in a compilation error.
I cannot call it like ((BusinessLogicBean) businessLogic).aMethod();, as it will result in a ClassCastException since the actual object is a com.sun.proxy.$ProxyXXX type.
or
Is there a way to inject BusinessLogicBean object directly instead of BusinessLogic?
You can annotate BusinessLogicBean with #javax.ejb.LocalBean
#Stateless
#LocalBean
#Remote(BusinessLogic.class)
public class BusinessLogicBean implements BusinessLogic {
...
}
and inject it by its class name:
#EJB BusinessLogicBean businessLogicBean;
See also:
Docs
A useful related question
I have to implement DAOs which use only the interfaces of the objects. Now I'm having trouble figuring out how to use the em.find()of the EntityManagerclass.
My specific question is, if it is ok to import the implementation of a class directly into the DAO like in this example:
import dao.IStreamingServerDAO;
import model.IStreamingServer;
import model.impl.StreamingServer;
import javax.persistence.EntityManager;
public class StreamingServerDAO implements IStreamingServerDAO {
protected EntityManager em;
public StreamingServerDAO(EntityManager em) {
this.em = em;
}
#Override
public IStreamingServer findById(Long id) {
return em.find(StreamingServer.class, id);
}
}
I feel like I'm hurting some privacy principles by simply importing the model.impl.StreamingServer class into the DAO.
Problem is I don't know how else I'm supposed to get the needed class for the em.find()method.
Please not that I can't change the return type of the findByIdmethod as it's defined like this by the interface. (Also this implementation right now works as expected).
I have this interface and simple implementation:
public interface Data {
}
import java.nio.file.Path;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class SimpleData implements Data {
#Inject
public SimpleData(#Assisted #Nullable Path path) {
}
}
I want to generate a Factory with different methods using guice.
import java.nio.file.Path;
import javax.annotation.Nullable;
public interface Factory {
Data create();
Data load(#Nullable Path path);
}
But the following module configuration:
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector(
binder -> binder.install(
new FactoryModuleBuilder().implement(Data.class, SimpleData.class)
.build(Factory.class)));
Data data = injector.getInstance(Factory.class).create();
}
}
fails:
Exception in thread "main" com.google.inject.CreationException: Guice creation errors:
1) No implementation for java.nio.file.Path annotated with #com.google.inject.assistedinject.Assisted(value=) was bound.
while locating java.nio.file.Path annotated with #com.google.inject.assistedinject.Assisted(value=)
for parameter 0 at SimpleData.<init>(SimpleData.java:10)
at Factory.create(Factory.java:1)
at com.google.inject.assistedinject.FactoryProvider2.initialize(FactoryProvider2.java:539)
at com.google.inject.assistedinject.FactoryModuleBuilder$1.configure(FactoryModuleBuilder.java:335)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:435)
at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:175)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:109)
at com.google.inject.Guice.createInjector(Guice.java:95)
at com.google.inject.Guice.createInjector(Guice.java:72)
at com.google.inject.Guice.createInjector(Guice.java:62)
at Main.main(Main.java:9)
I solved my problem using the annotation #AssistedInject. Quote from the javadoc:
When used in tandem with FactoryModuleBuilder, constructors annotated with #AssistedInject indicate that multiple constructors can be injected, each with different parameters.
So i add the annotation and a constructor to the SimpleData class:
public class SimpleData implements Data {
#AssistedInject
public SimpleData(#Assisted Path path) {
}
#AssistedInject
public SimpleData() {
}
}
i removed the #Nullable annotation from the factory:
import java.nio.file.Path;
public interface Factory {
Data create();
Data load(Path path);
}
#Nullable does not mean that if you don't have a binding, then null will be injected. It only allows writing bindings to null. If you don't have a binding and there is no applicable JIT-binding, then injection will fail.
Your factory's create() method requires Guice to find an #Assisted Path binding, but it obviously can't find it since you've never created one, so it fails.
Honestly, I'm not sure if there is a clean way to implement such defaulting. Ideally you should mark Path with some binding annotation and add a default binding to null for it, but #Assisted already is a binding annotation, and it is not possible to have multiple binding annotations on a single injection point. You can try creating a binding for #Assisted Path:
binder.bind(Path.class).annotatedWith(Assisted.class).toInstance(null);
However, I'm not sure if it would work because Assisted can be special to Guice. And even if it will work, it is not very clean - there may be conflicts with other assisted factories accepting Paths.
I would have Guice implement some kind of internal factory interface, then expose something else. Like this:
interface InternalFactory {
Data load(#Nullable Path path);
}
public interface Factory {
Data load();
Data load(#Nullable Path path);
}
class FactoryImpl implements Factory {
#Inject InternalFactory internalFactory;
#Override
public Data load() {
return load(null); // Pass your defaults here
}
#Override
public Data load(#Nullable Path path) {
// Sadly you'll have to explicitly forward arguments here, but it's not
// too bad IMO
return internalFactory.load(path);
}
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder()
.implement(Data.class, SimpleData.class)
.build(InternalFactory.class));
bind(Factory).to(FactoryImpl.class);
}
}