Jam's story
[Spring] days02 - AOP 본문
스프링이란: 자바 표준 프레임워크
스프링 사용하는방법 :
jar 파일 추가
-스프링 기능별로 주요 모듈 + 의존관계 ->jar 추가
-메이븐 빌드도구 : 프로젝트 생성 +배포 /pom.xml <dependency>
-서버 중앙저장소 (centeral repository)->jar 다운받아서 ->로컬저장소
에러메세지
7월 12, 2022 10:17:28 오전 org.springframework.context.support.AbstractApplicationContext prepareRefresh
정보: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@887af79: startup date [Tue Jul 12 10:17:28 KST 2022]; root of context hierarchy
Exception in thread "main" java.lang.IllegalStateException: CGLIB is required to process @Configuration classes. Either add CGLIB to the classpath or remove the following @Configuration bean definitions: [config]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:214)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:145)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:640)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:630)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:405)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:65)
at springDI.Ex02.main(Ex02.java:34)
이 파일을 추가해주면 된다 .
에러메세지
Config.java -> applicationContext.xml 파일 대신에 자바파일을 사용 - di.Config.java
@Configuration, @Bean 어노테이션을 사용해야 된다.
RecordViewImpl 객체는 RecordImpl 객체에 의존한다
RecordViewImpl.java에서 이 두 부분을 이용하여(나머지코드생략) , Config.java 파일 만듬
package di;
import java.util.Scanner;
public class RecordViewImpl implements RecordView {
private RecordImpl record =null;
//프로퍼티 의존성 주입방식
public void setRecord(RecordImpl record) {
this.record=record;
}
}
package di;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//XML 파일을 대신할 JAVA 설정 파일(DI)
//@Configuration, @Bean 어노테이션을 사용해야 된다.
@Configuration
public class Config {
//RecordImpl record = new RecordImpl();
//<bean id="record" class="di.RecordImpl"></bean>
@Bean
public RecordImpl record() {
return new RecordImpl();
}
/*
* <bean id="rvi" class="di.RecordViewImpl"> <property name="record"
* ref="record"></property> </bean>
*
*/
@Bean(name="rvi")
public RecordViewImpl getRecordViewImpl() {
RecordViewImpl rvi = new RecordViewImpl();
rvi.setRecord(record());//setter 프로퍼티를 통해서 DI
return rvi;
}
}
name="식별값"
// @Bean(name="rvi")
//식별값이 rvi인, 타입이 RecordViewImpl 인 객체를 가져와라
RecordViewImpl rvi=ctx.getBean("rvi",RecordViewImpl.class);
Ex02.java
package springDI;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.GenericXmlApplicationContext;
import di.Config;
import di.RecordViewImpl;
public class Ex02 {
public static void main(String[] args) {
//스프링 주요 모듈을 사용하여 recordViewimpl 객체 생성하여
//성적 입력, 출력하는 코딩을 하겠다.
/*
* 스프링 주요 모듈(jar) 추가
* 1) 메이븐 빌드도구를 사용하면 pom.xml <dependency></dependency>
* 2)라이브러리에 jar파일추가
*
*
* file:///C:/spring-framework-3.0.2.RELEASE/docs/spring-framework-reference/htmlsingle/spring-framework-reference.html
*/
//이제는 객체생성을 new 연산자로 하지 않는다.
/*
* String resourceLocations="applicationContext.xml";
* GenericXmlApplicationContext ctx=new
* GenericXmlApplicationContext(resourceLocations);
*
* //Object 리턴->RecordViewImpl클래스로 다운캐스팅 RecordViewImpl
* rvi=(RecordViewImpl)ctx.getBean("rvi");
*
* rvi.input(); rvi.output(); System.out.println("end");
*/
AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext(Config.class);
//스프링 컨테이너(공장)_= 스프링 빈 객체 생성+조립
//공장안에서 getBean해서 가져오자
//RecordViewImpl rvi=(RecordViewImpl)ctx.getBean("rvi");
RecordViewImpl rvi=ctx.getBean("rvi",RecordViewImpl.class); // @Bean(name="rvi")
}
}
Project springDI03 만들기
어노테이션 이용해서 객체 간 의존 자동 연결
@Autowired (생성자 ,필드 ,메소드 ) 세 곳에 적용 가능
@Resource
@Inject
@Autowired 또는 @Resource 어노테이션을 사용하려면 xml 파일에 추가
<context:annotation-config></context:annotation-config>
혹은 단일태그 <context:annotation-config/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- @Autowired 또는 @Resource 어노테이션을 사용하려면 추가
의존 자동연결할때 필요한 객체들을 등록하는 코딩 -->
<context:annotation-config></context:annotation-config>
@Autowired 사용하기
RecordImpl 스프링 빈객체가 생성되면 == <bean id="record" class="di.RecordImpl"></bean>
자동으로 의존 주입(DI)
만약 , 일치하는 스프링 빈이 없다면 예외를 발생하는데
예외를 발생시키지 않으려면 @Autowired(required=false) 로 설정
package di;
import java.util.Scanner;
import org.springframework.beans.factory.annotation.Autowired;
public class RecordViewImpl implements RecordView {
@Autowired
private RecordImpl record =null;
}
(코드 밑부분은 생략)
이름을 이용하여 주입할때는 @Resource
/*p111 xml에서 <bean id="record2" class="di.RecordImpl"></bean> */
@Resource(name="record2")
private RecordImpl record =null;
Spring04 프로젝트 만들기 + 생성
Ex02.java
p115 컴포넌트 스캔을 이용한 빈들을 등록
만약. xml에 생성해야하는 객체가 수백개가 되어야 한다면 ?
컴포넌트 스캔
패키지를 설정하면, 특정 패키지 안에서 스캔이되어져서 자동으로 빈으로 등록이 되고, 필요한 것들은 자동으로 주입이 되는것
= 빈 객체 자동으로 스캔하고 + 자동의존 주입
xml에 추가.
di 만 등록하면 그 밑에 있는 모든 패키지를 (하위패키지) 모두 등록됨
<context:component-scan base-package="di"></context:component-scan>
RecordViewImpl.java
(이름 =id 가 없으니 @Resource말고 @Autowired)
@Component //자동스캔
public class RecordViewImpl implements RecordView {
@Autowired //자동으로 주입까지 시켜줌
private RecordImpl record = null;
}
에러가 났는데,
검색된 클래스를 빈으로 등록할때 (첫글자를 소문자)로 빈의 이름을 사용한다.
RecordViewImpl rvi=(RecordViewImpl)ctx.getBean("recordViewImpl");
@component
public class ProductService{}
ProductService svc=context.getBean("productService",ProductService.class)
스프링 프레임워크 사용하면 객체생성+조립 x (Sample s=new Sample())
지금까지 프로젝트 정리
[springDI] xml 파일에 생성자 혹은 setter 로 주입
[springDI02] Config 자바파일로 주입 JavaSE-1.8 을 Jre 로 바꿔야한다.
[springDI03] xml추가하고 @Autowired @Resource한다.
[springDI04] 컴포넌트 스캔(스프링 가능) 스캔 대상이 되는 클래스 - 스프링 빈 객체 생성
@Component //자동스캔
public class RecordViewImpl implements RecordView {
@Autowired //자동으로 주입까지 시켜줌
private RecordImpl record = null;
}
@Component("rvi") 괄호안에 이름을 주면 id는 설정한 id로 (rvi)로 설정되고,
설정하지 않으면 기존 클래스의 소문자로 지정됨
@Component("rvi") //자동스캔
public class RecordViewImpl implements RecordView {
@Autowired //자동으로 주입까지 시켜줌
private RecordImpl record = null;
}
.xml
<context:component-scan base-package="부모패키지"></context:component-scan>
하위패키지도 자동 스캔된다.
p118 Top
@Component 어노테이션
ㄴ @Service 서비스클래스
ㄴ@Repository : DAO 클래스
ㄴ@ Controller: MV[C] 컨트롤러 클래스
스캔 대상 클래스 범위 지정하기
SpringAOP 프로젝트 생성
AOP = 관점지향적인 프로그래밍 기법
2가지관점
1) 공통부분: 인증(로그)처리, 트랜잭션 ,보안 부분 [공통적인 부분을 cross-cutting concern]
2)본연의 업무 (로직부분) => 글쓰기, 글수정 ,글삭제 부분 [핵심 관심 사항 core concern]
글쓰기 /write.do/ writeHandler /세션인증 null-> 로그인 요청 이동
글수정 /edit.do/ EditHandler /세션인증 null-> 로그인 요청 이동
글삭제 /delete.do/DeleteHandler /세션인증 null-> 로그인 요청 이동
- +++ 반드시 알아야할 AOP 용어 +++ (암기)
- Aspect: 여러 객체에 공통으로 적용되는 기능 (공통기능)
- Advice: 공통기능을 핵심기능에 [언제] 적용할지를 정의 - 전+후, 전, 후
- Weaving: advice 를 핵심로직코드에 적용 (삽입) 하는 것.
- Joinpoint: Advice 를 적용 (삽입) 가능하는 지점- 예) add() 메서드 호출
- Pointcut: Advice 를 실제 적용한 지점.
- 스프링에서는 정규표현식, [AspectJ문법] +++ 을 이용해서 Pointcut 을 설정할 수 있다.
- 세가지의 Weaving 방식
- 컴파일 시
- 클래스 로딩 시
- 런타임 시
- 프록시 (proxy) 기반으로 AOP 지원하기 때문에
- Joinpoint 를 메서드만 사용할 수 있다.
- 스프링 AOP 구현하는 3가지 방법
- 스프링 API 이용한 AOP 구현 X
- XML 스키마 기반의 POJO 클래스를 이용한 AOP 구현 O - xml 파일
- AspectJ 에서 정의한 @Aspect 어노테이션 기반의 AOP 구현 O - @Aspect 어노테이션
- Advice 종류
- Before Advice
- Around Advice
- After Advice,
- After Throwing Advice,
- After Returning Advice
SpringAOP 프로젝트 AOP
app.Calculator.java 인터페이스 덧.뺄.곱.나눗셈메소드
aop.Calculator.impl.java 구현클래스
Ex01.java main(){
//CalculatorImpl 객체 생성해서 계산
}
자바로
Calculator 인터페이스
package aop;
public interface Calculator {
int add(int x,int y);
int sub(int x,int y);
int mult(int x,int y);
int div(int x,int y);
}
CalculatorImpl.java
package aop;
public class CalculatorImpl implements Calculator {
@Override
public int add(int x, int y) {
long start=System.nanoTime();
int result=x+y;
long end=System.nanoTime();
System.out.printf("처리시간 :%dns\n",(end-start));
return result;
}
@Override
public int sub(int x, int y) {
long start=System.nanoTime();
int result=x-y;
long end=System.nanoTime();
System.out.printf("처리시간 :%dns\n",(end-start));
return result;
}
@Override
public int mult(int x, int y) {
long start=System.nanoTime();
int result=x*y;
long end=System.nanoTime();
System.out.printf("처리시간 :%dns\n",(end-start));
return result;
}
@Override
public int div(int x, int y) {
long start=System.nanoTime();
int result=x/y;
long end=System.nanoTime();
System.out.printf("처리시간 :%dns\n",(end-start));
return result;
}
}
springAOP2프로젝트 생성
CalculatorImpl.java를 springAOP 에서 복사해와서 공통된 기능들은 모두 제거
@Override
public int add(int x, int y) {
int result=x+y;
return result;
}
앞뒤로 지웠으니 around advice생성
라이브러리 추가
LogPrintAroundAdvice
package aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StopWatch;
// com.springsource.org.aopalliance-1.0.0.jar 추가
// MethodInterceptor 인터페이스를 구현해야 된다. - AroundAdvice
public class LogPrintAroundAdvice implements MethodInterceptor{
// method == add()/ sub()/ mult()/ div()
@Override
//호출한 메소드가 인자값으로 들어온다.
public Object invoke(MethodInvocation method) throws Throwable {
String methodName = method.getMethod().getName();
// 스프링 에서 제공하는 StopWatch 클래스 -> 로그 처리.
Log log = LogFactory.getLog(this.getClass());
StopWatch sw = new StopWatch();
log.info("> " + methodName + "() start.");
sw.start();
Object result = method.proceed(); //핵심기능 (add(), sub(),,,)
//메소드가 실행되고 반환되는 값을 Object로 받음
sw.stop();
log.info("> " + methodName + "() stop.");
log.info("> " + methodName + "() 처리 시간 : " + sw.getTotalTimeMillis() +"ms");
// 로그 처리
return result;
}
} // class
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="calc" class="aop.CalculatorImpl"></bean>
<bean id="logPrintAroundAdvice" class="aop.advice.LogPrintAroundAdvice"></bean>
<!-- calc의 add() sub() 등등 메서드등을 pointcut으로 설정+aroundadvice 등록 -->
<!--스프링 AOP: 프록시(proxy)기반 => : 핵심기능+ 공통기능 같이 처리하는 가짜 프록시를 만들자 -->
<bean id="calcProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- [1] -->
<property name="target" ref="calc"></property>
<!--[2] pointcut 설정= 적용되는 지점 -->
<!-- name값은 우리가 주는것이 아니다. -->
<!-- setProxyInterface ->proxyInterface -->
<property name="proxyInterfaces">
<list>
<value>aop.Calculator</value>
</list>
</property>
<!-- [3] Advice 등록 -->
<property name="interceptorNames">
<list>
<value>logPrintAroundAdvice</value>
</list>
</property>
</bean>
</beans>
Ex01.java
package springDI;
import org.springframework.context.support.GenericXmlApplicationContext;
import aop.Calculator;
import aop.CalculatorImpl;
public class Ex01 {
public static void main(String[] args) {
String resourceLocations="applicationContext.xml";
GenericXmlApplicationContext ctx= new GenericXmlApplicationContext(resourceLocations);
//Calculator calc=ctx.getBean("calc", CalculatorImpl.class);
Calculator calc=ctx.getBean("calcProxy", CalculatorImpl.class);
System.out.println(calc.add(4, 2));
}
}
- Calculator 인터페이스를 하나 선언함
- CalculatorImpl 클래스 구현함 = 여기서 공통기능은 다 빼고 핵심기능만 냅둠
- 보조기능을 할수있는 aroundAdvice인 LogPrintAroundAdvice클래스만듬 (로그기록- 전후 )
- Calculator calc=ctx.getBean("calcProxy", Calculator.class); 보조기능이 장착된 Proxy를 쓰자
LogPrintBeforeAdvice .java
package aop.advice;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;
public class LogPrintBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, //add()
Object[] args, //그 메소드 호출할때 매개변수들
Object target //핵심 기능이 구현된 실제객체 (calc)
) throws Throwable {
String methodName=method.getName();
Log log=LogFactory.getLog(this.getClass());
//호출되는 지 확인만 해보는 예제
log.info(">>>"+methodName+"() logPrintBeforeAdvice 호출됨 ");
}
}
xml에 추가
<bean id="logPrintBeforeAdvice" class="aop.advice.LogPrintBeforeAdvice"></bean>
<property name="interceptorNames">
<list>
<value>logPrintAroundAdvice</value>
<value>logPrintBeforeAdvice</value>
</list>
</property>
LogPrintAfterReturningAdvice.java
package aop.advice;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterReturningAdvice;
//핵심기능이 수행후에 예외가 발생하지 않았을경우
public class LogPrintAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning
(Object returnValue,
Method method, //add()
Object[] args, //매개변수
Object target) throws Throwable {
String methodName=method.getName();
Log log=LogFactory.getLog(this.getClass());
//호출되는 지 확인만 해보는 예제
log.info(">>>"+methodName+"() logPrintAfterReturningAdvice호출됨 :"+ returnValue);
}
}
xml에 추가
<!-- id는 소문자 -->
<bean id="logPrintBeforeAdvice" class="aop.advice.LogPrintBeforeAdvice"></bean>
<property name="interceptorNames">
<list>
<value>logPrintAroundAdvice</value>
<value>logPrintBeforeAdvice</value>
<value>logPrintAfterReturningAdvice</value>
</list>
</property>
실행결과
springAOP02 복사해서 springAOP02 _02
- advice 파일마다 @Component를 붙여주었고 xml 파일을 수정하였다.
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="aop"/>
<bean id="calcProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
</bean>
</beans>
에러메세지
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'calcProxy' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'calc' while setting bean property 'target'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'calc' is defined
@Component("calc")로 바꾸어주어야한다.
오늘까지 배운것은 AOP방법인데 잘 쓰이지 않는다.
XML 스키마 기반의 POJO 클래스를 이용한 AOP구현
/*
* 스프링 AOP 3가지 방법 중에
* *** [ XML 스키마 기반 AOP 구현 ] *** p209
* (처리과정)
* 1) 스프링 AOP를 사용하기 위해 jar 의존파일 추가
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* 2) aop.advice 패키지 -- 삭제
* ㄴ B, A,A advice 3가지
* --> 공통 기능을 제공할 클래스 추가
* aop.LogPrintProfiler.java
* trace() 구현 - Around Advice
* 3) xml 설정 파일
* ㄴ aop 설정하는 태그
* Aspect를 설정
* Advice를 어떤 Pointcut에 적용할지를 설정(지정)
* */
'Spring' 카테고리의 다른 글
[Spring] MVC2 모델을 이용한 게시판 구현 (0) | 2022.07.14 |
---|---|
[Spring] 다이나믹웹프로젝트생성과 MVC2패턴 (0) | 2022.07.13 |
[Spring] days03- @Aspect 애노테이션을 이용한 AOP (0) | 2022.07.13 |
[Spring] 뉴렉처강의_DI (0) | 2022.07.12 |
[Spring] days1 (0) | 2022.07.11 |