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 |
---|