<bean id="hello" class="myspring.di.xml.Hello">
<constructor-arg index="0" value="Srping"/>
<constructor-arg index="1" ref="printer"/>
</bean>
<bean id="hello" class="myspring.di.xml.Hello">
<constructor-arg name="name" value="Spring"/>
<constructor-arg name="printer" ref="printer"/>
</bean>
<bean id="hello" class="myspring.di.xml.Hello">
<property name="names">
<list>
<value>Spring</value>
<value>IoC</value>
<value>DI</value>
</list>
</property>
</bean>
<bean id="hello" class="myspring.di.xml.Hello">
<property name="ages">
<map>
<entry key="Kim" value="30" />
<entry key="Lee" value="35" />
<entry key="Ahn" value="40" />
</map>
</property>
</bean>
POJO 클래스 다이어그램
📋 Hello.java 📋
package myspring.di.xml;
public class Hello {
String name;
Printer printer;
public Hello() {}
public void setName(String name) {
this.name = name;
}
public void setPrinter(Printer printer) {
this.printer = printer;
}
public String sayHello() {
return "Hello" + name;
}
public void print() {
this.printer.print(sayHello());
}
}
package myspring.di.xml;
public interface Printer {
public void print(String message);
}
package myspring.di.xml;
public class StringPrinter implements Printer {
private StringBuffer buffer = new StringBuffer();
public StringPrinter() {
System.out.println(this.getClass().getName() + "생성자 호출됨");
}
public void print(String message) {
this.buffer.append(message);
}
public String toString() {
return this.buffer.toString();
}
}
package myspring.di.xml;
public class ConsolePrinter implements Printer {
public ConsolePrinter() {
System.out.println(this.getClass().getName() + "생성자 호출됨");
}
public void print(String message) {
System.out.println(message);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- StringPrinter 클래스를 Spring Bean으로 등록 -->
<!-- getBean() 메서드 사용시 bean의 id 사용 -->
<bean id="strPrinter" class="myspring.di.xml.StringPrinter" />
<!-- ConsolePrinter 클래스를 Spring Bean으로 등록 -->
<!-- 인터페이스는 작성할 필요X -->
<bean id="conPrinter" class="myspring.di.xml.ConsolePrinter" />
<!-- Hello 클래스를 Spring Bean으로 등록 -->
<bean id="hello" class="myspring.di.xml.Hello">
<property name="name" value="스프링"/>
<!-- value는 setName의 값으로 들어감 -->
<property name="printer" ref="strPrinter" />
</bean>
</beans>
package myspring.di.xml;
import static org.junit.jupiter.api.Assertions.*; // static 메서드를 테스트케이스에 많이 사용하여 static import를 사용하여 Assertions.생략 가능
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.GenericXmlApplicationContext;
public class HelloBeansJunitTest {
@Test
void hello() {
// 1. Spring Bean Container 객체 생성
// classpath에 xml 파일 경로 지정
BeanFactory factory = new GenericXmlApplicationContext("classpath:spring-beans.xml");
// 2. Container가 생성한 Bean을 요청하기
Hello hello1 = (Hello) factory.getBean("hello");
Hello hello2 = factory.getBean("hello", Hello.class); // 해당 방법 권장
// 3. HelloBean의 레퍼런스 비교하기
System.out.println(hello1 == hello2); // 싱글톤인지 아닌지 확인 목적
assertSame(hello1, hello2); // 주소가 같으면 테스트 성공으로 표시
assertEquals("Hello 스프링", hello2.sayHello()); // 예상한 값과 일치한지 확인
hello2.print();
Printer printer = factory.getBean("strPrinter", Printer.class);
assertEquals("Hello 스프링", printer.toString());
}
}
@Autowired, @Resource 어노테이션은 의존하는 객체를 자동으로 주입해주는 어노테이션
@Autowired는 타입으로, @Resource는 이름으로 연결됨
@Autowired
@Resource
@Value
@Qualifier
@Override
📋 Test.java 📋
package myspring.di.xml;
import static org.junit.jupiter.api.Assertions.*;
import javax.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
// Spring Bean Container 객체 생성 대신 @ExtendWith 사용 (싱글톤의 Application Context 보장)
// classpath로 경로 설정 대신 @ContextConfiguration (스프링 빈 설정 파일의 위치)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans.xml")
public class HelloBeanSpringTest {
// 1/ getBean 대신에 @Autowired 의존 받고 싶은 타입을 자동 연결
// type이 동일할 경우 Bean의 변수명과 Bean의 id가 동일한 것을 찾아옴 (동일한게 없을 경우 찾을 수 없다고 표시)
@Autowired
Hello hello;
@Autowired
// 2. Qualifier : 범위 한정자 (Autowired와 함께 기재)
@Qualifier("helloC")
Hello hello2;
// 3. 원하는 Bean의 id 값으로 가져옴
@Resource(name = "helloC")
Hello hello3;
@Autowired
@Qualifier("strPrinter")
Printer printer;
//@Autowired
//StringPrinter printer;
@Test
void helloC() {
assertEquals("Hello 생성자", hello2.sayHello());
assertEquals("Hello 생성자", hello3.sayHello());
}
@Test // @Disabled
void hello() {
assertEquals("Hello 스프링", hello.sayHello()); // import static으로 인해 Assertions.assertEquals와 동일
hello.print();
assertEquals("Hello 스프링", printer.toString());
}
}
<context:component-scan base-package=“myspring.di.annot" />
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- StringPrinter 클래스를 Spring Bean으로 등록 -->
<!-- getBean() 메서드 사용시 bean의 id 사용 -->
<bean id="strPrinter" class="myspring.di.xml.StringPrinter" />
<!-- ConsolePrinter 클래스를 Spring Bean으로 등록 -->
<!-- 인터페이스는 작성할 필요X -->
<bean id="conPrinter" class="myspring.di.xml.ConsolePrinter" />
<!-- Hello 클래스를 Spring Bean으로 등록 -->
<bean id="hello" class="myspring.di.xml.Hello">
<property name="name" value="스프링"/>
<!-- value는 setName의 값으로 들어감 -->
<property name="printer" ref="strPrinter" />
</bean>
<bean id="helloC" class="myspring.di.xml.Hello">
<!-- Constructor Injection -->
<constructor-arg index="0" value="생성자"/>
<constructor-arg index="1" ref="conPrinter"/>
<property name="names">
<list>
<value>Spring Framework</value>
<value>Spring Boot</value>
<value>Spring Cloud</value>
</list>
</property>
</bean>
</beans>
package myspring.di.xml;
import static org.junit.jupiter.api.Assertions.*; // static 메서드를 테스트케이스에 많이 사용하여 static import를 사용하여 Assertions.생략 가능
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.GenericXmlApplicationContext;
public class HelloBeansJunitTest {
BeanFactory factory;
@BeforeEach
void init() {
// 1. Spring Bean Container 객체 생성
// classpath에 xml 파일 경로 지정
factory = new GenericXmlApplicationContext("classpath:spring-beans.xml");
}
@Test
void 생성자주입테스트() {
Hello bean = factory.getBean("helloC", Hello.class);
assertEquals("Hello 생성자", bean.sayHello());
bean.print();
}
@Test @Disabled
void hello() {
// 2. Container가 생성한 Bean을 요청하기
Hello hello1 = (Hello) factory.getBean("hello");
Hello hello2 = factory.getBean("hello", Hello.class); // 해당 방법 권장
// 3. HelloBean의 레퍼런스 비교하기
System.out.println(hello1 == hello2); // 싱글톤인지 아닌지 확인 목적
assertSame(hello1, hello2); // 주소가 같으면 테스트 성공으로 표시
assertEquals("Hello 스프링", hello2.sayHello()); // 예상한 값과 일치한지 확인
hello2.print();
Printer printer = factory.getBean("strPrinter", Printer.class);
assertEquals("Hello 스프링", printer.toString());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 어느테이션이 선언된 클래스들을 스캔하기 위한 설정 -->
<!-- context라는 prefix 설정하는 이유
- namespace : 기능별로 xml의 태그명이 똑같더라도 namespace가 다르면 구분 가능
- beans는 default namespace라서 prefix 없이 사용
- context는 :context라는 prefix가 있어서 태그명에 작성해야함-->
<context:component-scan base-package="myspring.di.annot"/>
</beans>
package myspring.di.annot;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// xml파일의 bean 태그와 동일
@Component("helloBean")
public class Hello {
// property 속성의 value와 동일
@Value("어노테이션")
String name;
// preoverty 속성의 ref와 동일
@Autowired
@Qualifier("strPrinter")
Printer printer;
List<String> names;
public Hello() {
System.out.println(this.getClass().getName() + "생성자 호출됨");
}
public Hello(String name, Printer printer) {
System.out.println(this.getClass().getName() + "오버로딩된 생성자 호출됨");
this.name = name;
this.printer = printer;
}
public List<String> getNames() {
return this.names;
}
public void setNames(List<String> list) {
System.out.println("Hello setNames() " + list);
this.names = list;
}
// 어노테이션 사용시 없어도 됨
// public void setName(String name) {
// System.out.println("Hello setName() " + name);
// this.name = name;
// }
//
// public void setPrinter(Printer printer) {
// System.out.println("Hello setPrinter " + printer.getClass().getName());
// this.printer = printer;
// }
public String sayHello() {
return "Hello " + name;
}
public void print() {
this.printer.print(sayHello());
}
}
package myspring.di.annot;
import javax.annotation.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
// file:src/main/resources/spring-beans-annot.xml와 classpath:spring-beans-annot.xml 동일
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-annot.xml")
public class AnnotatedHelloBeanTest {
// Hello가 종류가 1개밖에 없어서 Bean의 id와 일치하지 않아도 상관 없음 (type으로 찾기 때문에)
@Autowired
Hello hello;
@Resource(name="stringPrinter")
Printer printer;
@Test
public void helloBean() {
Assertions.assertEquals("Hello 어노테이션", hello.sayHello());
hello.print();
Assertions.assertEquals("Hello 어노테이션", printer.toString());
}
}
package myspring.di.annot;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("helloCons")
public class HelloCons {
// @Value("어노테이션")
String name;
// @Autowired
// @Qualifier("stringPrinter")
Printer printer;
List<String> names;
public HelloCons() {
System.out.println(this.getClass().getName() + "생성자 호출됨");
}
// 생성자 통해서 injection 받기
// 생성자를 argument에 적용
@Autowired
public HelloCons(@Value("annot생성자") String name, @Qualifier("consolePrinter") Printer printer) {
System.out.println(this.getClass().getName() + "오버로딩된 생성자 호출됨");
this.name = name;
this.printer = printer;
}
public List<String> getNames() {
return this.names;
}
public void setNames(List<String> list) {
System.out.println("Hello setNames() " + list);
this.names = list;
}
// 어노테이션 사용시 없어도 됨
// public void setName(String name) {
// System.out.println("Hello setName() " + name);
// this.name = name;
// }
//
// public void setPrinter(Printer printer) {
// System.out.println("Hello setPrinter " + printer.getClass().getName());
// this.printer = printer;
// }
public String sayHello() {
return "Hello " + name;
}
public void print() {
this.printer.print(sayHello());
}
}
package myspring.di.annot.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//xml을 대신해서 설정 역할을 하는 클래스
@Configuration
@ComponentScan(basePackages = {"myspring.di.annot"})
public class AnnotatedHelloConfig {
}
package myspring.di.xml.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import myspring.di.xml.ConsolePrinter;
import myspring.di.xml.Hello;
import myspring.di.xml.Printer;
import myspring.di.xml.StringPrinter;
@Configuration
public class XmlHelloConfig {
/*
* <bean id="strPrinter" class="myspring.di.xml.StringPrinter" />
*/
// qualifier 하지 않아도 메서드 이름이 bean의 id값
@Bean
public Printer strPrinter() {
return new StringPrinter();
}
@Bean
public Printer conPrinter() {
return new ConsolePrinter();
}
@Bean
public Hello hello() {
Hello hello = new Hello();
hello.setName("Java컨피그");
hello.setPrinter(strPrinter());
return hello;
}
}
package myspring.di.annot.config;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import myspring.di.annot.Hello;
// Bean 컨테이너 종류가 바뀌어서 loader를 통해 가져옴
// AnnotationConfigContextLoader.class는 AnnotationConfigApplicationContext 라는 Spring Bean Container를 로딩해주는 Loader 클래스
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AnnotatedHelloConfig.class, loader = AnnotationConfigContextLoader.class)
public class AnnotatedHelloConfigTest {
@Autowired
Hello hello;
@Test
public void hello() {
System.out.println(hello.sayHello()); // Hello 어노테이션
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Properties file 정보 설정 -->
<context:property-placeholder location="classpath:value.properties"/>
<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
p:driverClassName="${db.driverClass}"
p:jdbcUrl="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
</beans>
db.driverClass=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://127.0.0.1:3306/boot_db?useUnicode=true&charaterEncoding=utf-8&useSSL=false&serverTimezone=UTC
db.username=boot
db.password=boot
myname=Spring
myprinter=printer
value1=JUnit
value2=AOP
value3=DI
printer1=stringPrinter
printer2=consolePrinter
package myspring.user;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-user.xml")
public class UserDBTest {
@Autowired
DataSource dataSource;
@Test
public void conn() {
try {
Connection connection = dataSource.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
System.out.println("DB Product Name : " + metaData.getDatabaseProductName()); //MariaDB
System.out.println("DB Driver : " + metaData.getDriverName()); // MariaDB Connector/J
System.out.println("DB URL : " + metaData.getURL()); // jdbc:mariadb://127.0.0.1/boot_db?user=boot&password=***&...
System.out.println("DB Username : " + metaData.getUserName()); // boot
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Properties file 정보 설정 -->
<context:property-placeholder location="classpath:value.properties"/>
<!-- DataSource 구현체인 HikariDataSource를 SpringBean으로 등록 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
p:driverClassName="${db.driverClass}"
p:jdbcUrl="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
/>
<!-- Mybatis-spring의 SqlSessionFactoryBean을 SpringBean으로 등록 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<property name="mapperLocations">
<list>
<value>classpath:mybatis/*Mapper.xml</value>
</list>
</property>
</bean>
<!-- SqlSessionTemplate -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
</beans>
package myspring.user;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import myspring.user.vo.UserVO;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:spring-beans-user.xml")
public class UserDBTest {
@Autowired
SqlSessionFactory sessionFactory;
@Autowired
SqlSession sqlSession;
@Test
public void session() {
UserVO user = sqlSession.selectOne("userNS.selectUserById", "dooly");
System.out.println(user);
}
@Test
public void sessionFactory() {
System.out.println(sessionFactory.getClass().getName()); // injection 잘 되었는지 확인 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
}
}
appender를 이용해서 console에도 찍을 수 있고 file에 로그 정보 저장 가능
sql 호출 인터페이스
selectOne : <T> T selectOne(String statement(select의 id), Object parameter(값을 파라미터로 넘김))
➡️ 해당 parameter 값이 value로 들어감
userVO 알려주면 getter/setter를 mybatis가 대신 해줌
주의사항
spring-beans-user.xml
// 해당 구문 추가
<!-- Mybatis-Spring의 MapperScannerConfigurer을 SpringBean으로 등록 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 참조하는 것 없어서 bean id 없어도 됨 -->
<property name="basePackage" value="myspring.user.dao.mapper" />
<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
</bean>
package myspring.user.dao.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import myspring.user.vo.UserVO;
public interface UserMapper {
//@Select("select * from users where userid=#{id}")
//UserVO selectUserById(@Param("id") String id);
UserVO selectUserById(String id);
List<UserVO> selectUserList();
void insertUser(UserVO userVO);
void updateUser(UserVO userVO);
void deleteUser(String id);
}
= tomcat 설정 파일
tomcat이 spring web bean container 구동시켜주는 역할
GenericWebApplicationContext가 spring web bean container 역할을 함
web.xml
dispatcherservlet 추가
servlet의 param location
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>CustomerSpringWeb</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-beans-user.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-beans-user.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
JoinPoint 인터페이스
ProceedingJoinPoint 인터페이스
📖 참고 📖
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
⬆️⬇️➡️
📖 참고 📖
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
⬆️⬇️➡️
📋 실행 📋
📖 참고 📖
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
⬆️⬇️➡️
📋 실행 📋
📖 참고 📖
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
⬆️⬇️➡️
📋 실행 📋
📖 참고 📖
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
⬆️⬇️➡️
📋 실행 📋