2013년 9월 25일 수요일

흥미로운 구조...


흠 흥미로운 클래스구조를 발견함

//

package com.jetddo.study.test;

public interface Page {
 
 public void message();

}

//////////////////////////////////

package com.jetddo.study.test;

public class BasePage implements Page {
 
 protected static Page m_instance;
 
 public static Page getInstance(){
  
  if(m_instance == null){
   m_instance = new BasePage(); 
  }
  
  return m_instance;
 }
 
 public void message(){
  
  System.out.println("Base Page");
 }
}

////////////////////////////////////////

package com.jetddo.study.test;

public class ExecPage extends BasePage {
 
 public static Page getInstance(){
  
  if(m_instance == null){
   m_instance = new ExecPage(); 
  }
  
  return m_instance;
 }
 
 public void message(){
  
  System.out.println("Exec Page");
 }
}

//////////////////////////////////////
 
싱글톤 기법인거 같은데..

protected 로 선언하면서 

확장도 가능하게 변형한거 같다.

//

package com.jetddo.study.test;

import org.junit.Test;

public class TestMain {
 
 @Test
 public void test(){
  
  
  
  ExecPage.getInstance();
  
  BasePage.getInstance().message();
  
 }

}

//

m_Instance 가 static 인것을 이용해 클래스간의 변수를 공유하게 하는 방법이다.
클래스를 재정의 하고 싶다면 구현후 ExecPage.getInstance(); 이것만 추가하면 객체가 바뀐다.

javascript


javascript 에서 java 의 package 나 c 의 namespace 처럼
객체를 체계적으로 관리 할 수 있다.
//

if(typeof window.jetddo =='undefined') window.jetddo = {};
if (!jetddo.test) jetddo.test = {};

jetddo.test.Class = new (function(){
			
	this.initClass = function(htOptions){
		alert(JSON.stringify(arguments[0]));
	};
			
})();
		
jetddo.test.Class = {
				
		initClass : function(htOptions){

					
				alert(JSON.stringify(arguments[0]));
					
		}
				
};

//
위에 javascript 객체를 사용하는데 있어서 두가지 방법이 있다.

위에 있는 것은 Class 라는 객체를 정의하는데 있어서 function 으로 위임하는 것이다.

즉 일종의 생성자가 되는 것이다.

아래에 있는 것은 객체를 json 형태로 규정해서 각 변수를 정의 한다고 볼 수 있다.

2013년 9월 17일 화요일

mybatis 적용


pom.xml 에 다음을 추가한다.




 org.mybatis
 mybatis
 3.1.1


 org.mybatis
 mybatis-spring
 1.1.1




기존 data-context.xml 에 설정되어있던 ibatis 설정을 다음과 같이 바꾼다.




 
            
 
     
     
  
 
  
     
  

 
 
  
  

 




mybatis-config.xml 을 추가한다. 경로는 알아서 잘 하면 댐





  


  
  
  
  
  
  
 
 
 
  
  
  
  
 

  



기존에 있던 sql xml 파일 형식을 다음과 같이 바꾼다.






 
 
  




설정파일 변경은 끝났다 이제 dao 클래스를 보자

////////////////////////////////

package com.jetddo.study.auth.dao;

import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.stereotype.Repository;

import com.jetddo.study.auth.model.AuthUserVo;

@Repository
public class AuthUserDao  extends SqlSessionDaoSupport {
 
 public AuthUserVo selectOnebyUserId(String usr_id){
  
  return getSqlSession().selectOne("auth_usr.selectOnebyUserId", usr_id);
  
 }
}

//////////////////////////
변경 끝~!

2013년 9월 13일 금요일

transaction 적용

/////////////////////////////



package com.jetddo.study.config;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.jetddo.study.util.Base64Decoder;

@Configuration
public class DataSourceConfig {
 
 @Bean(destroyMethod="close")
 public DataSource dataSource() throws IOException {
  
  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(Base64Decoder.decode("b3JhY2xlLmpkYmMuZHJpdmVyLk9yYWNsZURyaXZlcg=="));
  dataSource.setUrl(Base64Decoder.decode("amRiYzpvcmFjbGU6dGhpbjpAMTI3LjAuMC4xOjE1MjE6b3JjbA=="));
  dataSource.setUsername(Base64Decoder.decode("amV0ZGRv"));
  dataSource.setPassword(Base64Decoder.decode("Y2RtYTIwMDQ="));
  dataSource.setDefaultAutoCommit(false);
  
  return dataSource;
  
 }
}


/////////////////////////

위의 소스를 봅시다. 아랫줄에 AutoCommit 을 false로 설정하고 있다.
true 로 설정하면 dao 에서 바로 commit 이 일어나게 된다. 문제가 발생한 경우 되돌릴수 없다.
그래서 Transaction 을 적용한다. 매우 간단하다.

ibatis 설정 xml 에 다음을 추가하자.





 
            
 
    
 
       
 
 
 
  
  

 





dao class 에 다음과 같이 설정하자

/////////////////////////////////////

package com.jetddo.study.manage.dao;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.jetddo.study.manage.model.ManageUserVo;

@Repository
@SuppressWarnings("unchecked")

