Here goes a command object which needs to be populated from a Spring form
public class Person {
private String name;
private Integer age;
/**
* on-demand initialized
*/
private Address address;
// getter's and setter's
}
And Address
public class Address {
private String street;
// getter's and setter's
}
Now suppose the following MultiActionController
#Component
public class PersonController extends MultiActionController {
#Autowired
#Qualifier("personRepository")
private Repository<Person, Integer> personRepository;
/**
* mapped To /person/add
*/
public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Person person) throws Exception {
personRepository.add(person);
return new ModelAndView("redirect:/home.htm");
}
}
Because Address attribute of Person needs to be initialized on-demand, i need to override newCommandObject to create an instance of Person to initiaze address property. Otherwise, i will get NullPointerException
#Component
public class PersonController extends MultiActionController {
/**
* code as shown above
*/
#Override
public Object newCommandObject(Class clazz) thorws Exception {
if(clazz.isAssignableFrom(Person.class)) {
Person person = new Person();
person.setAddress(new Address());
return person;
}
}
}
Ok, Expert Spring MVC and Web Flow says
Options for alternate object creation include pulling an instance from a BeanFactory or using method injection to transparently return a new instance.
First option
pulling an instance from a BeanFactory
can be written as
#Override
public Object newCommandObject(Class clazz) thorws Exception {
/**
* Will retrieve a prototype instance from ApplicationContext whose name matchs its clazz.getSimpleName()
*/
getApplicationContext().getBean(clazz.getSimpleName());
}
But what does he want to say by using method injection to transparently return a new instance ??? Can you show how i implement what he said ???
ATT: I know this funcionality can be filled by a SimpleFormController instead of MultiActionController. But it is shown just as an example, nothing else
I'm pretty sure he means using the lookup-method system as documented in chapter 3 of the spring reference manual
Only down side is that <lookup-method> requires a no arg method rather than the newCommandObject(Class) method of MultiActionController.
this can be solved with something like:
public abstract class PersonController extends MultiActionController {
/**
* code as shown above
*/
#Override
public Object newCommandObject(Class clazz) thorws Exception {
if(clazz.isAssignableFrom(Person.class)) {
return newPerson();
}
}
public abstract Person newPerson();
}
in the context file:
<bean id="personController" class="org.yourapp.PersonController">
<lookup-method name="newPerson" bean="personPrototype"/>
</bean>
The down side is that using this sort of thing is you are kinda stuck with configuring the controller bean via xml it's not possible (certainly in < 3) to do this with annotations.
Related
I have created REST controller with base request mapping on class.
#RestController
#RequestMapping(".../{type}/{typeId}/param..")
public class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
/*
public Type getType(???){
return type;
}
*/
}
Next I have enum Type which determines what service will be used by proxy service (ie. proxy has injected list of serivces and gets one that supports type). I am wondering if there is any way how to make part of request mapping {type} and get it in getter method below so I don't have to repeat it in every request mapping in this class.
I only figured one alternative solution - make this class abstract and then extend it and return constant. This would however leave me with lot of classes without any added value. For example:
#RequestMapping(".../{typeId}/param..")
public abstract class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
protected abstract Type getType();
}
#RestController
#RequestMapping("/typeAbc)
public class TypeAbcFooController extends FooController{
public Type getType{
return Type.Abc;
}
}
So is it possible to bind #PathVariable from URL specified on class #RequestMapping in some shared method? Thanks
I hope i've understood your problem, but one way of improving your design could be to implement a strategy per type, to inject them, and to use them corresponding to your type received in your controller.
Exemple:
public enum MyType {
TYPE1,
TYPE2
}
public interface IService {
MyType getHandledType();
List<Foo> getFoo();
}
#Service
public class Type1Service implements IService {
#Override
public MyType getHandledType() {
return MyType.TYPE1;
}
#Override
public List<Foo> getFoo() {
// IMPLEMENTATION FOR TYPE1;
}
}
public class FooController{
#Autowired
List<IService> services;
public List<Foo> getFoo(MyType requestType){
IService service = services.stream().filter(iService -> iService.getHandledType() == requestType).findFirst().get();
return service.getFoo();
}
}
This way your controller is agnostic of the underlying service implementation, which is a big responsability.
CONTEXT:
I process reports with #Scheduled annotation and when invoke Component from Service property not getting initialized with #Value annotation even it physically exists in .properties and printed out in #PostConstruct.
DESCRIPTION:
ReportProcessor interface and InventoryReportProcessor implementation:
#FunctionalInterface
interface ReportProcessor {
public void process(OutputStream outputStream);
}
#Component
public class InventoryReportProcessor implement ReportProcessor {
#Value("${reportGenerator.path}")
private String destinationFileToSave;
/*
#PostConstruct
public void init() {
System.out.println(destinationFileToSave);
}
*/
#Override
public Map<String, Long> process(ByteArrayOutputStream outputStream) throws IOException {
System.out.println(destinationFileToSave);
// Some data processing in here
return null;
}
}
I use it from
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
My confusion comes from the fact that #Value in Service works fine but in #Component it returns null unless call in #PostConstruct. Also, if call #PostConstruct the value is still remains null in the rest of the class code.
I found similar Q&A and I did research in Srping docs but so far no single idea why it works this way and what can be a solution?
You need to Autowire the component to make your spring application aware of the component.
#Service
public class ReportService {
#Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
#Autowired
private ReportProcessor reportProcessor;
#Scheduled(cron = "*/10 * * * * *")
public void processReport() {
//InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
Field injection is done after objects are constructed since obviously the container cannot set a property of something which doesn't exist.
at the time System.out.println(destinationFileToSave); triggers values are not being injected;
if you want to see it working try something like this
#Autowired
InventoryReportProcessor pross;
pross.process(ByteArrayOutputStream outputStream);
#PostConstruct works as it is being called after the object creation.
Spring will only parse #Value annotations on beans it knows. The code you use creates an instance of the class outside the scope of Spring and as such Spring will do nothing with it.
One thing you can do is to create the instance explictly or use Autowire:
#Autowired
private ReportProcessor reportProcessor;
tl:dr If you have configured your application context correctly then a #Value cannot be null as that will stop the correct startup of your application.
Change your Code from
#Value("${reportGenerator.path}")
private String destinationFileToSave;
to
#Value("${reportGenerator.path}")
public void setDestinationFileToSave(String destinationFileToSave) {
SendMessageController.destinationFileToSave = destinationFileToSave;
}
I created a Spring website.
I used an abstract generic controller class, with different implementations.
It works well if I don't activate the Aspect class on any Controllers.
If I activate an Aspect, then all Mappings seem to be deactivated:
DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Did not find handler method for [/contact/2]
WARN : org.springframework.web.servlet.PageNotFound - No mapping found forHTTP request with URI [/clubhelperbackend/contact/2] in DispatcherServlet with name 'appServlet'
This is my abstract controller:
public abstract class AbstractController<T extends Data> implements ClubController<T> {
protected Dao<T> dao;
private Class<T> elementClass;
public AbstractController(Dao<T> dao, Class<T> element) {
super();
this.dao = dao;
this.elementClass = element;
}
#Override
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getAsView(#PathVariable("id") long id, #RequestParam(required = false) boolean ajax, Model m) {
String mapping = elementClass.getSimpleName();
m.addAttribute(mapping, getById(id));
return mapping + "Get" + (ajax ? "Ajax" : "");
}
#Override
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "application/json")
public T delete(#PathVariable("id") long id) {
T obj = getById(id);
// dao.delete(id);
return obj;
}
}
And an implementation:
#Controller
#RequestMapping("/contact")
public class ContactController extends AbstractController<Contact> {
#Autowired
public ContactController(Dao<Contact> contactDao) {
super(contactDao, Contact.class);
}
}
This is my Aspect:
#Aspect
#Component
public class DeletedStorageAspect {
//
// private DeletedEntriesDao deletedEntriesDao;
//
// #Autowired
// public DeletedStorageAspect(DeletedEntriesDao deletedEntriesDao) {
// super();
// this.deletedEntriesDao = deletedEntriesDao;
// }
#Pointcut("execution (public * de.kreth.clubhelperbackend.controller.abstr.AbstractController.delete(..))")
private void invocation() {
}
#AfterReturning(pointcut = "invocation()", returning = "deleted")
public void storeDeleted(JoinPoint joinPoint, Data deleted) {
System.out.println("Deleted: " + deleted);
String tableName = deleted.getClass().getSimpleName();
long id = deleted.getId();
Date now = new Date();
DeletedEntries entry = new DeletedEntries(-1L, tableName, id, now, now);
System.out.println(entry);
// deletedEntriesDao.insert(entry);
}
}
This is part of my beans.xml:
<aop:aspectj-autoproxy>
<aop:include name="mysqlDbCheckAspect" />
<aop:include name="daoLoggerAspect" />
<aop:include name="controllerSecurityAspect" />
<aop:include name="deletedStorageAspect" />
</aop:aspectj-autoproxy>
I can restore full functionality by commenting deletedStorageAspect.
What causes this behaviour? Why are the mappings not recognized with an aspect on them? Are aspects not allowed on Controllers?
Hoping for some help, please.
When using AOP with Spring by default spring creates proxies. Depending on the fact if you implement interfaces on your class (or not) it will create a JDK Dynamic proxy (interface based) or CGLIB based proxy (class based).
public abstract class AbstractController<T extends Data> implements ClubController<T> {
In the case of an interface based proxy (which applies to you) the MVC infrastructure isn't able to see the #RequestMapping annotations anymore and will not detect your mappings anymore. This is also the case that applies to you as you are implementing an interface. Also see the reference guide on the matter of proxying with request mappings.
To fix it you must force the use of class based proxies, to do so add proxy-target-class="true" to the <aop:aspectj-auto-proxy />.
<aop:aspectj-autoproxy proxy-target-class="true">
I am not a AOP expert.But by looking at your code I can say Abstract class in not executing and that may be the root cause. So have to modify Pointcut execution expression.
Solution 1
If you are not using delete signature for child classes you can easlily move to bello like abstract expression. It say's only within package like thing.
#Pointcut("execution (public * de.kreth.clubhelperbackend.controller.*.*.delete(..))")
private void invocation() {
}
Solution 2
You can use logic gates for expression like this
#Pointcut("target(de.kreth.clubhelperbackend.controller.abstr.AbstractController)")
private void anyAbstractOperation() {}
#Pointcut("execution(public * *.delete(..))")
private void anyDeleteOperation() {}
#Pointcut("anyAbstractOperation() && anyDeleteOperation()")
private void invocation() {}
reference :
1.http://docs.spring.io/spring/docs/2.5.x/reference/aop.html#aop-pointcuts-combining
2.http://www.baeldung.com/spring-aop-pointcut-tutorial
3.http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-using-aspectj
Explanation
About this and target
this limits matching to join points where the bean reference is an instance of the given type, while target limits matching to join points where the target object is an instance of the given type. The former works when Spring AOP creates a CGLIB-based proxy, and the latter is used when a JDK-based proxy is created. Suppose that the target class implements an interface:
public class FooDao implements BarDao {
...
}
In this case, Spring AOP will use the JDK-based proxy and you should use the target PCD because the proxied object will be an instance of Proxy class and implement the BarDao interface:
#Pointcut("target(org.baeldung.dao.BarDao)")
On the other hand if FooDao doesn’t implement any interface or proxyTargetClass property is set to true then the proxied object will be a subclass of FooDao and the this PCD could be used:
#Pointcut("this(org.baeldung.dao.FooDao)")
Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}
I have the following code :
public interface CreatorFactory<E extends Vehicle> {
public VehicleType<E> getVehicle();
public boolean supports(String game);
}
public abstract AbstractVehicleFactory<E extends Vehicle> implements CreatorFactory {
public VehicleType<E> getVehicle() {
// do some generic init
getVehicle();
}
public abstract getVehicle();
public abstract boolean supports(String game);
}
and i have multiple factories, for car, truck..etc..
#Component
public CarFactory extends AbstractVehicleFactory<Car> {
/// implemented methods
}
#Component
public TruckFactory extends AbstractVehicleFactory<Truck> {
/// implemented methods
}
What I would like to do is pull the implemented factories into a seperate class as a list, but im not sure how generics works in this case... I know in spring you can get all beans of a specific type... would this still work?...
With erasure, i guess the generic types would be removed .. ??
Firstly, I think there is maybe no need to get a list of beans. And you just want get the exact bean which has declared with generics type.
In BeanFactory interface in Spring framework, there is a method to use for your requirement:
public interface BeanFactory {
/**
* Return the bean instance that uniquely matches the given object type, if any.
* #param requiredType type the bean must match; can be an interface or superclass.
* {#code null} is disallowed.
* <p>This method goes into {#link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
* use {#link ListableBeanFactory} and/or {#link BeanFactoryUtils}.
* #return an instance of the single bean matching the required type
* #throws NoSuchBeanDefinitionException if there is not exactly one matching bean found
* #since 3.0
* #see ListableBeanFactory
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
}
You can use code like:
Car carFactory = applicationContext.getBean( CarFactory.class );
Trunk trunkFactory = applicationContext.getBean( TrunkFactory.class );
or just see #Qualifier annotation for injection automaticly.
#Component("carFactory")
public CarFactory extends AbstractVehicleFactory<Car> {
/// implemented methods
}
#Component("truckFactory ")
public TruckFactory extends AbstractVehicleFactory<Truck> {
/// implemented methods
}
In client side code:
#Qualifier("carFactory")
#Autowired
private CarFactory carFactory ;
#Qualifier("truckFactory")
#Autowired
private TruckFactory TruckFactory;
Looks like you need:
#Autowired
List<AbstractVehicleFactory> abstractVehicleFactories;