Spring Boot Build 없이 Run. 톰캣 서버 재기동 없이 수정 반영 class resource jsp 등등
업데이트:
Spring Boot 프로젝트 에서 java 파일 mapper 파일을 서버 재기동없이 자동 반영 하는 방법들을 정리해둔 곳입니다.
- Spring application.properties 설정 및 XML 설정 방식에 따른 차이점을 둘다 적었습니다.
Spring Boot application.properties 설정 방식에서 java 파일 Mybatis mapper 파일 수정시 자동으로 재기동 방법
Eclipse 에서 사용하는 경우 아래의 dependency 를 추가 하고
# Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- Restart를 원하지 않을 경우 false -->
</dependency>
# Gradle
compile "org.springframework.boot:spring-boot-devtools"
application.properties 에 아래의 옵션을 추가 하면 resource 파일 수정시 자동반영 된다.
xml mapper 들을 resource 에 두는 방식으로 구성된 프로젝트라면 쿼리 수정내용이
자동으로 반영될 것이다.
# resource 수정시 재기동 옵션
spring.devtools.livereload.enabled=true
# classpath 에 속한 파일 Ex. class 수정시 재기동 옵션
spring.devtools.restart.enabled = true
- 인텔리제이는 위의 설정에 추가 설정이 필요함
Spring Boot XML 설정에서 java 파일 수정시 재기동 방법
1. boot 서비스 pom.xml 파일에 아래 추가
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.8.RELEASE</version>
</dependency>
2. run Configurations 설정
boot 서비스 우클릭 → Run As → Run Configurations → Spring Boot App → Arguments → VM arguments 에디터 창에서 아래 설정 작성
-javaagent:C:\Users\Administrator\.m2\repository\org\springframework\springloaded\1.2.8.RELEASE\springloaded-1.2.8.RELEASE.jar -noverify
단, 경로는 본인 PC 경로 설정 할것
3. Mvn update 실시
4. 설정한 폴더에 jar 파일 확인
C:\Users\Administrator\.m2\repository\org\springframework\springloaded\1.2.8.RELEASE\ 내에 springloaded-1.2.8.RELEASE.jar 확인
5. 실행
Run As → Spring Boot App
Linux 에서 Tomcat 서버 설치후 설정하는 방법
# 1번 내용에서 Maven Repository 로 경로를 잡든
# 그냥 Maven Central 에서 직접 jar 파일을 다운받든 어디든 내려 받은 다음
# 파일이 있는 경로에 CATALINA_OPTS 로 export 걸면, 톰캣 기동시 해당 옵션이 포함되어 서버가 기동 된다.
export CATALINA_OPTS="$CATALINA_OPTS -javaagent:/root/username/tomcat8.5/springloaded-1.2.8.RELEASE.jar -noverify"
# 위의 옵션을 적는 파일은 톰캣 폴더 구조에서 bin 폴더 안의 setenv.sh 파일을 생성해서 적는다
# 톰캣 8 에서는 이 파일이 존재하지 않는다. 생성하면 된다.
# 아래는 catalina.sh 파일의 내용 중 일부 인데, CATALINA_BASE 또는 CATALINA_HOME 으로 잡힌 경로 안에서 bin/setenv.sh 파일이 존재하면 해당 파일을 실행해 준다.
# 기본적으로 startup.sh 든 shutdown.sh 든 내부적으로 catalina.sh 를 호출하게 되어 있다. Ex) catalina.sh start or catalina.sh stop
if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then
. "$CATALINA_BASE/bin/setenv.sh"
elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then
. "$CATALINA_HOME/bin/setenv.sh"
fi
Eclipse Tomcat 서버 설정
* 아래의 스샷들을 참고
* Server Option 관련 영역은 모두 체크 해제
* Publishing 관련 해서는 automatically publish when resourses change 선택
* server.xml 파일 안의 reloadable 속성 false로. 아래 정보 참고
<Context docBase="front" path="/" reloadable="false" source="org.eclipse.jst.jee.server:front"/>
* 마지막으로 아래 옵션을 넣어줘야 하는데.. 서버 만든거 더블클릭하면 뜨는 아래의 스샷 에서 Open launch configuration 클릭후 argument 탭 안의 VM argument 안에 아래의 명령어 입력. -D 옵션을 빼고 있는 그대로 붙여주면 됨. 경로는 알아서 다운받은 경로로 바꿔주기
-javaagent:C:\springloaded-1.2.8.RELEASE.jar -noverify
서버 설정 화면
Open launch configuration 팝업 argument 세팅
Spring XML 설정 방식에서 Mybatis mapper 자동 리로드 auto reload 방법
- 옜날 방식인 XML 방식의 설정은
- 아래의 파일을 작성하여 설정에 물려주면 동작한다.
- 아래 java 소스를 RefreshableSqlSessionFactoryBean.java 로 생성해서 원하는 package 경로에 넣고
- 아래의 xml 파일 중, SqlSessionFactoryBean 을 찾아서 아래의 내용으로 교체
<!-- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"> -->
<bean id="sqlSession" class="yeep.common.aop.RefreshableSqlSessionFactoryBean">
package com.package.name;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(RefreshableSqlSessionFactoryBean.class);
private SqlSessionFactory proxy;
private int interval = 500;
private Timer timer;
private TimerTask task;
private Resource[] mapperLocations;
private boolean running = false;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public void setMapperLocations(Resource[] mapperLocations) {
super.setMapperLocations(mapperLocations);
this.mapperLocations = mapperLocations;
}
public void setInterval(int interval) {
this.interval = interval;
}
public void refresh() throws Exception {
if (logger.isInfoEnabled()) {
logger.info("> Refresh SqlMapper");
logger.info("======================================================================================");
}
w.lock();
try {
super.afterPropertiesSet();
} finally {
w.unlock();
}
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
setRefreshable();
}
private void setRefreshable() {
proxy = (SqlSessionFactory) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSessionFactory.class }, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// log.debug("method.getName() : " + method.getName());
return method.invoke(getParentObject(), args);
}
});
task = new TimerTask() {
private Map<Resource, Long> map = new HashMap<Resource, Long>();
public void run() {
if (isModified()) {
try {
refresh();
} catch (Exception e) {
logger.error("caught exception", e);
}
}
}
private boolean isModified() {
boolean retVal = false;
if (mapperLocations != null) {
for (int i = 0; i < mapperLocations.length; i++) {
Resource mappingLocation = mapperLocations[i];
retVal |= findModifiedResource(mappingLocation);
}
}
return retVal;
}
private boolean findModifiedResource(Resource resource) {
boolean retVal = false;
List<String> modifiedResources = new ArrayList<String>();
try {
long modified = resource.lastModified();
if (map.containsKey(resource)) {
long lastModified = ((Long) map.get(resource)).longValue();
if (lastModified != modified) {
map.put(resource, new Long(modified));
//modifiedResources.add(resource.getDescription()); // 전체경로
modifiedResources.add(resource.getFilename()); // 파일명
retVal = true;
}
} else {
map.put(resource, new Long(modified));
}
} catch (IOException e) {
logger.error("caught exception", e);
}
if (retVal) {
if (logger.isInfoEnabled()) {
logger.info(
"======================================================================================");
logger.info("> Update File name : " + modifiedResources);
}
}
return retVal;
}
};
timer = new Timer(true);
resetInterval();
}
private Object getParentObject() throws Exception {
r.lock();
try {
return super.getObject();
} finally {
r.unlock();
}
}
public SqlSessionFactory getObject() {
return this.proxy;
}
public Class<? extends SqlSessionFactory> getObjectType() {
return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class);
}
public boolean isSingleton() {
return true;
}
public void setCheckInterval(int ms) {
interval = ms;
if (timer != null) {
resetInterval();
}
}
private void resetInterval() {
if (running) {
timer.cancel();
running = false;
}
if (interval > 0) {
timer.schedule(task, 0, interval);
running = true;
}
}
public void destroy() throws Exception {
timer.cancel();
}
}
하나의 톰캣 서버에 여려개의 Application 을 설정해서 원하는 대로 기동하는 방법
* 대부분 톰캣 서버 압축푼 폴더 전체를 복사해서 서버 별로 기동하는게 속이 편하긴 하다
* 하지만 난 궁금해서 한번 한대의 서버에 어떻게 하면 되는지 검색해 봤다
* 폴더 구조를 대충 그리자면
tomcat
- bin (기존 모든 파일 존재)
- controller (start.sh stop.sh 파일이 존재)
- lib
app1
- bin (여기 폴더에는 setenv.sh 파일만 존재)
- conf
- lib
- logs
- temp
- webapps
- work
app2
- bin (여기 폴더에는 setenv.sh 파일만 존재)
- conf
- lib
- logs
- temp
- webapps
- work
* 포인트는 app1, app2 의 CATALINA_HOME, CATALINA_BASE 의 경로를 다르게 잡아주기 위해 파라미터를 받아서
app1, app2 경로로 바꿔주고, 기존 톰캣에 들어있던 startup.sh 파일을 호출
* 각 app1, app2 폴더안의 setenv.sh 파일에 CATALINA_OPTS 를 설정해서 기본적인 추가 옵션 처리
# start.sh stop.sh 파일 내용
#! /usr/bin/env sh
app_instance=$1;
BASE_TOMCAT=/home/yourBaseDirectory
export CATALINA_HOME=$BASE_TOMCAT/tomcat8.5
export CATALINA_BASE=$BASE_TOMCAT/$app_instance
$CATALINA_HOME/bin/startup.sh
# stop.sh 는 startup.sh 를 shutdown.sh 로 변경 하면 나머진 동일
$CATALINA_HOME/bin/shutdown.sh
# setenv.sh 파일 내용
export CATALINA_OPTS="$CATALINA_OPTS -Xms1024m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx4096m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxPermSize=1024m"