newlec Spring AOP
1. AOP란?
AOP는 스프링이아니라 방법론이다. 이것을 구현할때 스프링이 지원을 해준다.
AOP방법론으로 구현하면 어떤 도움을 받을 수 있는지 알아보고자 한다.
일단 스프링과 관계없이 AOP란 무엇인지를 알아야한다.
Aspect Oriented Programming = 관점지향형 프로그래밍이다.
우리는 Object Oriented Programming 객체 지향에만 관심이 있었다.
사용자가 원하는 업무기반의 로직에만 관심이 있었다.
사용자의 업무를 분석하고 그에대한 로직만 구현했다.
그런데 사실 사용자 요구사항 말고 여러 코드들이 더들어가게 되었다.
개발자 관리자가 구현하든 테스트 하든 필요한 것들을 끼워넣엇다.
사용자는 모르는 내용이다. 개발자 운영자가 필요한 것 이런 내용들을 AOP이다.
관점이 다른 업무라고 보면 좋다. 개발자나 운영자가 필요한 것을 만드는 것이다.
관점지향 프로그래밍은 어찌보면 객체지향보다 큰 범위라고 할 수 있다.
업무들을 분류해서 만드는 것이다.
Primary(Core) Concern과 Cross-cutting Concern
관점에 해당되는 주관심사에 있는것을 객체로 만들고 실질업무를 메소드로 만들었다. 객체들이 이용관계에 있으니 화살표료 표시를 햇다.
가끔은 필요로의해서 로그처리(성능테스트 사용자요구권한처리), 보안처리, 트랜잭션처리 등의 로직이 필요하다.
이런거는 사용자가 요구한것이아닌 이것을 수반하려면 필요한 내용이다.
실질적인 업무를 처리하는 것에서 위와 아래를 담당하게 된다.
Cross-cutting ? 위에서 아래로 가는 흐름이 있기때문에 잘라서 바꿀 수 있게 해야하고 이것을 뺏따 넣엇다 해야한다.
그래서 과거에는 이 커팅을 하는 방법을 하기 힘들었다.
소스코드에 직접 했다가 주석을 했다가 하는 방식이었다.
또한 주석을 사용하기 때문에 소스가지고 있는 사람만이 이것을 할 수 있는 것이 문제엿다.
쉽게 하고자 해서 나온게 AOP이다. 관점에 해당되는 코드를 마치 꽃은거처럼 진행되게 하는 것이다.
프로그램은 흐름이니 주업무는 따로 만들고 중간에 꼽아넣는 것이다.
먼저 크로스커팅이 하게된후 코어를 불러오게 함으로써 중간에 껴있는 거처럼 되게 만드는 것이다.
이걸 스프링을 이용하면 매우 쉽게 해결 할 수 있게 된다.
2. AOP 자바 코드 이해하기
일단 먼저 자바만으로 AOP를 구현해보고자 한다.
권한을 주기 위해 하거나 자바코드를 공부하면서 너무 느린거같아서 시간을 확인하는 작업등을 했었다.
업무적 관점에서 소스코드를 넣는 것이 과거에는 하나 하나 넣고 없애고 했어야햇다.
또한 소스코드가 없다면 아예 가져다 쓰는것도 불가능햇엇다.
AOP를 구현하고 싶다면 주업무와 크로스커팅을 나눠서 작성해야한다.
이것이 Proxy클래스이다.
int total = proxy.total();
사용자는 프록시를 호출하는 것이고 이것을 호출하는 지점에서 앞뒤로 곁다리 업무를 하는 것이다..
마치 코드를 꽃아넣은 듯한 결과를 낳게 된다.
윗부분 실행 -> 코어 -> 다음부분실행 하는 것처럼보인다.
invoke로 실질적인 객체를 소환해서 처리한다.
proxy를 spring di로 함으로써 코드 변경과 수정을 할 수 있게 되는 것이다.
3. 순수 자바로 AOP 구현해보기
aop를 하기 위해서는 업무 로직이 필요하니 이전의 Exam을 활용해보자.
result를 만들어서 바로 생성되는게아니라 계산하고 중간에 넣을 수 있도록 해보자.
@Override
public int total() {
int result = kor + eng + math + com;
return result;
}
@Override
public float avg() {
float result = total() / 4.0f;
return result;
}업무자의 관점에서 시간이 얼마나 걸리는지 알기 위해서 계산 전후로 업무를끼워넣을 수 있다.
@Override
public int total() {
long start = System.currentTimeMillis();
int result = kor + eng + math + com;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다.";
System.out.println(message);
return result;
}성능을 테스트 해보기 위해 시간을 측정하는 것을 넣었다.
과거에는 직접 이렇게 주업무에 넣어서 작성했어야햇다.
그런데 소스코드가 없다면 이 주 업무를 건들 수 없는 문제가 존재한다.
이 곁다리 업무를 여기에 작성하지 않도록 하는 것이 AOP이다.
Proxy클래스가 곁다리 업무를 업무로직과 연결하는 역할을 한다.
Exam proxy = Proxy.newProxyInstance()메소드로 가짜 인스턴스를 만들어야한다.
타입은 만들고자 하는 객체의 타입으로 한다.
proxy는 가짜로 다르지만 기능이 닮아잇다.
사용자 입장에서는 exam을쓰는 것처럼느껴지지만 proxy가 기능을 하는 것이다.
loader = 실제로 불러올 객체 NewlecExam.class
intefaces = 복수형이니 배열로 가져오기 new Class[] {Exam.class} 구현해야할 인터페이스 타입의 정보를 건네준다.
여러 인터페이스를 구현하고 있다면 배열에 넣어주면된다.
h는 곁다리 업무이다.
구현할 내용 즉 new InvocationHandler()인터페이스를 구현한 객체가 들어가는 것이다.
별도의 클래스파일을 만들어야하지만 익명 클래스로 직접 적상할수잇다.
곁다리 업무 사이에 method.invoke(실제업무할객체, 호출할 메소드파라미터)
인자가빠지지 않도록 파라미터에 넣어줘야한다.
invoke는 호출하면 반환하는 값이 모든 형태를 반환한다.
그래서 Object로 받는다.
NewlecExam.class.getClassLoader()로더를 필요로하니 로더를 넣어주고
object 로 반환해주기 때문에 Exam으로 형변환도 해준다.
곁다리 업무가 필요없으면 실제 객체를 호출해서 하면된다.
System.out.printf("total is %d\n", exam.total());을 하면된다.
System.out.printf("total is %d\n", proxy.total());
System.out.printf("total is %f\n", proxy.avg());
인터페이스를 공유하고 있난 다른 메소드도 호출할 수 있다.
즉 다른 주 업무도 프록시 업무로 할 수 있다.
public class Program {
public static void main(String[] args) {
Exam exam = new NewlecExam(1, 1, 1, 1);
Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(), new Class[] { Exam.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(exam, args);
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다.";
System.out.println(message);
return result;
}
});
System.out.printf("total is %d\n", proxy.total());
// System.out.printf("total is %d", exam.total());
}
}4. 스프링으로 AOP 구현해보기-AroundAdvice
실제 업무가 되는게 아니라 프록시로 하게 햇엇다.
스프링이 자바코드로 구현 했던 것을 자유롭게 만들 수 있다.
AOP를 구현하고자 한다면 스프링이던 아니던 프록시를 이용해야한다.
IoC컨테이너에 넣어두고 곁다리업무 주업무를 꽃아넣을 수 있게 할 수 있다.
곁다리 업무를 주업무 앞에만 넣을 수도 있는데 하기전 검사하는 등 어떤경우에만 뒤로만 넣을 수도잇다.
그래서 스프링은
Before Advice/ After retrunning Advice/ After throwing Advice / Arround Advice
시작하기전 / 이후 / 예외처리 / 앞뒤로 필요한 것
4가지로 구분해서 처리한다.
가장 알맞는 녀석을 꽃아서 사용하면된다. 이번에 구현했던것은 Arround였다.
스프링 AOP를 하는 방법은 역시나 XML로 하거나 어노테이션으로 처리할 수 있다.
먼저 XML에서 처리하는데 필요한 코드는 다음과 같다.
1.Exam exam = new NewlecExam(1, 1, 1, 1);생성하는코드
<bean id="target" class="AOPstudy.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>2.프록시 생성하는 코드
<bean id="LogAroundAdvice" class="AOPstudy.aop.advice.LogAroundAdvice"/>
<!-- 프록시 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>LogAroundAdvice</value>
</list>
</property>
</bean>setTarget으로 위에서 생성한 exam 객체를 꽃는 것이다.
자바코드를 사용할때는 인터페이스 등등을 넣었는데 이부분은 스프링이 알아서 넣어준다.
핸들러만 넣어주면된다. 핸들러는 복수로 넣어줄 수 있다.
이 핸들러는 객체 생성을 해줘야한다.
list의 value에 핸들러 이름을 넣어주면된다.
3.곁다리 업무 하는거 꽃는 코드
MethodInterceptor인터페이스를 구현하는 업무 객체를 따로 만들어주면된다.
구현해야하는 invoke메소드는 이전과 비슷하지만 invoke가 proceed가되엇을뿐이다.
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다.";
System.out.println(message);
return result;
}프로그램 설계할때 특이한점은 모든 설정을 XML로 햇다는 것이다.
객체를 만들고 결합되는 관계를 잘 이해하기만 하면된다.
proxy라는 이름으로 만들엇으니 proxy라는 이름으로 getBean해서 실행하면된다.
public class Program {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AOPstudy/aop/setting.xml");
//ApplicationContext context = new AnnotationConfigApplicationContext(NewlecDIConfig.class);
Exam proxy = (Exam) context.getBean("proxy");
System.out.printf("total is %d\n", proxy.total());
System.out.printf("total is %f\n", proxy.avg());
}
}주업무 내용만 호출하고 싶다면 exam객체를 만들엇으니 exam을 호출하면된다.
5. BeforeAdvice 구현하기
앞에만 삽입하는 것이다.
<bean id ="logBeforeAdivce" class="spring.aop.advice.LogBeforeAdivce"/>추가하고 핸들러는 리스트이기 때문에 그냥 밑에 추가해도된다.
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<value>logAroundAdvice</value>
<value>logBeforeAdivce</value>
</list>
</property>LogBeforeAdivce클래스는 이전에 실행될 메소드이기때문에 MethodBeforeAdvice인터페이스를 구현하면된다.
구현해야할 메소드는 다음과 같다.
public void before(Method method, Object[] args, Object target) throws Throwable {}
구현하는데 잇어서 현재 실행되는 함수의 파라미터를 얻고 싶다면 args
타겟에 대한 객체를 이용하고 싶다면 target을 사용하면된다.
하지만 저런 파라미터들과 관련없는 것을 실행하고 싶다면 로직만 작성하면된다.
앞에 들어가기 때문에 함수를 호출하는 과정도 필요없다.
public class LogBeforeAdivce implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("앞에서 실행될 로직");
}
}6. After Returning / Throwing 어드바이스 구현하기
6.1 After Returning
AfterReturningAdvice 인터페이스를 구현해야한다.
before와 차이점은 다음과 같다.
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
애프터는 코어 함수가 호출된후 반환값을 받는다 필요하다면 사용하면된다.
public class LogAfterReturningAdivce implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("returnValue: " + returnValue + ", method: " + method.getName());
}
}6.2 Throwing 어드바이스
ThrowsAdvice를 구현해야한다.
얘는 구현해야할 함수가 정해지지 않기때문에 인자가 달라진다.
public void afterTrowing(?(예외) e) throws Throwable{}
?부분 어떤 예외가 발생할지 모르기 때문이다. 예외이름을 넣어주면된다.
target이 있으면 total이든 뭐든 before가 호출된다.
그다음after로 간다. 이 중간에 예외가발생하면 throwing이 발생한다.
public class LogThrowingAdivce implements ThrowsAdvice {
public void afterTrowing(IllegalArgumentException e) throws Throwable {
System.out.println("예외 발생!!!");
}
}7. Point Cut(Weaving, Join Point)
원하는 메소드에만 곁다리 함수를 지정할 수 있다.
포인트컷 조인포인트 위빙
cross-cutting concern과 core concern을 엮는 것을 위빙(뜨개질)이라고 한다.
곁다리업무가 대상으로 삼고있는 메소드를 Join Point라고 한다.
target으로 삼고있는 내용이있다면 타겟을 대상으로 모든 메소드를 조인포인트라고 생각한다.
우리가 원하는 것은 한개의 메소드에만 추가 하고싶다. 이것을 Point Cut이라고 한다.
원하는 메소드만 할 수 잇게 된다.
<bean id="classicPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="total"/>
</bean>이름을 매핑해주고 각 어드바이스에 연결을 해줘야한다.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logBeforeAdivce"/>
<property name="pointcut" ref="classicPointCut"/> //setPointcut인데 그냥 pointcut
</bean>classicBeforeAdvisor를 만들고 포인트컷과 비포어드바이스와 연결해준다.
그리고 리스트에 비포어드바이저대신 추가해주면 total()이 실행될때만 실행되게 된다.
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"/>
<property name="interceptorNames">
<list>
<!-- <value>logBeforeAdivce</value> -->
<value>classicBeforeAdvisor</value>
</list>
</property>
</bean>어라운드에도 추가하면
<bean id="classicArroundAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAroundAdivce"/>
<property name="pointcut" ref="classicPointCut"/>
</bean>불편한점은 한개추가할때마다 결합을 하나씩해야한다는 점이 불편하다
이것은 클래식한거니 더 다양하게 만들 수 있다.
8. 간소화된 Advisor
필요에 따라서 어드바이저 포인트컷을 합치는 것이 필요하다.
NameMatchMethodPointcutAdvisor 이것은 포인트 컷이면서 어드바이저기능을 한다.
함수가 한개일때 Name
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdivce"/>
<property name="mappedName" value="total"/>
</bean>포함할 함수가 여러개일때 mappedNames하고 list에 넣어주면된다.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdivce"/>
<property name="mappedNames">
<list>
<value>total</value>
</list>
</property>
</bean>포인트컷이 설정했던 내용을 값으로 가지게 된다.
함수명이 많을 경우 하나하나 추가하기 어렵다.
RegexpMethodPointcutAdvisor 이 포인트컷을 사용하면된다.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdivce"/>
<property name="mappedNames">
<list>
<value>.*to.*</value>
</list>
</property>
</bean>정규식 문자를 사용해서 to가 들어간것이 실행되게 할 수 있다.
newlec Spring - MVC
0. Spirng Web 강의 소개
현재 기본적인 스프링에 대해 배웠다. DI, AOP
웹에 대해 배워야한다.
Tomcat 서블릿 jsp - web.xml을 설정해서 해서 스프링이 없어도 가능하다.
Tomcat
Tomcat Configuration
web.xml
Spring Dispatcher를 활용해서 한다.
Spring Web
Spring Configuration
-servlet
-service
-security
Mybaties
Tiles
컨트롤러를 모아놓는 Spring Dispatcher가 있다. 이것을 스프링이 지원한다.
여기에 스프링을 올리면 이런것을 스프링이 관리하게 되는 것이다.
톰캣에 있던걸 가지고 이설정을 xml annotation java 버전에 따라 스프링에 맞추어서 다 나뉘어서 설정을 한다.
이런설정을 도와주는 것이 spring boot이다.
시작내용들을 잡아주는 프레임워크이다.
Starter Configuration
application.properties OR YAML(제이슨같은것)
매우 설정이 줄어든다.
버전에 따라 다르고 xml 자바설정 이 뭐가다른지 어노테이션 어디에 하는건지 알 수없다.
결국 버전을 따라가는게 좋을 것이다.
이번에 배울 MVC과정은 덮어쓰기 과정 개발을 빨리하고싶다면 / Spirng Boot과정
1. Spring MVC란
스프링으로 웹개발을 해보자.
먼저 Dispatcher를 집중화하기 전 모델을 보면 다음과 같다.
모델2에서 controller & Dispatcher 이것을 서블릿으로 view단을 jsp로 하고 Dispatcher 로 포워딩을 햇엇다.
그런데 모든 컨트롤러가 Dispatcher를 구현해야하고 이것을 모두다 가지고 있는 것이 비효율적일 수 있다.
순수하게 Controller POJO(Plain Old Java Object)로 빼고 Dispatcher만 남기는 것이다.
포워딩하는 기능은 하나만 만들면된다.
사용자 요청을 컨트롤러에게 하고 뷰단에 보여준다.
단순히 요청하는게아니라 입력값이 있다면 입력값도 컨트롤러에 제공해야한다.
입력값을 단순히 GET파라미터 등이 아니라 서블릿 기능을 이용할 수 잇도록해서 자바로 변형해서 전달하도록 한다.
단순히 데이터 기본형이 아니라 객체화해서 담아서 보내줄 수 있다.
컨트롤러(pojo)는 서블릿하나도 사용안하고 만들 수 있게 되는 것이다.
결합을 낮춤으로서 컨트롤러를 다른데도 사용할 수 있도록 한다.
Dispatcher를 잘만들면 톰캣과 이별할 수도 있다.
Dispatcher서블릿이 알맞는 컨트롤러에대해 여러가지 url을 잘 처리해야한다.
알맞는 컨트롤러에 배분을 해줘야한다.
스프링이 또다른 문제가 생길수잇다. 꼭 스프링으로 만드는게 아니라 더 발전시킬 필요가 잇다.
MVC를 지원하기 위한 스프링 라이브러리들을 사용해야한다.
2. 실습환경 준비하기
톰캣 jdk등등 스프링이 필요한 실습환경 스프링을 위한 도구들을 말한다.
부트에는 톰캣이 내장되어 있어서 안해도된다. 기본적인 환경을 아는게 좋다.
https://spring.io 에서 Spring Tools 4 for Eclipse를 다운받자
스프링을 배제하고 메이븐으로만 웹프로젝트를 만들어보자.
다운받은것이 STS이다.
기존에는 이클립스 + STS플러그인을 다운받아 사용한것이다.
STS는 이클립스 베이스에 이 플러그인이 자동으로 추가된 IDE라고 보면된다.
기존 이클립스에 플러그인을 다운받아 쓰는 것은 버전 호환성 문제가 발생할 수 있으니 주의하는 것이 좋다.
스프링부트는 이런 톰캣 설치 등의 초기 설정을 없애주는 것이다.
3. 메이븐을 이용한 기본 웹 프로젝트 생성하기
메이븐 프로젝트를 만들면 기본적으로 메이븐 구성의 프로젝트가 만들어진다.
메이븐으로 war 만들면 플러그인이 없어서 오류나니 플러그인 추가해줘야한다
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>jsp파일만드는데 없으면 에러난다.
tomcat-api를 추가해줘야한다.
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-api -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>9.0.73</version>
</dependency>2023.04.17
AOP는 객체 지향 프로그램에서 우리가 중간에 필요한 일을 처리하기 위해 확장된 느낌이다.
트랜잭션 등등은 매우 필요한 기능이기 때문에 후에 어노테이션으로 처리하는 방법을 익힐 필요가 있어보인다.
'기초단계 > SPRING' 카테고리의 다른 글
| 2023.04.20 Spring (0) | 2023.04.20 |
|---|---|
| 2023.04.18 Spring (0) | 2023.04.18 |
| 2023.04.14 Spring (0) | 2023.04.17 |
| 2023.04.13 Spring (0) | 2023.04.13 |
| 2023.01.25-1 Spring (0) | 2023.01.25 |