Spring

부트캠프80일차 (AOP , 어노테이션정리 , spring소규모프로젝트)

동곤일상 2025. 6. 2. 16:08
반응형

 

 

1) AOP( 핵심 기능과 공통 기능을 분리하는 프로그래밍 방식 )

1-1) LA

1-2)ACA

1-3)TA

 

2) 어노테이션 정리

 

3) spring방식으로 shop만들어보기

3-1)  프로젝트생성  및 설정

3-2) controller

3-3)  config

 


1-1) LA

 AOP 관련 용어
*  pointcut : 핵심메서드 설정
 * [접근제어자] * annotation : annotation패키지 하위 메서드의 모든 리턴 타입

 advice : 실행되는 시점 설정
* 1) Before : 메서드 실행 전
 * 2) After : 핵심메서드 실행 후
 * 3) afterReturning : 핵심메서드 정상 실행 후
 * 4) afterThrowing : 핵심메서드 오류 발생 후
 * 5) around : 핵심메서드 실행 전,후

 

AOP클래스

package annotation;

@Component
@Aspect //AOP 처리클래스
@Order(3) //우선순위 3번쨰
public class LoggingAspect {
	
	//pointcut 설정 
	final String publicMethod   = "execution(public * annotation..*(..))";
	// annotation 패키지 및 하위 패키지의 모든 public 메서드에 Aspect를 적용
	
	//advice설정
	@Before(publicMethod)
	public void before() {
		System.out.println("[LA]Before 메서드 실행 전 호출");	
	}
	
	@AfterReturning(pointcut=publicMethod,returning="ret")
	public void afterReturning(Object ret) {
		System.out.println("[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : "+ret);
	}
	
	@AfterThrowing(pointcut=publicMethod,throwing ="e")
	public void afterThrowing(Throwable e) {
		System.out.println("[LA]afterThrowing 메서드 정상종료 후 호출, 예외msg : "+e.getMessage());
	}
	
	@After(value=publicMethod)
	public void afterFinally() {
		System.out.println("[LA] After 메서드 종료 후 실행");
	}
	

}

main실행

package main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import annotation.Article;
import annotation.ReadArticleService;
import kr.gd.aop.AppCtx;

public class Main1 {
	public static void main(String[] args) {
		//ApplicationContext : 컨테이너(객체들을 저장하는공간)
		ApplicationContext ctx = 
				new AnnotationConfigApplicationContext(AppCtx.class);
		//AppCtx에 @cofiguration이 없다면 동작하지않을거임(spring 환경설정)
		
		ReadArticleService service =
				ctx.getBean("readArticleService",ReadArticleService.class);
		//타입은 ReadArticleService이긴하지만 Impl(구현체)을 호출할거임
		//(@Component("readArticleSerice") 로 설정해놨음)
		
		try {
			//a1 : Article id값이 1인객체
			Article a1 = service.getArticleAndReadCnt(1);
			Article a2 = service.getArticleAndReadCnt(1);
			System.out.println("a1.hashCode() : "+a1.hashCode());
			System.out.println("a2.hashCode() : "+a2.hashCode());
			System.out.println("[main]a1==a2 : "+(a1==a2));
			service.getArticleAndReadCnt(0);
		} 
		catch (Exception e) {
			System.out.println("[main1_Exception] : "+e.getMessage());
		}
	}
}

--- -getArticleAndReadCnt(1)

[LA]Before 메서드 실행 전 호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : annotation.Article@791d1f8b

[LA] After 메서드 종료 후 실행

 

--- -getArticleAndReadCnt(1)

[LA]Before 메서드 실행 전 호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : annotation.Article@2415fc55

[LA] After 메서드 종료 후 실행

 

a1.hashCode() : 2031951755

a2.hashCode() : 605420629

[main]a1==a2 : false

 

----getArticleAndReadCnt(0)

[LA]Before 메서드 실행 전 호출

[LA]afterThrowing 메서드 정상종료 후 호출, 예외msg : 0입력 불가

[LA] After 메서드 종료 후 실행

[main1_Exception] : 0입력 불가

 


1-2)ACA

package annotation;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect 
@Order(2) //우선순위 2위 
public class ArticleCacheAspect {

    private final LoggingAspect loggingAspect;

	 private Map<Integer, Article> cache = new HashMap<>();

    ArticleCacheAspect(LoggingAspect loggingAspect) {
        this.loggingAspect = loggingAspect;
    }
	 
