1. Spring DI
- Spring Framework의 특징
- POJO(Plain Old Java Object) 방식의 프레임워크
- POJO란?
- 오래된 방식의 간단한 자바 오브젝트
- JavaEE 등의 중량 프레임워크들을 사용하며 해당 프레임워크에 종속된 무거운 객체를 만들게 된 것에 반발해 사용하게 된 용어
- 특정 기술에 종속되어 동작하는 것이 아닌 순수한 자바 객체를 의미함
- 왜 POJO를 지향해야 하는가?
- 가독성 떨어지고 유지보수의 어려움
- 특정 기술의 클래스를 상속받거나, 직접 의존하게 되어 확장성이 매우 떨어지는 단점
- 특정 기술을 사용하고 싶다면? (스프링이 POJO를 유지하면서 Hibernate를 사용할 수 있는 이유) ⇒ PSA
- POJO란?
- 의존성 주입(Dependency Injection)을 통한 객체 관계 구성
- 관점지향 프로그래밍(AOP, Aspect Oriented Programming)
- 제어 역전(IoC, Inversion of Control)
- 높은 확장성과 다양한 라이브러리
- POJO(Plain Old Java Object) 방식의 프레임워크
- ClassA 객체가 어떤 일을 처리하기 위해서 ClassB 객체의 도움을 받아야만 일을 처리할 수 있다면 ‘ClassA는 ClassB에 의존한다’라고 표현
- 위 예제에서는 Programmer가 Desktop Class에 의존성을 가지고 있음.
- Programmer가 컴퓨터를 laptop으로 바꾸고 싶다면 코드 수정이 필요함
- 현재 객체 생성의존성과 타입 의존성 이렇게 두 가지를 가지고 있음
- 이걸 의존 안하고 싶다면? new 를 하지 않고 내가 다른 곳에서 만든 객체를 전달해주기만 하면 됨.
- Container ?
- 스프링에서 핵심적인 역할을 하는 객체를 Bean이라고 함
- Container는 Bean의 인스턴스화 조립, 관리의 역할, 사용 소멸에 대한 처리를 담당
- BeanFactory
- 프레임워크 설정과 기본기능을 제공하는 컨테이너
- 모든 유형의 객체를 관리할 수 있는 메커니즘 제공
- ApplicationContext
- BeanFactory 하위 인터페이스
- 이벤트 처리, 국제화용 메시지 처리, AOP 통합 기능 제공
- WebApplicationContext
- 웹 환경에서 Spring을 사용하기 위한 기능이 추가됨
- 대표적인 구현클래스로 XMLWebApplicationContext가 있음
- 스프링 설정 정보
- 애플리케이션 작성을 위해 생성할 Bean과 설정 정보, 의존성 등의 방법을 나타내는 정보
- 설정정보를 작성하는 방식은 XML방식, Annotation방식, Java방식이 있음
-
- 의존성 주입 생성자
- constructor-arg 를 이용하여 의존성 주입
- <ref>, <value>와 같이 하위 태그를 이용하여 설정 or 속성을 이용하여 설정
- 위와 같이 설정하면, container가 build될 당시에 Programmer를 만들 당시에 desktop을 먼저 만들고 이를 Programmer에 주입시켜놓음
- bean의 순서가 달라도 문제 없음. 의존성 주입에 순서는 중요하지 않음.
- 의존성 주입 생성자
- 의존성 주입(설정자)
- setter를 이용하여 의존성 주입
- <ref>, <value>와 같이 하위 태그를 이용하여 설정 또는 속성을 이용하여 설정
- <property name = “setter의 이름” ref = “ ~ “ > </property>
- 빈(Bean) 생성 및 설정(@Component)
- 빈(Bean) 생성 - @Component : 객체 생성할 대상 클래스에 작성해주는 Annotation
- 생성되는 bean의 이름은 클래스의 첫 글자를 소문자로 바꾼 것
- 스프링은 @Component, @Service, @Controller, @Repository 의 Stereotype Annotation을 제공
- 각 Repository, Service, Controller는 목적에 맞는 구체적인 사용을 위한 @Component의 확장. 목적에 맞게 구체화하여 사용하면 Spring에서 더 효율적으로 사용 가능
- Annotation 방식으로 Bean을 등록하기 위해서는 의존성 설정을 위해 대상 패키지를 지정해야 함
<context:component-scan base-package = "com.ssafy.di"></context:component-scan>
- 의존성 주입(@Autowired)
- 의존성을 주입할 대상에 가서 Autowired annotation 작성
- Spring 컨테이너 내에서 타입 매칭 시행(컨테이너에 해당되는 타입 bean이 있다면 매칭)
- Autowired 사용 가능 위치
- 생성자 : 생성자를 하나만 정의한다면 생략 가능
- Setter : Qualifier를 이용하여 같은 타입이 여러 개일 경우, bean을 지정하여 식별 가능
- field : Qualifier를 이용하여 같은 타입이 여러 개일 경우, bean을 지정하여 식별 가능
- Qualifier : 느슨한 결합을 함으로써 같은 인터페이스 타입이 Bean에 등록되어 있을 때 그들 중 특정 하나만 지정하기 위해 사용.
- Qualifier가 다 살아있을 땐(하나는 field, 하나는 setter) 둘 중 어떤 Qualifier 중 어떤게 살아남을까 ?
- field보다는 Setter가 더 세다 !!
- Qualifier가 다 살아있을 땐(하나는 field, 하나는 setter) 둘 중 어떤 Qualifier 중 어떤게 살아남을까 ?
- AOP(Aspect Oriented Programming)
- OOP(객체지향)에서 모듈화의 핵심 단위는 클래스인 반면, AOP에서 모듈화 단위는 Aspect
- OOP를 더 발전시키기 위한 개념. 하나의 소프트웨어가 거대한 OOP로 설계, 프로그래밍되어 있다면 이를 각 기능별로 모듈화해서 분리시키는 개념
- AOP의 핵심 : 공통 모듈을 분리시켜 해당 소스코드가 외부의 다른 클래스에 존재하는 것
- 핵심관심(각 서비스의 핵심 비즈니스 로직)과 횡단 관심(공통 모듈 ex. Security, Profiling, Logging, Transaction Management)
- Aspect는 여러 타입과 객체에 거쳐 사용되는 기능(Cross Cutting, 트랜잭션 관리 등)의 모듈화
- 횡단 관심사는 비즈니스 기능과는 별개의 영역이지만 필연적으로 대다수의 비즈니스 기능에 분포되어 있으며 이는 가독성을 떨어뜨리고 자칫 중복된 코드 생겨날 가능성. 비즈니스의 모듈성을 감소시키는 가장 큰 요인.
- Spring Framework의 필수 요소는 아니지만 AOP프레임워크는 SpringIoC를 보완
- 장점
- 각 비즈니스 로직마다 복붙을 통해 생겨난 중복 코드 제거 가능
- 각 비즈니스 로직을 구현하는 개발자는 자기 자신의 코드에만 집중할 수 있음
- 코드 간결, 유지보수 쉬워짐
- 재활용성 높아짐
- 용어
- Aspect : 여러 클래스에 공통적으로 구현되는 관심사의 모듈화
- logging하는 기능, transaction하는 기능, 보완적인 기능 등
- Join Point : 메서드의 실행이나 예외처리와 같은 프로그램 실행 중의 지점들(여러 위치). Spring에서는 메서드 실행을 의미함(공통 관심사들이 들어갈 수 있는 위치)
- Pointcut : Join Point에 Aspect를 적용하기 위한 조건 서술. Aspect는 지정한 pointcut에 일치하는 모든 join point에서 실행됨(특정 위치)
- Advice : 특정 조인포인트에서 Aspect에 의해 취해진 행동, Around, Before, After 등의 Advice 타입 존재
- Target 객체 : 하나 이상의 Advice가 적용될 객체, Spring AOP는 Runtime Proxy를 사용하여 구현되므로 객체는 항상 Proxy객체가 됨.
- AOP Proxy : AOP를 구현하기 위해 AOP 프레임워크에 의해 생성된 객체, Spring Framework에서 AOP 프록시는 JDK dynamic proxy 또는 CGLIB proxy이다.
- Weaving : Aspect를 다른 객체와 연결하여 Advice 객체 생성. 런타임 또는 로딩 시 수행할 수 있지만 Spring AOP는 런타임에 위빙을 수행. 특정 JoinPoint에 Advice하여 핵심기능과 횡단기능이 교차하여 새롭개 생성된 객체를 프로세스에 적용하는 일련의 모든 과정을 말함.
- Aspect : 여러 클래스에 공통적으로 구현되는 관심사의 모듈화
- Spring AOP Proxy
- 실제 기능이 구현된 Target객체를 호출하면, target이 호출되는 것이 아니라 advice가 적용된 Proxy객체가 호출됨
- Spring AOP는 기본값으로 표준 JDK dynamic proxy를 사용
- 인터페이스를 구현한 클래스가 아닌 경우 CGLIB프록시를 사용
- SpringAOP
- @AspectJ : 일반 Java클래스를 Aspect로 선언하는 스타일. AspectJ에 의해 소개됨
- Spring AOP에서는 pointcut 구문 분석, 매핑을 위해 AspectJ라이브러리를 사용함
- 하지만 AOP runtime은 순수 Spring AOP이며 AspectJ에 대한 종속성은 없음
Intercepter
- HandlerIntercepter(인터페이스)를 구현한 것. 또는 HandlerIntercepterAdapter(클래스)를 상속한 것
- 요청을 처리하는 과정에서 요청을 가로채서 처리
- 접근제어(Auth), 로그(log) 등 비즈니스 로직과 구분되는 반복적이고 부수적인 로직 처리
- HandlerIntercepter의 주요 메서드
- preHandle()
- Controller(핸들러) 실행 이전에 호출
- false를 반환하면 요청을 종료
- 클라이언트가 서버에 요청을 날릴텐데 인터셉터가 이걸 가로채서 판단을 하고 true를 반환하면 계속 진행, false를 반환하면 Controller까지 가지 못 함.
- postHandle()
- Controller 실행 후 호출
- 정상 실행 후 추가 기능 구현시 사용
- Controller에서 예외 발생 시 해당 메서드 실행되지 않음
- afterCompletion()
- 뷰가 클라이언트에게 응답 전송한 뒤 실행
- Conroller에서 예외 발생 시, 네번째 파라미터로 전달이 됨(기본은 null)
- 따라서 Controller에서 발생한 예외 혹은 실행시간 같은 것들을 기록하는 등 후처리 시 주로 사용
- 코드
- preHandle()
package com.ssafy.mvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
//이거 인터셉터로 할랭
//1. 구현 implements HandlerInterceptor
//2. 상속extends HandlerInterceptorAdapter
//2번 했더니 왜 줄 생겨? 이거 옛날 방식이고 이제 좀 안썼으면 좋겠다는 의미.
//지금은 괜찮아. 써도 동작은 하는데 추후 동작 안할 수 도 있으니!
public class AInterceptor implements HandlerInterceptor {
//아래 메서드를 빈으로 등록해야 쓸 수 있음.
//Intercept는 웹과 관련된 설정인가 혹은 그 외의 설정인가?
//웹과 관련된 설정임 => Servlet.context에 저장 됨.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("A : preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("A : postHnadle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("A : afterCompletion");
}
}
인터셉터 등록(xml에)
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan
base-package="com.ssafy.mvc.controller" />
<beans:bean class = "com.ssafy.mvc.interceptor.LoginInterceptor" id = "confirm"></beans:bean>
<interceptors>
<interceptor>
<mapping path="/*" />
<beans:bean class = "com.ssafy.mvc.interceptor.AInterceptor"> </beans:bean>
</interceptor>
<interceptor>
<mapping path="/*" />
<beans:bean class = "com.ssafy.mvc.interceptor.BInterceptor"> </beans:bean>
</interceptor>
<interceptor>
<mapping path="/*" />
<beans:bean class = "com.ssafy.mvc.interceptor.CInterceptor"> </beans:bean>
</interceptor>
</interceptors>
</beans:beans>
package com.mvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//여기에서 검사 진행
//로그인 정보가 있는지, 로그인 했는지에 대한 정보는 주로 session에 저장
//세션은 이렇게 가져올 것!
HttpSession session = request.getSession();
//로그인할 때 이렇게 id를 세션에 담아둘 거고! 그 id를 가져와봤을 때 null이면
//로그인 상태가 아니라는 의미
if(session.getAttribute("id") == null){
response.sendRedirect("login");
return false;
}
return true; //통과
}
}
- 파일 입출력
코드
package com.ssafy.mvc.controller;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class MainController {
@Autowired
private ServletContext servletContext;
//resource 경로를 가져오기 위해 사용
@Autowired
private ResourceLoader resLoader;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home() {
return "home";
}
@GetMapping("regist")
public String registForm() {
return "regist";
}
@PostMapping("upload")
public String upload(MultipartFile upload_file, Model model) {
//실제 저장될 위치
String uploadPath = servletContext.getRealPath("/upload");
//위의 값이 실제 파일 위치와 다른 이유?
//run on server를 하면 프로젝트를 톰캣에 실어서 실행
//실제 실행되는 서버의 주소인 것.
//파일 업로드를 했을 때는 프로젝트 폴더가 아니라 톰캣에 등록되는 것.
//만약 등록하려고 하는 upload폴더가 없을 수도 있다.
//그러면 그 폴더를 만들자.
File folder = new File(uploadPath);
if(!folder.exists()) folder.mkdir();
//실제 파일 이름
String fileName = upload_file.getOriginalFilename();
File target = new File(uploadPath, fileName);
//파일을 해당 폴더에 저장하자
//저장방법은 크게 두가지
//1. FileCopyUtiles 이용
try {
FileCopyUtils.copy(upload_file.getBytes(), target);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("upload : " + fileName);
model.addAttribute("fileName", fileName);
//2. File인스턴스 이용
return "result";
}
@PostMapping("upload2")
public String upload2(MultipartFile[] upload_files, Model model) throws IOException {
//파일들의 이름을 저장할 리스트를 생성하자(임시)
List<String> list = new ArrayList<String>();
//지금은 단순히 upload라고 하는 폴더에 그대로 파일을 저장하고 있지만
//폴더 구조를 잡고 저장하는 것이 더 있어보인다.(?)
//파일 이름도 실제 저장할 때는 OriginName / 저장할 이름 두가지로 나누어 저장 하는 것도 하나의 방법
//같은 이름을 사용하면 덮어버리기 때문
//사전에 null이 아닌지 확인해주는게 좀 더 안전함
if(upload_files != null) {
Resource res = resLoader.getResource("resources/upload");
if(!res.getFile().exists()) res.getFile().mkdir();
for(MultipartFile mfile : upload_files) {
//조금 더 커팅해보면
if(mfile.getSize()>0) { //파일이 있으면
mfile.transferTo(new File(res.getFile(), mfile.getOriginalFilename()));
list.add(mfile.getOriginalFilename());
}
}
}
model.addAttribute("list", list);
return "result";
}
@GetMapping("download")
public String download(Model model, String fileName) {
Map<String, Object> fileInfo = new HashMap<String, Object>();
fileInfo.put("fileName", fileName);
// System.out.println(fileName);
model.addAttribute("downloadFile", fileInfo);
// model.addAttribute("downloadFile", fileName);
return "fileDownLoadView";
}
}
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<resources mapping="/upload/**" location="/upload/"></resources>
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.ssafy.mvc.controller" />
<beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
id="multipartResolver">
<beans:property name="defaultEncoding" value="UTF-8"/>
<!-- 용량 작성 단위는 바이트 단위를 하겠다. -->
<beans:property name="maxUploadSize" value="10485760"/>
</beans:bean>
<beans:bean class="com.ssafy.mvc.view.FileDownLoadView" id="fileDownLoadView"/>
<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<beans:property name="order" value="0"></beans:property>
</beans:bean>
- MyBatis 구성
- 환경설정 파일
- MyBatis 전반에 걸친 세팅
- DB접속정보, 모델 클래스 정보, 매핑 정보
- Mapper
- 사용할 SQL문 정의
- Mapped Statement
- SqlSession 빌드 및 사용
- SQL문 실행
- Input/Output
- SQL 실행 시 필요한 데이터
- SQL 실행 결과
- SqlSessionFactory
- 모든 마이바티스 애플리케이션은 이 인스턴스 사용\
- sql와 MyBatis 연
- SqlSessionFactory는 SqlSession을 만듦
- SqlSession
- 데이터베이스에 대해 SQL 명령어를 실행하기 위한 메서드 포함
- SqlSessionFactory 빌드하기(예시)
- 환경설정 파일
String resource = "config/mybatis-config.xml";
Reader reader = Resources.getResourcesAsReader(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader));
- 별칭 정하기 (편의를 위한 것!!)
- 패키지에 대한 별명 지정 가능. type에 풀 패키지 명. alias에는 별명 지정
<typeAliases>
<typeAlias type="com.ssafy.board.model.dto.Board" alias ="Board" />
</typeAliases>
https://hip-tuck-d59.notion.site/2023-04-25-REST-API-84459e60dbc7483791e544aab0467f87
https://hip-tuck-d59.notion.site/2023-04-26-Swagger-71e3e9c83ede4ad0aa170b41f317287c
properties
typeAliases
environments
mappers