I have a very simple Spring Boot application with classes detailed below.
My problem is with the application.properties file and how they get auto-configured. I'm trying to get Groovy Templates to update in dev by setting 'spring.groovy.template.cache: false', however this is not working. I added two more properties to see if the application.properties file was being read. The 'logging.level.org.springframework.web: ERROR' still results in INFO level messages printed to the console. However, some.prop is read correctly into the MyBean class on application start.
Is there a configuration declaration I'm missing for these properties?
src/main/resources/application.properties:
spring.groovy.template.cache: false
logging.level.org.springframework.web: ERROR
some.prop: bob
src/main/java/sample/MyBean.java:
#Component
public class MyBean {
#Value("${some.prop}")
private String prop;
public MyBean() {}
#PostConstruct
public void init() {
System.out.println("================== " + prop + "================== ");
}
}
and src/main/java/sample/Application.java:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and src/main/java/sample/com/example/MainController.java
#Controller
public class MainController {
#RequestMapping(value="/login", method = RequestMethod.GET)
public ModelAndView risk(#RequestParam Optional<String> error) {
return new ModelAndView("views/login", "error", error);
}
}
It seems you missing scanned your package "sample". Please make sure that you have scanned it.
#ComponentScan({
"sample" })
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also, your application.properties is right. No problem with it.
It appears the solution was much simpler than I thought:
gradle bootRun
should be used to hot reload templates
gradle run does not work (all compiled classes are just built in build/ )
Related
I have a basic spring data application and I have written a unit test. What appears to happen is that when I run the Spring test my application run method gets called as well. I would like to know why this is and how to stop it please.
I have tried using active profiles but that doesnt fix the problem
#SpringBootApplication
#EntityScan({ "com.demo" })
public class Application implements ApplicationRunner {
#Autowired
private IncrementalLoadRepository repo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
IncrementalLoad incrementalLoad = new IncrementalLoad("fred", Instant.now(), Instant.now(), Instant.now());
repo.save(incrementalLoad);
}
and the unit test........
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { Application.class })
#ActiveProfiles("test")
public class IncrementalLoadServiceTest {
#Autowired
private IncrementalLoadService incrementalLoadService;
#Test
public void checkInitialRecords_incrementalLoad() {
List<IncrementalLoad> incrementalLoads = incrementalLoadService.list();
assertEquals(3, incrementalLoads.size());
}
So I think I found the solution. I created another #SpringBootApplication class in my test folders. Initially that failed but I believe thats because the entity scan annotation pointed to packages where my "production" #SpringBootApplication was. I moved that class up a level and it all seems to work ok now.
I am trying to identify a way to know the name of the Main-Class that started SpringBoot.
e.g.
#SpringBootApplication
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
#RestController
public class SampleController
{
#GetMapping("/hello")
pubic String sayHello()
{
System.out.println("Need start class name: "+System.getProperty("sun.java.command"));
return "hello";
}
}
}
When I run the springboot using java -jar myappname.jar ,the System.getProperty("sun.java.command") returns org.springframework.boot.loader.JarLauncher
Can anyone advise, how can I get the name of actual run class. I have tried specifying the start-class attribute in the manifest.mf. It still gave me org.springframework.boot.loader.JarLauncher as the start-class.
You should be able to #Autowire in the ApplicationContext and then do context.getBeansWithAnnotation(SpringBootApplication.class).values().toArray()[0].getClass().getName(), which will give you the first (and presumably only) bean in the context annotated with #SpringBootApplication
I struggle to use aspect in Spring MVC project.
Method that is a pointcut is running fine, but without advise.
Here is class, that starts whole spring boot and that is root of spring context:
#Lazy
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Configuration
public class MainSpringBootClass{
public static void main(String[] args)
{
SpringApplication.run(MainSpringBootClass.class, args);
}
}
Here is class with method, that is pointcut.
#Component
#Log
#Aspect
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleClass
{
public void example()
{
System.out.println("example");
}
}
And here is my aspect:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyAspect implements MethodBeforeAdvice
{
#Pointcut("execution(* com.example.MyExampleClass.example())")
public void asd()
{
// pointcut
}
#Before("asd()")
public void login()
{
System.out.println("im am logging in");
}
#Before("execution(* com.example.MyExampleClass.example())")
public void login2()
{
System.out.println("im am logging in2");
}
#Override
public void before(Method method, Object[] args, Object target) throws Throwable
{
System.out.println("aop before");
}
}
And here is my controller:
#RestController
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleController
{
private final MyExampleClass myExampleClass;
#Inject
public AdController(MyExampleClass myExampleClass)
{
this.myExampleClass = myExampleClass;
}
#RequestMapping("/")
public String index()
{
myExampleClass.example();
return "x";
}
}
As You can see, I have been trying to bruteforce correct result with annotations.
I have also seen on some website, that I need specific dependencies, so here are mine (pasting only those, related to aspects):
compile 'org.springframework:spring-aop:+'
compile 'org.aspectj:aspectjrt:+'
compile 'org.aspectj:aspectjweaver:+'
compile 'cglib:cglib:+'
compile 'cglib:cglib-nodep:+'
All dependencies have been successfully downloaded, project compiles and runs fine.
When I hit localhost:8080 then I see returned value "x", and inside logs I see "example".
However, I do not see any advices from spring aop nor aspectj - what am I doing wrong?
I am just using this project as sandbox to learn aspects, so I would be eager to learn, how to do it with each of Spring AOP and AspectJ.
The most important thing for me is to do it all without XML.
EDIT:
I have added simple constructor to MyAspect with println to check, if it is created (as it is normal spring bean with #Component after all) and it does - it is correctly created by spring.
EDIT 2:
IntelliJ IDEA tells me about methods login and login2: "This advice advices no method", but at the same time, I am able to jump (with control-click) from string, that is value in annotations to correct implementations.
All you should need is something like this:
#Aspect
#Component
public class MyAspect {
#Before("execution(* com.example.MyExampleClass.example(..))")
public void logBefore(JoinPoint pjp) throws Throwable {
System.out.println("before...");
}
}
You might have to replace all of the aspectJ dependencies with spring-boot-starter-aop .
Here's an example project that works (see RestControllerAspect.java):
https://github.com/khoubyari/spring-boot-rest-example
I am using this Spring AOP code in my Spring Boot starter project in STS. After debugging this for some time I don't see any problem with the AspectJ syntax. The Maven dependencies are generated by STS for a AOP starter project. Is there a glaring omission in this code like an annotation ? The other problem could be with the AOP starter project or with the way I try to test the code in a #PostConstruct method.
I installed AJDT but it appears STS should show AspectJ markers in the IDE on its own. Right ? I don't see the markers. What other AspectJ debugging options are included in STS ? -Xlint is what I used in Eclipse/AJDT.
StateHandler.java
public class StateHandler<EVENTTYPE extends EventType> {
private State<EVENTTYPE> state;
private Event<EVENTTYPE> event;
public StateHandler(State<EVENTTYPE> state, Event<EVENTTYPE> event) {
this.state = state;
this.event = event;
}
public void handle( Event<EVENTTYPE> event ){
state = state.handle( event );
}
public State<EVENTTYPE> getState() {
return state;
}
}
DeviceLogger .java
#Aspect
#Component
public class DeviceLogger {
private static Logger logger = Logger.getLogger("Device");
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public void log() {
logger.info( "Logger" );
}
}
LoggerApplication.java
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
SpringApplication.run(LoggerApplication.class, args);
}
#PostConstruct
public void log(){
DeviceState s = DeviceState.BLOCKED;
StateHandler<DeviceEvent> sh = new StateHandler<DeviceEvent>( s,
Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
}
There are 3 obvious things wrong and 1 not so obvious wrong.
Your aspect is wrong and breaks proper method execution. When using an around aspect you must always return Object and use a ProceedingJoinPoint and call proceed() on that.
You are creating new instances of classes yourself, Spring, by default, uses proxy based AOP and will only proxy beans it knows.
In a #PostConstruct method it might be that proxies aren't created yet and that nothing is being intercepted
You need to use class based proxies for that to be enabled add spring.aop.proxy-target-class=true to your application.properties. By default JDK Dynamic Proxies are used which are interface based.
Fix Aspect
Your current aspect doesn't use a ProceedingJoinPoint and as such never does the actual method call. Next to that if you now would have a method that returns a value it would all of a sudden return null. As you aren't calling proceed on the ProceedingJoinPoint.
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
logger.info( "Logger" );
return pjp.proceed();
}
Create a bean to fix proxying and #PostConstruct
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(LoggerApplication.class, args);
StateHandler<DeviceEvent> sh = context.getBean(StateHandler<DeviceEvent>.class);
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
#Bean
public StateHandler<DeviceEvent> auditMessageStateHandler() {
return new StateHandler<DeviceEvent>(DeviceState.BLOCKED, Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
}
}
Add property to enable class proxies
In your application.properties in src\main\resources add the following property with a value of true
spring.aop.proxy-target-class=true
I am following the spring-data-rest guide Accessing JPA Data with REST. When I http post a new record it is inserted (and the response is a 201). That is great, but is there a way to configure the REST MVC code to return the newly created object? I'd rather not have to send a search request to find the new instance.
You don't have to search for the created entity. As the HTTP spec suggests, POST requests returning a status code of 201 Created are supposed to contain a Location header which contains the URI of the resource just created.
Thus all you need to do is effectively issuing a GET request to that particular URI. Spring Data REST also has two methods on RepositoryRestConfiguration.setReturnBodyOnCreate(…) and ….setReturnBodyOnUpdate(…) which you can use to configure the framework to immediately return the representation of the resource just created.
Example with Spring Boot:
#Configuration
#EnableMongoRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
RepositoryRestConfiguration restConfiguration = ctx.getBean(RepositoryRestConfiguration.class);
restConfiguration.setReturnBodyOnCreate(true);
}
}
or
#Configuration
#EnableMongoRepositories
#EnableAutoConfiguration
public class Application extends RepositoryRestMvcConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
super.configureRepositoryRestConfiguration(config);
config.setReturnBodyOnCreate(true);
}
}
Good Luck!
If you are using Spring Boot, you can add the following lines to your application.properties file for POST (create) and PUT (update) respectively
spring.data.rest.return-body-on-create=true
spring.data.rest.return-body-on-update=true
Here's another variant that uses DI rather than extending RepositoryRestMvcConfiguration or using the ConfigurableApplicationContext.
#SpringBootApplication
#EnableConfigurationProperties
#Configuration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired private RepositoryRestConfiguration repositoryRestConfiguration;
#PostConstruct
public void exposeIds() {
this.repositoryRestConfiguration.setReturnBodyForPutAndPost(true);
}
}