    //ReadArticleService의 모든메서드
	 @Around("execution(public * *..ReadArticleService.*(..))")
	 public Object cache(ProceedingJoinPoint joinPoint) throws Throwable {
		 Integer id = (Integer)joinPoint.getArgs()[0];
		 System.out.println("[ACA]"
				 +joinPoint.getSignature().getName()+"("+id+") 메서드호출 전");
		 
		 Article article = cache.get(id);
		 if(article!=null) {
			 System.out.println("[ACA] cache에서 Article["+id+"] 가져옴");
			 return article;
		 }
		 //다음 메서드 호출
		 Object ret = joinPoint.proceed();
		 
		 System.out.println("[ACA]"+joinPoint.getSignature().getName()
				 +"("+id+") 메서드 호출 후");
		 //ret이 null이 아니면서 Article로 형변환이 가능할 때 
		 if(ret != null && ret instanceof Article) {
			 cache.put(id, (Article)ret);
			 System.out.println("[ACA] cache에 Article["+id+"] 추가함");
		 }
		 return ret;
	 }
}

[ACA]getArticleAndReadCnt(1) 메서드호출 전

[LA]Before 메서드 실행 전 호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : annotation.Article@1130520d

[LA] After 메서드 종료 후 실행

[ACA]getArticleAndReadCnt(1) 메서드 호출 후

[ACA] cache에 Article[1] 추가함

[ACA]getArticleAndReadCnt(1) 메서드호출 전

[ACA] cache에서 Article[1] 가져옴

a1.hashCode() : 288379405

a2.hashCode() : 288379405

[main]a1==a2 : true

[ACA]getArticleAndReadCnt(0) 메서드호출 전

[LA]Before 메서드 실행 전 호출

[LA]afterThrowing 메서드 정상종료 후 호출, 예외msg : 0입력 불가

[LA] After 메서드 종료 후 실행

[main1_Exception] : 0입력 불가

 

 


1-3) TA ( TraceAspect ) 

annotation.UpdatetraceAspect.java

package annotation;

@Component
@Aspect
@Order(1)
public class UpdateTraceAspect {
	//args(..,id,info) : 매개변수로만 pointcut 지정
	// ..,id,info : 
	//매개변수의 마지막목록이 (id)String , (info)UpdateInfo인 메서드 선택
	@AfterReturning(pointcut = "args(..,id,info)",
			argNames = "ret,id,info",returning = "ret")
	public void traceReturn(Object ret , String id , UpdateInfo info) {
		System.out.println("[TA] 정보수정 결과 : "+ret+", 대상ID : "+id+
				", 수정정보 : "+info);
	}
}

@AfterReturning(pointcut = "args(..,id,info)",
argNames = "ret,id,info",returning = "ret")

의 부분과같이 자료형에따라 TA가 출력이 될수도있고 안될수도있다.

(자료형은 해당 메서드(traceReturn)의 매개변수를 따름)

 

 

 

 

main코드의 일부

(regist메서드는 updateInfo를매개변수로 갖지않기에 TA가 호출되지않을것임)

System.out.println("\n @@ UpdateMemberInfoTraceAspect연습 @@");
		MemberService ms = ctx.getBean("memberService",MemberService.class);
		ms.regist(new Member());
		ms.update("hong", new UpdateInfo());
		ms.delete("hong2", "test", new UpdateInfo());

@@ UpdateMemberInfoTraceAspect연습 @@

[LA]Before 메서드 실행 전 호출

MemberService.regist(Member) 메서드 호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : null

[LA] After 메서드 종료 후 실행

 

[LA]Before 메서드 실행 전 호출

MemberService.update(String,UpdateInfo)메서드호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : true

[LA] After 메서드 종료 후 실행

[TA] 정보수정 결과 : true, 대상ID : hong, 수정정보 : annotation.UpdateInfo@6f6745d6

 

[LA]Before 메서드 실행 전 호출

MemberService.delete(String,String,UpdateInfo)메서드호출

[LA]AfterReturning 메서드 정상종료 후 호출, 리턴값 : false

[LA] After 메서드 종료 후 실행

[TA] 정보수정 결과 : false, 대상ID : test, 수정정보 : annotation.UpdateInfo@27508c5d

 

2) 어노테이션 정리

 

 

1.환경설정 어노테이션

 * @Configuration : 스프링 환경설정해주는 클래스
 * @ComponentScan : 객체생성을 위한 패키지설정
 * @EnableAspectJAutoProxy : AOP를 사용하도록 설정
 * @Bean : 객체를 생성해줌(Configuration내에서만 사용)


 2.클래스에서 사용되는 어노테이션

 * @Component : 객체화되는 클래스
 * @Autowird : 자료형기준으로 객체주입(주입대상의 객체가없다면 오류)
 * @Autowird(required=false) : 주입대상의 객체가 없으면 null로 주입
 * @Scope : 일회용객체생성 ( 사용될떄마다 새로운객체로생성됨)

 3.AOP관련 어노테이션

 * @Aspect : AOP로 사용될 클래스로 지정
 * @Order(순서) : 순서는 before기준 . after의 경우는 역순실행
 * @Before : 핵심기능 수행 이전
 * @AfterReturning : 핵심기능 정상적으로 실행 후 리턴값조회 
 * @AfterThrowing : 핵심기능 예외발생 시  
 * @After : 핵심기능 수행 이후
 * @Arround: 핵심기능 수행 이전 , 이후 처리

 

 