public class ManageUserDao extends SqlMapClientDaoSupport {
 
 @Resource(name="sqlMapClient")
 public void setSuperSqlMapClient(SqlMapClient sqlMapClient) {
  super.setSqlMapClient(sqlMapClient);
 }
 
 @Transactional
 public List selectAll(){
  
  return (List)getSqlMapClientTemplate().queryForList("mng_usr.selectAll");
  
 }
 
 @Transactional
 public ManageUserVo selectOnebyUserUid(long usr_uid){
  
  return (ManageUserVo)getSqlMapClientTemplate().queryForObject("mng_usr.selectOnebyUserUid",usr_uid);
  
 } 
 
 @Transactional
 public void insertOne(ManageUserVo mngUsrVo){
  
  getSqlMapClientTemplate().insert("mng_usr.insertOne",mngUsrVo);

 }
 
 @Transactional
 public void deleteOnebyUserUid(long usr_uid){
  
  getSqlMapClientTemplate().delete("mng_usr.deleteOnebyUserUid",usr_uid);

 }
}

////////////////////////////////////


참고로 autocommit 을 false로 설정하면 transaction 을 통해서만 commit 이 된다.

tiles 3.0

심심해서 tiles 3.0 을 적용시켜 보았다.
설정들을 봅시다





  org.apache.tiles
  tiles-core
   3.0.0
  

   org.apache.tiles
  tiles-servlet
   3.0.0


   org.apache.tiles
   tiles-jsp
    3.0.0







    
   

 

    
        /WEB-INF/tiles.xml
    

 
 









    
        
        
        
    
    
    
     
    
    
    
     
    
    
    
     
    




설명함

알아서 파일이름 매칭시켜서 복붙하면 되고,
tiles.xml 패턴은 아주 쉽다.

controller 에서 
return "common/tiles/test.jsp" 같이 리턴했다면
common={1}, tiles={2}, test={3} 이 된다.
다른거 건드릴필요 없다.

template.jsp 페이지도 만들어주자

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t" %>
<%@ page session="false" pageEncoding="UTF-8" %>



 
 

 Home
 
  
  

component-scan 2

component-scan 보충




 
 



spring-security 적용시 root-context.xml 에서 로드가 되어야 하기 때문에 해당되는 Service 나
Dao 객체등을 미리 읽어야 한다. 그래서 위와 같이 필요한 패키지나 클래스만을 읽어 사용 할 수 있다.

물론 spring 은 모든 객체를 singleton registry 형태로 관리하기 때문에 객체가 중복될 염려는 없겠다.

2013년 9월 11일 수요일

datasource 관련

spring 에서 sql connection 을 위한 db 접속정보를 설정할때 보통 datasource 를 통해서 기본정보들을 넣어주게 된다


            
 
    
 
       
 
 


위에는 datasource 생성은 빠져있다. 만약 xml형태로 설정을 한다면 보안상 문제가 될 소지가 있다.
그래서 source 상에서 암호화를 통해 설정하는 방식도 존재한다.

하지만 db connection 을 위해서는 datasource 를 생성해야 하고 xml 상에서 생성 할 수 없다면
source 내에서 new 로 생성을 할 수 밖에 없다. 하지만 spring framework 특성상 DI 법칙에 따라 
생성을 주도 할 수 있는 entrypoint 가 존재하지 않는다.
따라서 다음과 같은 방법을 사용한다.

package com.jetddo.study.config;

import java.io.IOException;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.jetddo.study.util.Base64Decoder;

@Configuration
public class DataSourceConfig {
 
 @Bean(destroyMethod="close")
 public DataSource dataSource() throws IOException {
  
  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(Base64Decoder.decode("b3JhY2xlLmpkYmMuZHJpdmVyLk9yYWNsZURyaXZlcg=="));
  dataSource.setUrl(Base64Decoder.decode("amRiYzpvcmFjbGU6dGhpbjpAMTI3LjAuMC4xOjE1MjE6b3JjbA=="));
  dataSource.setUsername(Base64Decoder.decode("amV0ZGRv"));
  dataSource.setPassword(Base64Decoder.decode("Y2RtYTIwMDQ="));
  dataSource.setDefaultAutoCommit(false);
  
  return dataSource;
  
 }
}

@Configuration, @Bean 의 어노테이션이 보일거다.
@Configuration 이 것은 이 java class 는 spring의 설정 역할이라는 것을 알려준다.
그리고 @Bean 은 이 메소드에서 리턴 되는 객체는 spring의 Bean객체라는 것을 알려준다.
즉 위 클래스는 소스상에서 자동으로 bean객체를 생성한다.

또한 @Configuration 은 @Component 어노테이션을 meta-annotation 으로 가지고 있기 때문에
component-scan 을 통해 자동으로 bean 으로 등록이 된다.

AOP Logging

Interceptor logging 은 url 요청에 한해서만 로깅이 가능한 단점이 있다. 모든 class 에 대해 logging 관심사를 추가하는 aop 사용이 더 효율적이다.
 

 

