I am new to Spring framework. Maybe this is more of a Java EE bean design question than related to Spring framework. Anyway, I just shoot it and see how clear I can make myself.
So I have a service. The service takes a connection string as constructor parameter. Then you can use the service to upload files to the location indicated by the connection string.
So you will start with something like:
public class MyService{
public MyService(String connectionStr){ ... }
}
When you need such a service, you call:
MyService service = new MyService("xxx");
...
That's what I used to do. Nothing fancy. Now if I do it in Java under Spring, I somehow want the service to be a bean. I need to do this:
#Component
public class MyService{
#Autowired
public MyService(#Value(...some connection string...) String connectionStr) {...}
}
But I get confused how you can inject dependency in compile time? I never know what connection string I will pass to create the service. When I read Spring tutorials, most of them have parameters coded in XML config file. Can I design a Spring bean like the one above but require the parameters to be passed in runtime?
Thanks.
You can design a method like this:
void upload(String location,XXX other parameters);
I didnt really get your question but will try to answer.
Check here or google to check if you really want to go for spring.
Coming to your query, For your service you would have to define some thing like this in your spring context.
<bean id="myService" class="com.blah.MyService">
<constructor-arg>
<value>http://HOST/test/</value>
</constructor-arg>
</bean>
Your service class will be,
public class MyService{
public MyService(String connectionString) {...}
}
This is how you will call your service in your application
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "context.xml" });
MyService service = (MyService) context
.getBean("myService");
The above can be implemented using annotations also. Check here for more details
Related
I am currently debugging some code to optimize it and I am facing a puzzling situation :
Basically we are deploying a random web service and we are doing some dependency injection(actually I am taking over an already developped and working app, so I am still discovering the app's structure).
My question is pretty specific to the utilization of Spring AND jax-ws at the same time.
We have two classes : an interface and an implementation of this service which will be our web service. We then put two annotations on top of the description of our implementing class :
#Service("myService") //here we let Spring know that this class is a service called "myService"
#WebService(endPointInterface = "com.mydomain.myService") //we expose this service at the given endpoint
public class MyServiceImplementation implements MyServiceInterface(){
//some code
}
public Interface MyServiceInterface {
//some code
}
Here is my point : somewhere, the implementing class declares a property called otherService, this property's type is "MyServiceInterface" so basically it implements the same interface as MyServiceImplementation :
#Autowired
#Qualifier("myClient")
private MyServiceInterface otherService;
so if we put things back in the context :
#Service("myService")
#WebService(endPointInterface = "com.mydomain.myService")
public class MyServiceImplementation implements MyServiceInterface(){
#Autowired
#Qualifier("myClient")
private MyServiceInterface otherService;
//some code
}
If my understanding is good so far : MyService exposes its endpoint at "com.mydomain.myService" and when it is instantiated by the application, Spring automatically looks for the class associated with the qualifier "myClient" and which implements the interface MyServiceInterface to initiate the property otherService with an instance of that same class (which would be the basic principle of dependency injection right ?)
So following this logic, there should be, somewhere in my code, a class declared like this :
#Qualifier("myClient")
public RandomClass implements MyServiceInterface {
}
But there is not, and upon searching for the string "myClient" in the whole project, the only matching results are as follows :
< jaxws:client id="myClient" serviceClass="com.mydomain.myService"
address="some_address" />
which is located within the application context of the webApp
So I figured out, maybe the qualifier is referring to this jaxws client but then, this would be silly since this would mean that the service is actually trying to "call himself", wouldn't it?
If you could enlighten me on this, I would greatly appreciate it and hopefully this will show helpful to most of us too. Thank you !
in spring #qualifier goes along with autowiring in order to let spring know which beans you'd like to autowire.
To define the bean that matches this qualifier, you don't have to use the qualifier annotation, there are several other options:
- define it's id, like they do here
- if it's annotation based, use #Bean(name="myClient")
First, forgive my poor English, I am just working hard on my English:).
I'm trying to find an easy way to set the communication more simple between front-end and back-end, because I use ActiveMQ as the Message Oriented Middleware. So XML string became the request carrier.
For example, front-end send a string request to back-end including package name, class name, method name and parameters list, in this way, back-end is allowed to invoke the correct method by these information, and send invoke result back to front-end. It works, but not perfect. The problem is that when I tried to invoke a method in a service class with #Transational and #Service annotation(which is the common practice to connect to the database),the transaction seemed not being opened, request and response are both received, just left a lot of sleeping connection in mysql database process, as much as the ActiveMQ's consumers every time.
Target method in service class:
#Service
#Transactional
public class UserService {
#Autowired
private IUserDAO udao;
public User getUserByName(String username) {
return udao.findByUsername(username);
}
}
Invoke method(some code has been omitted):
#Component
public class ReflectTool {
public Object invokeMethod(String packageName,String className,String methodName,List paramList) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
Object obj = beanFactory.getBean(packageName+"."+className);
Class cla = obj.getClass();
Method method = findMethod(Class cla,String methodName);
return method.invoke(obj, params);
}
}
I've searched a lot answer, but none of them worked. Like:use a proxy object to invoke but not the target object, cause spring framework has used a proxy class instead the service class with #Transactional annotation to help we manager the transaction, but the code (AopUtils.isAopProxy(obj)) returns true, so it mean the object is exactly a proxy object which I got from the spring context? I'm not very familiar with Dynamic Agent Model.
Thanks for your attention, please tell me if I did something wrong.
Well I was going to this I'd try the following approach:
DON'T use BeanFactory, inject in your ReflectTool the ApplicationContect: #Autowired private ApplicationContext applicationContext; if the beans you want to recover implements some interface or extends a class then maybe you can replace this injection by a Map. Spring will make it work
Try to get the object of the bean you need.
the lines you have regarding the capture of the method and execution should work since it is an bean calling another bean.
Hope this helps. I had a similar situation where I needed to invoke a #Transactional method and I fixed in a similar way I have described.
I'm new to Spring and am using an existing Spring library/api. All beans are configured in java classes, ie. no xml, and the existing configuration comes with one line in the main method:
SpringApplication.run(ServerExchange.class, args);
The library deals with the protocols to interface with a private server, I've been able to create normal instances of classes, ie:
AccountManager aM = new AccountManager()
however the configuration that's meant to be autowired into each class, such as the AccountManager, isn't being done as it's intended to be created as a "bean", so when I go to use the AccountManager, it's dependencies that are #Autowired are null
I've been looking online and trying to find an example of how to go from the first given line in the main method however what I can find online is mainly based around either xml or an "ApplicationContext". I'm not sure how to take the first step and simply create a bean, can anyone please provide an example of the code to create an AccountManager bean?
Thanks in advance.
Edit:
To clarify, I'm interested in the code required in the main method of how to get the instance of it once the beans have been set up in their respective classes.
If you want to configure been at class, you can do it as follow.
#Configuration
public class AppConfig {
#Bean
public AccountManager accountManager() {
return new AccountManager();
}
}
Or If you wanna use xml then
<beans>
<bean id = "accountManager" class = "packageName.AccountManager" />
</beans>
Then you can use it in a class as follows.
#Autowired
AccountManager accountManager ;
To create a bean of AccountManager do following:
#Bean
public AccountManager accountManager() {
return new AccountManager();
}
If you need any field to be autowired in an object you cannot instantiate it with new because when you do so Spring don't know about it.
You can do the following :
class MyClass{
#Autowired
ApplicationContext applicationContext;
public void method(){
AccountManager ac = applicationContext.getBean(AccountManager.class);
}
}
if your AccountManager is known by Spring. With an annotation on the class like #Component
As I'm not a user of Spring-boot in this way I found here : How to get bean using application context in spring boot how to get ApplicationContext in a different way than the one I'm using
Note: I have purposefully removed words from the names of classes and objects, so please excuse the horribly names in my code examples
I've got a test class that sets up my application using some test Spring context files. The application is a wrapper to a web service.
As this is a test, I have mocked the main interface to the web service in question (the ITransporter class). This gives me the ability to set expectations so that I can check that the requests sent to the web service are: in the expected format; have the expected fields complete; etc...
My mock is defined in a file called test-beans-context.xml and is passed into some service beans, as follows:
<bean id="mockTransporter" class="org.easymock.EasyMock" factory-method="createMock" scope="singleton">
<constructor-arg index="0" value="transport.ITransporter" />
</bean>
<bean id="accountService" class="service.AccountService">
<property name="transporter" ref="mockTransporter" />
</bean>
This context file is used in 2 places. (And I fear this is where my problem arises.)
The first being the test class, which is defined as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration( locations={"classpath:test-beans-context.xml"} )
public class AbstractIntegrationTest {
#Autowired
private ITransporter mockTransporter;
//Some tests that perform expectations like the following:
// EasyMock.reset( this.mockTransporter );
// EasyMock.expect( this.mockTransporter.sendRequest( EasyMock.capture(this.requestXmlCapture) ) ).andReturn( responseXml );
}
The second place is in a class that is within the logical trail for getting to sending the request. It loads a separate XML context file /lite-api-context.xml that then imports the test one in my test set up.
public class Factory implements IFactory {
public Factory() {
context = new ClassPathXmlApplicationContext("/lite-api-context.xml");
}
#Override
public IAccountService getAccountService() {
return (IAccountService) context.getBean("accountService");
}
}
And lite-api-context.xml includes this:
<import resource="classpath:/test-beans-context.xml" />
My problem is that in the test class, I'm getting a different instance of the mocked ITransporter to the one that is ultimately being used by my other services. So the expectations I set up are never actually executed as the mocks end up being different instances.
Is there a way to ensure I get the same instance in both places?
Or am I going to have to create my own singleton test implementation of the ITransporter interface? (Basically creating a stub that behaves exactly like my mock does now.)
EDIT: (Answer)
As The Thom said, it appears I need to create my own class to manage the mock.
I wanted to add my solution here too in case anyone stumbled across a similar problem.
Just wrote a quick static class like this:
public class MockTransporter {
private static ITransporter mockTransporter = EasyMock.createMock(ITransporter.class);
public static final ITransporter getInstance() {
return mockTransporter;
}
}
And had to change the XML config to this:
<bean id="mockTransporter" class="MockTransporter" factory-method="getInstance" />
Oh yeah, that's a problem. When you create a new context that's like creating a new object space for Spring. The one created in your XML file is different from the one created in your handmade context. They will always produce different variables.
I've been burned on that one before.
You're only hope if you want the same ones is to manage your own singletons.
The ideal way to solve this problem would be to create the Factory as a Spring bean as well, and inject the AccountService into the Factory bean.
In general context.getBean() should be avoided in production code because it harms the concept of Inversion of Control (for more information see: Why is Spring's ApplicationContext.getBean considered bad?). It's okay to use it in test code though.
I'm trying to learn groovy and integrating it with an existing Java jar. The java code makes use of DI but i cant seem to get it to work from my groovy script.
The Java application contains a data access layer using Mybatis. This layer consists of a number of Interfaces (e.g IUser) and Controllers
e.g.
#Service
public class UserController implements IUser
The Controllers make use of Mybatis Mapper Classes.
The whole thing is pulled together using Spring with default-autowire="byName">
Its set up to use annotations to access the mappers within the controllers.
Mybatis is configured in Spring to scan and inject the mappers
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.directski.data.mapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
So when i run my application in java everything works normally. Including any mappers i have called using
#Autowired
private UserMapper userMapper;
When i try to include this jar as part of a groovy scrip i start to run into some problems. Im using the same applicationContext file for Spring
ApplicationContext ctx = new ClassPathXmlApplicationContext("controller-layer-applicationContext.xml");
When i run as a script i can see from the logs that the components are scanned. Some of my controllers include a #PostConstruct method which gets called, and database queries are sucessfully executed. However when trying to call my controllers from my script i get null pointer errors.
I have tried using #Autowired to create my controllers in groovy but they dont seem to get injected. I have also implemented factory.registerBeanDefinition() as per examples in http://groovy.codehaus.org/Using+Spring+Factories+with+Groovy
however this does seem to get my controller to be created, but the Mybatis Mappers within my controllers are returning null
How can i make sure my controllers are autowired correctly from Groovy?
Ok, So i have something thats working. Taken from answer 5 How to programmatically create bean definition with injected properties?
public class AppContextExtendingBean implements ApplicationContextAware{
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
version1(beanFactory);
}
private void version1(AutowireCapableBeanFactory beanFactory){
IUser userController= (UserController) beanFactory.createBean(UserController,AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
beanFactory.initializeBean(userController, "user");
User testuser = userController.getUser(3);
}
}
ApplicationContext ctx = new ClassPathXmlApplicationContext("controller-layer-applicationContext.xml");
AppContextExtendingBean bean = new AppContextExtendingBean();
bean.setApplicationContext(ctx);