How to create a REST service with spring-boot? - java

I'm using spring-boot and want to integrate a simple REST service as follows.
#Controller
#RequestMapping("/content")
public class MyServiceRest extends SpringBeanAutowiringSupport {
#RequestMapping(method = RequestMethod.GET)
public String test() {
return "OK";
}
}
Result: both localhost:8080/<app-name>/services/content results "No service was found.". Why?
Do I have to explicit publish the service somehow?
Maybe it is due to my dispatcher servlet?
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(new CXFServlet(), "/services/*");
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
}

Since you are using Spring Boot, make sure that your application is correctly setup by adding the correct annotations. For instance,
#EnableAutoConfiguration
#EnableWebMvc
#Configuration
#ComponentScan
/*
* Application Setups using Spring boot.
*/
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#EnableWebMvc is the annotation to add for using Spring MVC with Spring boot.
And then you can define your controller as you did in your question.

add package with controller class to #Component scan in main class like: #ComponentScan( basePackages = { "your.package.with.controller" } ), this happens when spring didn't initialize (doesn't know about) controller

you should also add url mapping for your method
#RequestMapping(method = RequestMethod.GET, value = "url_here",
try
#RequestMapping(method = RequestMethod.POST, value = "/",

In the latest version of Spring Boot, that I am currently using, the web Service would address be http://localhost:8080/content
Also, the class I use to launch the service looks as follows:
#ComponentScan("eu.buzea")
#EnableAutoConfiguration
#EnableTransactionManagement
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Source Code
https://drive.google.com/open?id=0BzBKpZ4nzNzUWmJmOTFwbTFjWWM
using Swagger
http://localhost:7070/swagger-ui.html#/
**Cheers*

As of current spring-boot.1.5.6 there is no requirement using cxf.
Just use a #RestController with #GetMapping, and be happy to access localhost:8080/content.

Related

SpringBoot - Register Bean as Rest Controller

I'm using Spring Boot AutoConfiguration for registering the beans. Need to find a way through which I can register the beans configured via Auto-Configuration as a Rest Controller
SampleController.java
public class SampleController
{
#GetMapping("/sample-path")
public String sampleMethod()
{
return "Sample String";
}
}
SampleControllerAutoConfiguration.java
#Configuration
#ConditionalOnProperty(value = "some.property", havingValue = "true") // using this property, the parent app may or may not chose to have the Controller Endpoint
public class SampleControllerAutoConfiguration
{
// Need to register this bean as a controller
#Bean
#ConditionalOnMissingBean
public SampleController sampleController()
{
return new SampleController();
}
}
I can't annotate SampleController with #RestController since it's in the same package as the parent project which imports this and hence get's auto-configured due to Component-Scan

Spring AOP adviced methods do not work. My configuration seems ok

I'm trying to create a demo AOP application but it just does not work right.
I read through all tutorials and got it working with #RestController but as I tried it with a plain java spring driven application I just can't get it to work. Please review my files and tell me where my mistake lies in.
Application Class
#SpringBootApplication
#ComponentScan("com.xetra.experimental")
#EnableAspectJAutoProxy
public class AoptryoutnowebApplication {
public static void main(String[] args) {
SpringApplication.run(AoptryoutnowebApplication.class, args);
DefaultClassToAspectImpl defaultClassToAspectImpl = new DefaultClassToAspectImpl();
defaultClassToAspectImpl.doStuff();
}
}
ClassToAspect Interface
public interface ClassToAspect {
void doStuff();
}
ClassToAspect Implementation
#Component
public class DefaultClassToAspectImpl implements ClassToAspect {
#FooAnnotation
public void doStuff(){
System.out.println("DoStuff!");
}
}
Annotation for Pointcut
public #interface FooAnnotation {
}
Aspect Class
#Aspect
public class FooAspect {
#Pointcut("#annotation(FooAnnotation)")
public void methods(){
}
#Before("methods()")
public void doAspect(){
System.out.println("FooAspect before");
}
}
Try this:
replace #EnableAspectJAutoProxy with #EnableAspectJAutoProxy(proxyTargetClass = false)
change pointcut to
#Pointcut("execution (* your.package..*.*(..)) && #annotation(fooAnnotation))")
The problem is you are using a non Spring managed instance by doing new DefaultClassToAspectImpl(). Spring AOP only works for Spring managed beans, because by default Spring AOP is proxy based.
So instead of doing new DefaultClassToAspectImpl() you should be obtaining the instance from the ApplicationContext. When using Spring Boot the SpringApplication.run call returns an ApplicationContext. Here you can use one of the getBean methods to obtain the instance you want.
ApplicationContext ctx = SpringApplication.run(AoptryoutnowebApplication.class, args);
ClassToAspect bean = getBean(ClassToAspect.class);
bean.doStuff();
This way you get the Spring managed

Spring Boot: Is it safe to pass many Instances for CommandLineRunner

