본문 바로가기
Developer/Spring & eGovFrame

[Spring] 서버재기동 없이 XML반영

by 순수한소년 2021. 9. 8.
728x90
반응형

/project이름/src/main/java/test/cmm/util/RefreshableSqlSessionFactoryBean.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package test.cmm.util;
 
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.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
/* mybatis mapper 자동 감지 후 자동으로 서버 재시작이 필요 없이 반영  */
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
    
    private static final Log log = LogFactory.getLog(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;
    }
 
    /** * @throws Exception */
    public void refresh() throws Exception {
        if (log.isInfoEnabled()) {
            log.info("refreshing sqlMapClient.");
        }
        w.lock();
        try {
            super.afterPropertiesSet();
        } finally {
            w.unlock();
        }
    }
 
    /** * 싱글톤 멤버로 SqlMapClient 원본 대신 프록시로 설정하도록 오버라이드. */
    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) {
                        log.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());
                            retVal = true;
                        }
                    } else {
                        map.put(resource, new Long(modified));
                    }
                } catch (IOException e) {
                    log.error("caught exception", e);
                }
                if (retVal) {
                    if (log.isInfoEnabled()) {
                        log.info("modified files : " + 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();
    }
}
cs

@

 

#

/test/src/main/resources/egovframework/spring/com/context-mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
    <!-- 실행환경에서 빈이름 참조(EgovAbstractDAO) -->
    <bean id="egov.lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" />
    
    <!-- Mybatis setup for Mybatis Database Layer -->
<!--20210908 주석 S -->
<!-- <bean id="egov.sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"> -->
<!--20210908 주석 E -->
 
<!--20210908 추가 S -->
<!-- <property name="interval" value="1000" /> -->
<!--20210908 추가 E -->
 
    <bean id="egov.sqlSession" class="test.cmm.util.RefreshableSqlSessionFactoryBean">
        <property name="dataSource" ref="egov.dataSource" />
        <property name="configLocation" value="classpath:/egovframework/mapper/config/mapper-config.xml" />
        <property name="interval" value="1000" />
 
        <property name="mapperLocations">
            <list>
                <value>classpath:/egovframework/mapper/test/**/*_SQL.xml</value>
            </list>
        </property>
    </bean>
    
    <!-- Mybatis Session Template -->
    <bean id="egov.sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="egov.sqlSession"/>
    </bean>
            
</beans>
cs

#

 

728x90
반응형