log4j
의존성 추가
log4j
log4j
1.2.17
controller
@WebServlet("/ex01.do")
public class Ex01Controller extends HttpServlet {
Logger log = Logger.getLogger("com.bit.controller.Ex01Controller");
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.debug("출력");
response.getWriter().append("Served at: ").append(request.getContextPath());
}
}
log4j 기본설정
src/main/resources/log4j.properties 파일 생성
# log4j.properties
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
실행 결과
log4j pattern
%p debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다. 포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. rn 또는 n 일것이다.
%c 카테고리를 표시합니다 예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다. 예)클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다. %X{key} 형태.
ex) [%d{MM-dd hh:mm:ss}][%C] - %m%n
로그 레벨 예외 설정
# log4j.properties
# 전체 모드는 ERROR만 뜨도록 설정
log4j.rootLogger=ERROR, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
# 해당 패키지 이하의 클래스는 예외로 debug모드로 찍히도록
log4j.logger.com.bit.controller=debug
콘솔에 출력할 것과 파일에 저장할 것을 분리해서 설정가능
stdout은 콘솔에 출력되는 설정, R은 파일에 저장하는 설정
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=E:/webspace/day52/log/test.log #로그 내용을 저장할 파일
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
Servlet Filter
필터를 사용하여 반복되는 공통의 작업, 중복코드를 없앨 수 있다.
- 인증(사용자 인증) 필터
- 로깅 및 감시(Audit) 필터
- 이미지 변환 및 데이터 압축 필터
- 암호화 필터
- XML 컨텐츠를 변형하는 XSLT 필터
- URL 및 기타 정보들을 캐싱하는 필터
단, filter의 Servlet은 HttpServlet이 아니기 때문에 캐스팅을 한다음 사용해야 하는 메서드도 있다.
----------------- web.xml
<filter>
<filter-name>ex01</filter-name>
<filter-class>com.bit.util.filter.Ex01Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>ex01</filter-name>
<url-pattern>/ex02.do</url-pattern>
</filter-mapping>
----------------- Filter
public class Ex01Filter implements Filter{
Logger log = Logger.getLogger("com.bit.util.filter.Ex01Filter");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.debug("시작 할 때");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.debug("before ex01Filter");
chain.doFilter(request, response);
log.debug("after ex01Filter");
}
@Override
public void destroy() {
log.debug("종료 시");
}
}
----------------- Filter 2
@WebFilter(value = {"/ex01.do"})
public class Ex03Filter implements Filter {
Logger log = Logger.getLogger(this.getClass().getCanonicalName());
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
req.setCharacterEncoding("utf-8");
log.debug(req.getSession().getId());
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Servlet Listener - ServletContextListener
----------------- web.xml
<listener>
<listener-class>com.bit.util.listen.Ex01Listener</listener-class>
</listener>
----------------- Listener
public class Ex01Listener implements ServletContextListener {
Logger log = Logger.getLogger(this.getClass().getCanonicalName());
@Override
public void contextInitialized(ServletContextEvent sce) {
log.debug("listener init");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.debug("listener destroy");
}
}
------------------ Listener 2
//@WebListener
public class Ex02Listener implements ServletRequestListener {
Logger log = Logger.getLogger(this.getClass().getCanonicalName());
@Override
public void requestDestroyed(ServletRequestEvent sre) {
log.debug("listen req destroy");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
log.debug("listen req init");
}
}
Encoding Decoding
인코딩 디코딩 처리는 보통 브라우저가 해주나 해주지않는 브라우저를 사용할 때는 직접해줘야한다.
java -> URLEncoder.encode("한글", "utf-8"); URLdecoder
JS -> encodeURI() - 주소의 표현방식 맞춰 인코딩, encodeURIComponent() - value값으로 사용하도록 모든 문자를 인코딩
protected void doDo(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String param = req.getParameter("id");
log.debug(param);
resp.setContentType("text/html; charset=utf-8");
resp.setCharacterEncoding("utf-8");
try(PrintWriter out = resp.getWriter();){
out.println("Ex02 Controller... param:"+param);
}
}
file download
@WebServlet("/download.do")
public class Ex03Controller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("fname");
if(name!=null) {
String path = "E:/webspace/day52/download/";
File f = new File(path+name);
if(f.exists()) {
resp.setContentType("application/octet-stream"); //무조건 다운로드 받도록
resp.setHeader("Content-Disposition", "attachment; filename=\""+name+"\"");
try(InputStream is = new FileInputStream(f);
OutputStream os = resp.getOutputStream();){
int cnt = -1;
while((cnt=is.read())!=-1) {
os.write(cnt);
}
}
} else {
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().append("<h1>파일 없음</h1>");
}
}
}
}
FrameWork
junit
test 순서제어 (4버젼에선 한개뿐)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Before @BeforeClass
@BeforeClass - 테스트 실행 시 한번만 실행 (static이 반드시 붙어야한다.)
@Before - 테스트마다 실행
assertEquals 을 제대로 쓰기 위해서 DTO의 equals를 재정의 해줘야한다! (자동완성 기능)
package com.bit.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DeptDaoTest {
DeptDao dao;
static DeptDto target;
@BeforeClass
public static void beforeClass() {
target = new DeptDto();
target.setDeptno(9999);
target.setDname("test1");
target.setLoc("test2");
}
@Before
public void before() {
dao = new DeptDao();
}
@Test
public void test1SelectAll() {
assertNotNull(dao.selectAll());
assertTrue(dao.selectAll().size()>0);
}
@Test
public void test2InsertOne() {
assertSame(1, dao.insertOne(target.getDeptno(), target.getDname(), target.getLoc()));
}
@Test
public void test3SelectOne() {
assertEquals(target, dao.selectOne(target.getDeptno()));
}
@Test
public void test4UpdateOne() {
target.setDname("test1");
target.setLoc("test2");
assertSame(1, dao.updateOne(target.getDeptno(), target.getDname(), target.getLoc()));
}
@Test
public void test5DeleteOne() {
assertSame(1, dao.deleteOne(target.getDeptno()));
}
}
command pattern - DispatchSevlet
모든 요청을 커맨드로써 접근하게하는 것
-------------- Interface
public interface Controller {
public String execute(HttpServletRequest res);
}
------------- Controllers
public class AddController implements Controller {
@Override
public String execute(HttpServletRequest res) {
return "emp/add.jsp";
}
}
public class InsertController implements Controller {
@Override
public String execute(HttpServletRequest res) {
return "redirect:list.do";
}
}
-------------DispatchSevlet.java
package com.bit.framework.handler;
public class DispatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDo(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDo(req,resp);
}
protected void doDo(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
uri = uri.substring(req.getContextPath().length());
Controller controller = null;
String classInfo = this.getInitParameter(uri);
try {
Class cls = Class.forName(classInfo);
controller = (Controller)cls.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
String viewName = controller.execute(req);
if(viewName.startsWith("redirect:")) {
resp.sendRedirect(viewName.replace("redirect:", ""));
} else {
req.getRequestDispatcher("/WEB-INF/views/"+viewName).forward(req,resp);
}
}
}
template method pattern - JdbcTemplate
자바의 특성 다형성, 추상화를 이용하여 의존도를 없애고 코드 중복을 제거
----------------- DeptDAO
package com.bit.model;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import com.bit.framework.jdbc.JdbcTemplate;
import com.mysql.cj.jdbc.MysqlDataSource;
public class DeptDao {
String driver = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/lecture";
String user = System.getenv("MYSQL_USER");
String password = System.getenv("MYSQL_PW");
JdbcTemplate template = new JdbcTemplate() {
@Override
public Object mapper(ResultSet rs) throws SQLException {
DeptDto bean = new DeptDto();
bean.setDeptno(rs.getInt("deptno"));
bean.setDname(rs.getNString("dname"));
bean.setLoc(rs.getNString("loc"));
return bean;
}
};
private DataSource getDataSource(){
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setURL(url);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
public List<DeptDto> selectAll(){
String sql = "select * from dept";
template.setDataSource(getDataSource());
return template.queryForAll(sql, new Object[] {});
}
public Object selectOne(int deptno) {
String sql = "select * from dept where deptno=?";
template.setDataSource(getDataSource());
return template.queryForAll(sql, new Object[] {deptno}).get(0);
}
public int insertOne(int deptno, String dname, String loc) {
String sql = "insert into dept values(?,?,?)";
Object[] objs = {deptno, dname, loc};
template.setDataSource(getDataSource());
return template.update(sql, objs);
}
public int updateOne(int deptno, String dname, String loc) {
String sql = "update dept set dname=?, loc=? where deptno=?";
Object[] objs = {dname, loc, deptno};
template.setDataSource(getDataSource());
return template.update(sql, objs);
}
public int deleteOne(int deptno) {
String sql = "delete from dept where deptno=?";
template.setDataSource(getDataSource());
return template.update(sql, new Object[] {deptno});
}
}
----------------- JdbcTemplate.java
package com.bit.framework.jdbc;
// 추상메서드가 있기 떄문에 추상클래스가 되어야한다.
public abstract class JdbcTemplate {
DataSource dataSource;
PreparedStatement pstmt = null;
ResultSet rs = null;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
// insert, update, delete
// 다형성을 이용하기 위해 Object[]을 이용
public int update(String sql, Object[] objs) {
Connection conn = null;
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);
for(int i = 0; i < objs.length; i++) {
pstmt.setObject(i+1, objs[i]);
}
return pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
close(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
return 0;
}
public void close(Connection conn) throws SQLException {
if(rs!=null) rs.close();
if(pstmt!=null) pstmt.close();
if(conn!=null) conn.close();
}
// select
public List queryForAll(String sql, Object[] arr){
List list = new ArrayList();
Connection conn = null;
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(sql);
for(int i = 0; i < arr.length; i++) {
pstmt.setObject(i+1, arr[i]);
}
rs = pstmt.executeQuery();
while(rs.next()) {
list.add(mapper(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
close(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
return list;
}
// 의존성을 없애기 위한 추상화
public abstract Object mapper(ResultSet rs) throws SQLException;
}
'회고록(TIL&WIL)' 카테고리의 다른 글
TIL 2023.02.22 Java Web 11 (JdbcTemplate, MongoDB replica set) (0) | 2023.02.22 |
---|---|
TIL 2023.02.21 Java web 10 (EL표현식, JSTL, framework - DispatcherServlet) (0) | 2023.02.21 |
TIL 2023.02.17 Java Web 8 (H2 DB, Maven-eclipse, Cookie, Session) (0) | 2023.02.17 |
TIL 2023.02.17 Java web 7(miniProject, H2, maven) (0) | 2023.02.17 |
TIL 2023.02.16 Java Web 6 (maven, Junit, CP) (0) | 2023.02.16 |