3) spring방식으로 shop만들어보기

원래는 스프링부트를 사용하게된다면 tomcat이 필요없지만현재는 tomcat9.0을 이용해서 만들어보자

 

 

3-1) 프로젝트생성  및 설정

new Mavenproject를 고른 후 다음 선택창과같이 필터링 해줌

 

자바 버전 확인바람 (본인은 17사용)

 

tomcat 설정

 

pom.xml에 스프링관련 의존성추가

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>kr.gd</groupId>
	<artifactId>shop1</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>shop1 Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>5.3.39</spring.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<!-- springContext -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>


		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!--
		https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
			<version>3.1.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.13</version>
		</dependency>
		
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>2.0.6</version>
		</dependency>
		<!-- javax.annotation -->
		<dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>javax.annotation-api</artifactId>
			<version>1.3.2</version>
		</dependency>

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.6</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.38</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>shop1</finalName>
	</build>
</project>

 

 

web.xml 변경

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
 http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" >
  <display-name>shop1</display-name>
  <servlet>
  	<servlet-name>shop1</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextClass</param-name>
  		<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  	</init-param>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>config.MvcConfig
  					 config.DBConfig
  		</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping> 
  	<servlet-name>shop1</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
  
  <filter>
  	<filter-name>encoding</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  <filter-mapping> <!-- 모든 경로에 인코딩적용 -->
  	<filter-name>encoding</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

 


3-2)controller

controller

package controller;

import java.util.List;
import logic.Item;
import service.ShopService;

@Controller
@RequestMapping("item")
public class ItemController {
	
	@Autowired
	private ShopService service;
	
	@RequestMapping("list")
	public ModelAndView list() {
		ModelAndView mav = new ModelAndView();
		List<Item> itemList = service.itemList();
		mav.addObject("itemList",itemList);
		return mav;
	}
	
}

 

 

3-3) config

config.MvcConfig

package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration //spring 환경설정
//controller,logic,dao패키지를 등록
@ComponentScan(basePackages = {"controller","logic","service","dao"})
public class MvcConfig  implements WebMvcConfigurer{
	
	@Bean // configuration내에만 사용(객체등록)
	public HandlerMapping handlerMapping() {
		RequestMappingHandlerMapping hm = new RequestMappingHandlerMapping();
		hm.setOrder(0);
		return hm;
	}
	
	//컨트롤러가 반환한 뷰 이름(예: "home")을 
	//실제 JSP 파일 경로(예: /WEB-INF/view/home.jsp)로 변환하여 렌더링
	@Bean 
	public ViewResolver viewResolver() {
		InternalResourceViewResolver vr = new InternalResourceViewResolver();
		vr.setPrefix("/WEB-INF/view/");
		vr.setSuffix(".jsp");
		return vr;
	}
}

 

DBConfig

package config;

import java.beans.PropertyVetoException;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import com.mchange.v2.c3p0.ComboPooledDataSource;


@Configuration
public class DBConfig {
	
	//DBConnection객체
	@Bean(destroyMethod = "close")
	public DataSource dataSource() {
		ComboPooledDataSource ds = new ComboPooledDataSource();
		try {
			ds.setDriverClass("org.mariadb.jdbc.Driver");
			ds.setJdbcUrl("jdbc:mariadb://localhost:3306/gdjdb");
			ds.setUser("gduser");
			ds.setPassword("1234");
			ds.setMaxPoolSize(20);
			ds.setMinPoolSize(3);
			ds.setInitialPoolSize(5);
			ds.setAcquireIncrement(5);
		}
		catch (PropertyVetoException e) {
			e.printStackTrace();
		}
		return ds;
	}
	
	@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception{
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource());
		bean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
		return bean.getObject();
	}
	
	@Bean
	public SqlSessionTemplate sqlSessionTemplate() throws Exception{
		return new SqlSessionTemplate(sqlSessionFactory());
	}

}

 

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org/DTD config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<settings>
	<setting name="jdbcTypeForNull" value="VARCHAR"/>
</settings>
<mappers>
<!-- dao하위의 mapper패키지 사용 -->
	<package name="dao.mapper"/>
</mappers>
</configuration>

 

 

'Spring' 카테고리의 다른 글

부트캠프79일차( 스프링기초)  (0) 2025.05.30