I'm a bit new to Spring Boot. I have an Application.java class where I have some code:
#SpringBootApplication(exclude = JpaRepositoriesAutoConfiguration.class)
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
...
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner App(DBReportRepository dbReportRepository,
ClientRepository clientRepository, FileParser fileParser,
MessagingService messagingService, ClientReportFactoryImpl clientReportFactory) ...
I was wondering is it a good practice to pass so many parameters (which are #Services annotated classes) to CommandLineRunner.
Or am I making Spring Boot do too much and there is another way to make Spring Boot be aware of those #Services classes.
Turned out my spring boot configuration was missing an AppConfig class. Without it I could only use #Autowired with #Component classes or #Service classes which were passed as arguments to CommandLineRunner. I created the AppConfig.java looking like this:
#Configuration
#ComponentScan(basePackages = "your.main.package")
public class AppConfig {
}
And after that I could use the #Autowired everywhere possible.

RequestMapping placeholders don't work in JUnit tests

I have a simple Spring MVC controller whose RequestMapping is a property. It's a jar that include a controller. Downstream apps will take this jar and use the common endpoint, only the precise URL can vary by app)
Everything works fine when I include my jar into another app. The app has a property or yaml file and the property is set. I've verified that the endpoint works fine.
However, being the good developer that I am, I want to make an integration test that verifies that the URL determined by the property is exposed properly. I can get an #Value in the controller injected properly, but a ${} expression in the #RequestMapping will not be substituted from a properties file. I found a couple threads (Spring Boot REST Controller Test with RequestMapping of Properties Value and #RequestMapping with placeholder not working) But either they don't apply or I tried what they said and I couldn't get it to work.
The test that hits the static (iWork) endpoint works, but the one that is pulled from the property (iDontWork) doesn't work.
(This is Spring 4.2.6)
Controller
#RestController
#RequestMapping(value = {"/${appName}", "/iWork"})
public class Controller {
#Value("${appName}")
private String appName;
#RequestMapping(method= RequestMethod.GET)
public String handlerMethod (HttpServletRequest request) throws Exception {
// Proves the placeholder is injected in the class, but
// Not in the RequestMapping
assert appName != null;
assert !appName.equals("${appName}");
return "";
}
}
ControllerTest
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class,
classes = { ControllerTest.Config.class })
public class ControllerTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Configuration
#ComponentScan(basePackages = {"test"})
static class Config {
// because #PropertySource doesnt work in annotation only land
#Bean
PropertyPlaceholderConfigurer propConfig() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("test.properties"));
return ppc;
}
}
#Test
public void testStaticEndpoint() throws Exception {
mvc.perform(get("/iWork")).andExpect(status().isOk());
}
#Test
public void testDynamicEndpoint() throws Exception {
mvc.perform(get("/iDontWork")).andExpect(status().isOk());
}
}
test.properties
appName = iDontWork
You're "simply" missing
#EnableWebMvc
on your #Configuration class. Without it, Spring's Mock MVC stack will register your controller handler methods with DefaultAnnotationHandlerMapping which isn't smart enough to resolve the placeholders in #RequestMapping.
If you do provide it, Mock MVC will instead use RequestMappingHandlerMapping, which is.
You need to add placeholder value while creating mockMvc as shown below.
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(accountController)
.addPlaceholderValue("propertyName", "propertyValue")
.build();
}

Combine 2 Spring boot application

Just following Spring Guides http://spring.io/guides#gs I took gs-rest-service and gs-accessing-data-jpa. Now I want to combine them in one application, and that is where like more understanding of new org.springframework.boot.SpringApplication is needed.
In gs-rest-service config looks emazing, that is almost absent
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
gs-accessing-data-jpa is more like Spring XML JavaConfig based app.
#Configuration
#EnableJpaRepositories
public class CopyOfApplication {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(H2).build();
}
// 2 other beans...
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(CopyOfApplication.class);
CustomerRepository repository = context.getBean(CustomerRepository.class);
//...
}
}
How to combine them?
Does it mean that I need to re-write SpringApplication.run now on more detailed level ?
In the Spring Boot application simply add the dependencies from the JPA sample (the ones which you don't already have.
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M7")
compile("org.springframework:spring-orm:4.0.0.RC1")
compile("org.springframework.data:spring-data-jpa:1.4.1.RELEASE")
compile("org.hibernate:hibernate-entitymanager:4.2.1.Final")
compile("com.h2database:h2:1.3.172")
testCompile("junit:junit:4.11")
}
or instead of this you could also use the spring boot starter project for Spring Data JPA. In that case the dependencies would look like the following.
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M7")
compile("org.springframework.boot:spring-boot-starter-data-jpa:0.5.0.M7")
compile("com.h2database:h2:1.3.172")
testCompile("junit:junit:4.11")
}
This will pull in all needed dependencies.
Next copy the CustomerRepository to the Spring Boot application. That basically should be everything you need. Spring Boot auto-configure now detects Spring Data JPA and JPA and will bootstrap hibernate, spring data for you.
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(Application.class, args);
CustomerRepository repository = context.getBean(CustomerRepository.class);
//...
}
}

Categories

Resources