설정 파일을 만들어 위의 내용을 작성한다. 필요한 lib 는 spring 관련 aop 와 aspectj, cglib2 이다. 자세한 내용은 검색 ㄱㄱ
package com.jetddo.study.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class LoggingAdvice {
 
 @Before("execution(* com.jetddo.study..*.*(..))") 
 public void loggingAdvice(JoinPoint joinPoint){
  
  String pointStr = joinPoint.toShortString();
  
  if(!pointStr.contains("SessionInterceptor")){
   System.out.println(" Method : "+pointStr);
   System.out.println("[ Arguments ]");
   Object[] objArray = joinPoint.getArgs();
   for(int i=0 ; i < objArray.length ; i++){
    System.out.println(i+" : "+objArray[i]);
   }
   System.out.println("--------------------------------------------------------------------");
  }
  
  
 }
}


위 class 를 추가한다.
execution(* com.jetddo.study..*.*(..))
에 대해서는 나중에 정리

2013년 9월 9일 월요일

component-scan

  
 
@Repository, @Service, @Controller, @Component
위와 같은 annotation 을 spring stereotype 이라고 한다.

compenent-scan 을 통해 자동으로 bean 객체로 등록이 된다.

Session 관리 2

다른 버전도 올리자 이전 소스에서는 restful 형식의 url에 촛점을 맞추느라 일반화가 안되어있다. 그래서 그냥 초기화면으로 돌아가게 하는 식으로 만들어봤다.
package com.jetddo.study.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class SessionInterceptor implements HandlerInterceptor {
 
 private static final Logger logger = LoggerFactory.getLogger(SessionInterceptor.class);

 
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
  
  HttpSession session = request.getSession(false);
  
  if(session != null && session.getAttribute("SPRING_SECURITY_CONTEXT") != null
          && session.getAttribute("sessionVo") == null){   
      
   session.invalidate();
        
   logger.info("[sessionVo] has EXPIRED !!!");
   
   response.sendRedirect(request.getContextPath());
      
   return false;   
  }
    
  return true;
 }
 
   
 public void postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView) throws Exception {

 }

 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     Object handler, Exception ex) throws Exception {

 }
}

2013년 9월 5일 목요일

Session 관리

오랫동안 자리를 비우거나 할때 session 이 유실되면 로그인 정보와 동기화가 맞지 않아 오류를 낼 때가 있다. 모든 페이지에서 세션검사를 하는 것은 매우 비효율적이다. Interceptor 를 통해서 손쉽게 관리가 가능하다.
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
  
  HttpSession session = request.getSession(false);
  
  if(session == null || session.getAttribute("SPRING_SECURITY_CONTEXT") == null){
   
   logger.info("------------NOT LOGINED--------------");
   // 첫 로그인
  } else if(session != null && session.getAttribute("sessionVo") != null
          && session.getAttribute("SPRING_SECURITY_CONTEXT") != null){
   
   logger.info("--------------LOGINED----------------");
   
  } else if(session != null && session.getAttribute("SPRING_SECURITY_CONTEXT") != null
          && session.getAttribute("sessionVo") == null){   
      
   logger.info("----------SESSIONVO EXPIRED----------");
   
   session.invalidate();
        
   response.sendRedirect(request.getRequestURI());
      
   return false;   
  }
    
  return true;
 }

코드 수정함 원래 목적은 session 이 없을때는 spring-security 로 넘기는 식으로 하려 했으나 interceptor의 개입시점이 spring-security 보다 늦는 문제가 있다 그래서 sessionVo 가 소멸됬을때 session 전체를 소멸하고 다시 request url 로 이동하는 식으로 했다 이는 POST 방식일때 같은 url 로 get방식으로 이동하므로 restful 스타일에 알맞다

Interceptor

Controller 가 엄청 많은데 log 형식을 바꾼다면 모든 클래스에 손을 대야한다. 그러나 Interceptor 를 활용하면 손쉽게 관리할 수 있다.
package com.jetddo.study.interceptor;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


@Component
public class LoggingInterceptor implements HandlerInterceptor {

   private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);

   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler) throws Exception {
    
    logger.info("-------------------------------");
       logger.info("url: "+request.getRequestURI());
       
       String param = "";
       
       Enumeration< ? > e = (Enumeration< ? >)request.getParameterNames();
       while(e.hasMoreElements()){
        
        String name = (String)e.nextElement();
        
        param += "["+name+": "+request.getParameter(name)+"]";
       }
       
       if(param.trim().length() > 0){
        logger.info("params: "+param);
       }
       
       return true;
   }
   
   public void postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView) throws Exception {

   }

   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     Object handler, Exception ex) throws Exception {

   }
}

위와 같이 작성 후
 
  
   
   
  
  
   
   
  
 
servlet-context.xml 에 위와 같이 작성한다.

@Autowired, @Resource

1. @Autowired Type 을 통해 자동주입 ( 인터페이스를 통해 2개의 구현체가 있을 경우 사용 불가능) 2. @Resource name 을 통해 자동 주입 ex)
@Resource(name="userDaoImpl") 
UserDao userDao;

//UserDaoImpl 이라는 Class 를 주입함
//spring 설정 파일에서 context-scan 를 사용하거나 bean 객체로 등록되어야 함