Spring Framework
- 오늘날 가장 많이 사용하는 java기반 응용 프로그램 개발 프레임워크(효율적인 개발을 위해서 개발방식을 정한것, 우리가 짠소스를 프레임워크안에 집어넣는것)
- 모든 java어플리케이션 개발에 이용할수있으며, java EE 위의 웹어플리케이션 개발에 주로사용.
Spring Framework의 특징
- 경량 컨테이너로써 자바 객체를 직접관리(개발생성 소멸..)
- POJO(Plain Old Java Object) 방식의 프레임워크
- (자바 모델이나, 기능, 프레임워크 등에 따르지 않고 홀로 독립적으며 단순한 기능만을 가진 객체들 = 그냥 자바 bean)
- IOC(Inversion of Control) 지원
- (개발자가 코드의 흐름이나 객체 생성에 관련된 코드를 프로그래밍코드에 직접 작성하는 것이 아닌, 프레임워크가 사용하는 파일에 작성하면 이를 토대로 프레임워크가 객체를 생성하여 반환하고 코드가 동작하는 순서를 결정하게 된다는 의미.
- DI(Dependecy Injection)지원
- AOP(Aspect-Oriented Programming)지원
- ibatis, mybatis, hibernate 등의 데이터베이스 라이브러리를 지원
- java파일에서 java코드를 줄일 수 있다.(xml이나 어노테이션등으로 줄일수있음)
- 반복되는 작업을 줄일 수 있어 기능개발에 집중할 수 있다.
- 프로젝트 관리가 용이하다(xml이나 어노테이션등으로 뽑으면됨)
- 처음 프로젝트 셋팅이 다소복잡하다.
- 스프링 프레임워크 사용은 xml / 어노테이션 방법 2가지로 구분이된다.
자바 프로젝트 → 스프링 프로젝트
pom.xml에서 spring context 추가
ioc 컨테이너의 종류
- 빈팩토리 (클래스를 통해 객체를 생성하고 이를 전달) // 더이상 사용은 안함
- applicationContext (클래스를 통해 객체를 생성하고 이를 전달, 국제화지원 등 문자열 기능, 리스터로 등록되어있는 bean에 이벤트 발생 등..)
Bean객체 생성
- class : 객체를 생성하기위해 사용할 클래스
- id : bean객체를 가져오기위해 사용하는 이름
- lazy-init : 싱글통인 경우 xml을 로딩할때 객체생성 여부를 결정 (true : xml로딩시 객체생성하지않음, 디폴트는 false)
- scope : 객체 범위설정 (singleton : 객체를 하나만 생성해서 사용(디폴트) / prototype : 객체를 가져올때마다 객체 생성)
Bean객체 생명주기
- spring bean은 다음과 같은 상황일 때 객체가 생성된다. (개발자가 객체를 생성하는게 아니라 스프링이 이렇게 생성해줌)
- 싱글톤인 경우 xml파일을 로딩 할 때 객체가 생성된다.
- 싱글톤이고 lazy-init=true일 경우, getBean 메서드를 호출할때 객체가 생성된다.
- scope = prototype 일 경우, getBean 메서드를 호출할때 객체가 생성된다.
- ioc 컨테이너가 종료될때 객체가 소멸된다.
객체 생성과 소멸시 호출될 메서드 등록
- 객체가 생성되면 가장 먼저 생성자가 호출된다.
- init-method : 생성자 호출 이후 자동으로 호출된다. (해당메서드가 없으면 오류)
- default-init-method : init-method를 설정하지 않은 경우 자동으로 호출된다.
- destory-method : 객체가 소멸되기 전에 자동으로 호출된다. (해당메서드가 없으면 오류)
- default-destroy-method : destroy-method를 설정하지 않은 경우 자동으로 호출된다.
<?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:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"
default-init-method="default_init" default-destroy-method="default_destroy">
<!-- bean에 등록해둔init, destroy가 없다면 여기서 찾게됨. 해당 클래스에서 default_init()메소드를 호출, 만약 메소드가 없다면 호출안하기때문에 오류가 안남 -->
<bean id="hello" class='beans.HelloWorldKo'/>
<!-- lazy-init : true - xml로딩할때 객체가 생성되지않고 getBean할때 생성. (디폴트는 false) -->
<bean id="t1" class='beans.TestBean' lazy-init="true"/>
<!-- scope : prototype - xml로딩할때 객체가 생성되지않고 getBean메서드를 호출할때마다 새로운 객체가 생성됨 -->
<bean id="t2" class='beans.TestBean' scope="prototype"/>
<!-- 빈 생성될때 beans.TestBean 클래스에서 bean_init()메소드를 호출(얘네는 없으면 오류) -->
<bean id="t3" class='beans.TestBean' lazy-init="true" init-method="bean_init" destroy-method="bean_destory"/>
</beans>
BeanPostProcessor
- bean객체를 정의할 떄 init-method 속성을 설정하면 객체가 생성될 떄 자동으로 호출될 메서드를 지정할 수 있다.
- 이 때, BeanPostProcessor 인터페이스를 구현한 클래스를 정의하면 Bean객체를 생성할 때 호출될 init 메서드 호출을 가로채 다른 메서드를 호출할 수 있다.
- postProcessBeforeInitialization : init-method에 지정된 메서드가 호출되기 전에 호출
- postProcessAfterInitialization : init-method에 지정된 메서드가 호출되기 후에 호출
- init-method가 지정되어있지 않더라도 자동으로 호출
public class TestBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
if (beanName.equals("t4")) { // 일단 지정해놓으면 모든 빈이 생성될때 다 적용되기 떄문에, beanName으로 특정이름만 먹도록 할 수 있음.
System.out.println("before");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
if (beanName.equals("t4")) {
System.out.println("after");
}
return bean;
}
}
의존성주입
- bean객체를 생성할때 bean객체가 관리할 값이나 객체를 주입하는 것을 의미.
- bean객체 생성 후 bean객체가 가질 기본값을 자바 코드로 설정하는것이 아닌 bean을 정의하는 xml코드에서 정의하는 개념
public class TestBean2 {
private int data1;
private double data2;
private String data3;
private TestBean tb;
public TestBean2() {
System.out.println("TestBean2의 기본생성자");
this.data1 = 0;
this.data2 = 0.0;
this.data3 = null;
this.tb = null;
}
public TestBean2(int data1, double data2, String data3, TestBean tb) {
System.out.println("TestBean2의 매개변수생성자");
this.data1 = data1;
this.data2 = data2;
this.data3 = data3;
this.tb = tb;
}
public void printData() {
System.out.printf("Data1 : %d\\n", data1);
System.out.printf("Data2 : %f\\n", data2);
System.out.printf("Data3 : %s\\n", data3);
}
}
.xml
// 생성자주입
<bean id='obj1' class='beans.TestBean2'>
<constructor-arg value='100' type='int'/> <!-- spring은 int보다 double우선인데, int로 넣고싶을때는 type을 지정 -->
<constructor-arg value='50' /> <!-- 지정해주지않으면 알아서 double로 인식 -->
<constructor-arg value='abcd' />
<!-- <constructor-arg>
<bean class="beans.TestBean" /> // 이렇게 넣을떄 직접 객체를 생성하던가
</constructor-arg> -->
<constructor-arg ref='t4' /> // 아니면 만들어져있는거 넣던가
</bean>
// setter주입
<bean id="t5" class='beans.TestBean2' lazy-init="true">
<property name="data1" value="100" />
<property name="data2" value="100" />
<property name="data3" value="alsie" />
<property name="tb" ref="t2" />
</bean>
컬렉션션 주입
<bean id="listBean" class='beans.ListBean' lazy-init="true">
<property name="list">
<list>
<value>문자열1</value>
<value>문자열2</value>
<value>문자열3</value>
</list>
</property>
<property name="list2">
<list>
<value type='int'>1</value>
<value type='int'>2</value>
<value type='int'>3</value>
</list>
</property>
<property name="list3">
<list>
<bean class='beans.TestBean' lazy-init="true" />
<ref bean='t1'/>
<ref bean='t1'/>
</list>
</property>
<property name="set">
<set>
<value>문자열셋1</value>
<value>문자열셋2</value>
<value>문자열셋3</value>
</set>
</property>
<property name="map">
<map>
<entry key="a1" value='문자열' />
<entry key="a2" value='100' value-type='int'/>
<entry key="a3" value-ref='t1' />
<entry key="a4" >
<list>
<value type='int'>1</value>
<value type='int'>2</value>
<value type='int'>3</value>
</list>
</entry>
</map>
</property>
<property name="property">
<props>
<prop key="p1">문자열1</prop>
<prop key="p2">문자열2</prop>
<prop key="p3">문자열3</prop>
</props>
</property>
</bean>
자동주입
- bean을 정의할 때 주입할 객체는 생성자나 setter를 통한 주입을 한다
- spring에서는 객체를 주입할때 자동으로 주입될 수 있도록 설정할 수 있다.
- 자동주입은 이름, 타입, 생성자를 통할 수 있으며 auto wire라는 용어로 부른다.
- byName : 빈 객체의 프로퍼티 이름과 정의된 빈의 이름이 같은 것을 찾아 자동으로 주입
- byType : 빈 객체의 프로퍼티 타입과 정의된 빈의 타입이 일치할 경우 주입. (단, 들어갈수있는 타입이 2개이상이면 에러)
- constructor : 생성자의 매개변수 타입과 정의된 빈의 타입이 일치할 경우 주입. (단, 들어갈수있는 타입이 2개이상이면 에러)
- 자동주입을 쓸때는, 객체의주소값밖에 입력되지않음. 나머지 기본타입은 직접명시를 해주어야함.
<beans .....
default-autowire="byName" > // autowired가 정의되지않았을때 디폴트로 byName을 함.
<bean id='databean' class='beans.DataBean1' scope='prototype'/>
<bean id="obj1" class='beans.Bean'>
<property name="dataBean1" ref='databean' />
<property name="dataBean2" ref='databean' />
</bean>
<bean id="obj2" class='beans.Bean' autowire="byName" />
<bean id='dataBean1' class='beans.DataBean1' />
<bean id='dataBean2' class='beans.DataBean1' primary="true"/>
<!-- 한가지 이상이므로 에러가 맞지만 primary때문에 ok -->
<bean id="obj3" class='beans.Bean' autowire="byType" />
<bean class='beans.TestBean' />
<bean id="obj4" class='beans.TestBean2' autowire="constructor"> // 생성자도 타입별로찾음
<constructor-arg value='100' type='int'/>
<constructor-arg value='100' type='double'/>
<constructor-arg value='abdc' />
</bean>
<!-- 디폴트 autowired를 받고싶지않을때 no -->
<bean id="obj5" class='beans.Bean' autowire="no" />
Java를 활용한 빈등록
- 지금까지 xml로 빈객체를 생성하여 java코드에서 가져와서 사용.
- 자바 버전이 올라감에 따라서 xml 대신에 java 코드에서 어노테이션을 써서 사용가능.
- 무엇을 써도 동일하게 동작.
- @Configurtion - 현재 자바파일이 빈등록을 위한 자바파일임을 명시 = <beans>
xml파일에 아래 내용추가.
<xmlns:context="<http://www.springframework.org/schema/context>"
xsi:schemaLocation="<http://www.springframework.org/schema/context> <http://www.springframework.org/schema/context/spring-context.xsd>">
</xmlns:context="<
@Bean
- bean객체를 정의 = <bean>
- @Bean(name="이름") : bean의 이름을 새롭게 정의, (따로정의하지않았으면 메서드의 이름이 bean의 이름이 됨)
- @Bean(initMethod="메소드명", destoryMethod="메소드명") : 객체 생성, 소멸 이후 자동호출
- @Lazy : lazy-init 속성을 지정
- @Scope : bean의 scope를 설정
- @Primary : primary 속성 지정
@Configuration
public class BeanConfig {
@Bean(initMethod = "bean_init", destroyMethod = "bean_destory")
@Lazy // xml생성할때 객체가 생성되지않고 최초 getBean할때 생성됨
@Scope("prototype") // getBean될때마다 객체 생성
public TestBean testBean() {
TestBean t1 = new TestBean();
return t1;
}
@Bean(name = "java500")
public TestBean java600() {
TestBean t1 = new TestBean();
return t1;
}
@Bean
@Primary // 같은 클래스타입이 여러개라도 이클래스를 우선적으로 넣음
public DataBean1 dataBean() {
DataBean1 d1 = new DataBean1();
return d1;
}
@Bean
public DataBean1 dataBean1() {
DataBean1 d1 = new DataBean1();
return d1;
}
}
자동주입
- 주입은 자동주입과 직접주입이 있다. 직접주입은 (생성자 주입, setter주입 둘중에 하나로 하는것(일반메서드로 주입하는것도 있는데 잘 안쓰는듯))
- @Bean(autowire=주입방식) : 자동 주입방식을 설정한다.
- Autowire.BY_NAME : 이름을 통한 자동 주입
- Autowire.BY_TYPE : 타입을 통한 자동 주입
스프링 5.1부터는 Deprecated 되었다. 5.1부터는 config파일이 아니라 bean에 직접 설정하는 방식을 추천
어노테이션을 이용한 빈 설정
- spring 2.5부터 xml 방식 외에 어노테이션을 이용한 빈 설정 방법을 제공
- context:annotation-config/ : 빈 설정 파일에 추가하면 빈에 대한 설정을 bean클래스의 어노테이션을 검색해 반영하게 된다. (이걸써야 @Autowired같은게 먹는다)
@Required: 반드시 주입을 해야함 (스프링 5.1부터는 Deprecated 되었다. 반드시 주입을해주려면 생성자를 통해서해야함 / 5.1이상에서는 이제 아무일도 안함)- @Autowired : 객체 타입을 통해 bean 객체를 자동으로 주입 (변수(변수에 넣을시 자동으로 setter메서드가 추가되서 주입받음) / 메서드 / 생성자에 모두 가능)
- @Qualifier : @Autowired로 주입시 같은 타입의 bean이 여러개 정의되어있다면 Qualifier에 설정되어있는 이름이 같은 bean을 찾아 주입
- 생성자주입 : 참조변수 타입변수들은 자동으로 주입되지만, 기본 자료형 및 문자열값은 안되므로 얘네들만 직접주입해주면됨.
@Autowired
@Qualifier("java500")
private TestBean tb; // TestBean타입을 자동으로 주입하는데 동일한 타입이 2개 이상이라면 id가 java500인 객체를 주입
// 기본타입은 자동주입이 당연히 안되므로 직접 넣어줘야함
public TestBean2(@Value("100") int data1, @Value("13.2") double data2, @Value("abcd") String data3, TestBean tb) {
....
}
JSR-250 어노테이션
스프링을 위해서 만들어진 어노테이션은 아니고, 일반적으로 여러 자바프로그램에서 사용한다.
근데 스프링쪽에서도 지원은 하지만 제공은 안해주기때문에 별도로 넣어줘야함. 그럼 유용한 어노테이션을 사용할 수 있다.
- 스프링에서 기본으로 제공되지는 않지만 자바 플랫폼 공통 어노테이션인 JSR-250을 적용할 수 있다.
- 적용을 위해서는 반드시 라이브러리 추가해야함 (지금은 javax.annotation-api로 변경됨)
- @postConstruct : 생성자 호출 후 지동으로 호출될 함수를 등록 (기존의 init, destoryMethod메서드를 대체)
- @preDestory : - 생성자 호출 후 지동으로 호출될 함수를 등록 (기존의 init, destoryMethod메서드를 대체)
- @Resource : autowired + qualified 두개를 합친것이라고 생각하면됨. 변수의 이름과 동일한 이름의 빈 주입
@Bean(initMethod = "bean_init", destroyMethod = "bean_destory") // **이런식의 설정을 config파일에다가 하는게 아니라 클래스에 autowired하듯이 클래스에 직접하자는 뜻!**
public TestBean testBean() {
TestBean t1 = new TestBean();
return t1;
}
public class TestBean {
@PostConstruct // 생성자 호출 이후 자동으로 호출
public void bean_init() {
System.out.println("TestBean init 메서드");
}
@PreDestroy // 객체가 소멸되기 전에 자동으로 호출
public void bean_destory() {
System.out.println("TestBean destory 메서드");
}
}
@Resource(name = "java500") // 주입되는 빈의 이름도 tb면 name은 쓸필요가없음 java500이라는 이름을 tb에 주입하기때문에 name으로 명시해줌
private TestBean tb;
Component
- @Component : bean configuration파일(or xml파일)에 bean을 등록하지 않아도 자동으로 등록된다.
- xml을 이용하는 방식 : <context:component-scan base-package="...." />
- java config를 이용하는 방식 : @ComponentScan(basePackages = {"...", "...."})
- @Lazy : 실제로 getBean할때 객체 생성
- @Scope("prototype") : getBean 호출할때마다 새로운 객체를 생성 (싱글톤이 아님)
@Component == <bean class="beans.TestBean" />
@Component("java500") == <bean id='java500' class="beans.TestBean" /> (사실상 의미는 없음)
@Lazy == <... lazy-init="true"/>
@Scope("prototype")
<bean id='id1' class="beans.TestBean" />
<bean id='id2' class="beans.TestBean" />
public TestBean id1() {
TestBean t1 = new TestBean();
return t1;
}
public TestBean id2() {
TestBean t1 = new TestBean();
return t1;
}
// xml, javaConfig파일은 클래스마다 다른이름을 집어넣을 수 있지만, **@Component방식은 클래스마다 한개의 이름밖에 지정할 수 없는 단점이 있음. 하고싶다면 xml, java에다가 직접등록해주어야함**
@Component로 등록된 클래스에서 다른 타입을 주입시킬때는 주입받는 타입도 @Component로 선언되어있어야한다. 그렇기때문에 주입하고자 하는 클래스가 같은데 이름이 다를경우에는 xml로 빼야한다.
@Component("이름")으로 해당클래스의 id를 정할수있는데, Component는 1개 클래스의 타입은 1개의 id를 가지므로 사실상 의미는 별로없음.
- 객체 생성시 반드시 넣어줘야하는 값들은 생성자를 통해서 해결하면된다. (이전에는 @Required를 썼는데 쓸필요가 없어짐)
// 생성자를 통하면 반드시 주입을 받아야하기 때문에, @required를 쓸필요가 없다
// **컴포넌트되어있는 클래스는 알아서 주입시켜주기때문에 @Autowired가 필요없음**
public Bean2(@Value("100") int data1, @Value("abcd") String data2,
TestBean testBean, TestBean2 testBean2) {
this.data1 = data1;
this.data2 = data2;
this.testBean = testBean;
this.testBean2 = testBean2;
}
AOP
Aspect Oriented Programming의 약자로 기존 비즈니스 로직외 작성해야하는 코드를 별도로 분리함으로써 개발자가 좀 더 비즈니스 로직에만 집중할수있게 하는 방법
Aspect는 사전적의미로 측면을 의미하고 실제 프로그램 개발에서느 비즈니스 로직은 아니지만, 반드시 하긴해야하는 작업.. (보안, 로깅, 트랜잭션) 등을 의미. 개발에서는 이런 행위를 '횡단 관심사'라고 표현. 대부분의 개발과정에서 공통적으로 들어가있는것.
기존 코드를 건들지않고 곁다리를 넣었다 뺐다 할수있는 이유는 실제 작성한 코드와 런타임이 동작하는 코드가 다르다는 부분에 있다.
외부에서 특정한 객체를 호출하면 - 실제 객체를 감싸고있는 Proxy객체가 대신 나오기때문에 외부에서는 실제 객체처럼 쓸수있다. 프록시객체가 곁다리를 추가해주는것.
advice : 실제로 기능을 구현한 객체(여기서 로깅 등을 실제로 구현한다는뜻)
join points : 공통 관심사를 적용할 수 있는 대상. spring aop에서는 각 객체의 메소드가 해당
pointcuts : 여러 join points중에 실제 advice가 적용될 대상 메서드
aspect : 공통 관심사에 대한 추상적인 명칭. 로깅, 보안, 트랜잭션 같은 기능 자체에 대한 용어 (point cut + advice)
target : 대상 메소드를 가지는 객체(진짜)
proxy : advice가 적용되었을때 만들어지는 객체
weaving : advice와 target이 결합되서 프록시 객체를 만드는 과정 (advice를 핵심로직코드에 적용하는것)
advice의 종류
Before Advice : target 메소드 호출 전에 적용
After returning : target 메소드 호출 이후에 적용 (예외 없이)
After throwing : target 예외 발생 후 적용
After : target메소드 호출 후 예외의 발생에 관계 없이 적용
Around : target 메소드 호출 이전 이후에 모두 적용 (가장 광범위하게 사용됨)
- Spring AOP 구현을 위해서는 XML을 이용하는 방법 / @AspectJ 를 이용하는 방법 2가지가 있다.
aop를 활용하려면 관련 라이브러리 필요하고, 트랜잭션 처리를 위해서 spring-tx 라이브러리가 필요.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AOP기능을 적용하기 위해 AspectJ언어의 문법을 이용하는 라이브러리 설정.-->
<!-- <dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency> -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
aop를 적용하는 방식은
1.직접 만들던가(스프링1.x버전),
2.xml적용하던가 (xml에 <aop:config></aop:config>추가)
xml파일에 아래 내용추가.
<xmlns:context="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id='advisor1' class="advisor.AdvisorClass" lazy-init="true"/>
<aop:config>
<aop:aspect ref='advisor1'>
<!-- 모든패키지의 모든클래스에 있는 aopTest()메소드를 호출하는 행위 -->
<aop:pointcut id="point1" expression="execution(* aopTest())" />
<!-- 위의 메서드가 호출되기전에 advisor1가 가지고있는 beforeMethod메서드를 먼저 호출해라 -->
<aop:before method="beforeMethod" pointcut-ref="point1" />
<aop:after method="afterMethod" pointcut-ref="point1" />
<aop:around method="aroundMethod" pointcut-ref="point1" />
<aop:after-returning method="afterReturningMethod" pointcut-ref="point1" />
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="point1" throwing="e1"/>
</aop:aspect>
</aop:config>
public class AdvisorClass {
public void beforeMethod() {
System.out.println("aop_before 메서드 호출");
}
public void afterMethod() {
System.out.println("aop_after 메서드 호출");
}
// around는 관심사 호출 전후로 호출하기떄문에 중간에 관심사메서드를 호출해야할 일이 있다.
// 그때를 위해서 pjp를 만들어둔것.
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("aop_around 메서드 호출");
// 원래 메서드 호출
// 어떤값이 리턴될지 모르므로 object타입이여야만함.
Object obj = pjp.proceed();
// 반환값이 있을수도 있으므로..
return obj;
}
// 오류없이 정상적으로 끝났을때만
public void afterReturningMethod() {
System.out.println("aop_afterReturn 메서드 호출");
}
public void afterThrowingMethod(Throwable e1) {
System.out.println("aop_afterThrow 메서드 호출");
System.out.println(e1);
}
}
Execution 명시자
- pointcut을 지정할때 사용하는 문법
- execution(접근제한자 리턴타입 클래스이름 메서드이름(매개변수))
- 접근제한자 : public만 지원
- 리턴타입 : 메서드의 매개변수 타입
- 클래스 이름 : 패키지를 포함한 클래스 이름
- 매개변수 : 매개변수의 형태
- * : 모든것을 의미
- .. : 개수 상관없이 모든것을 의미
-
<!-- 모든패키지의 모든클래스에 있는 aopTest()메소드를 호출하는 행위 --> <aop:pointcut id="point1" expression="execution(* aopTest())" /> <!-- 반환타입이 void, beans.DataBean1클래스, aopTest메서드, 매개변수 없음 --> <aop:pointcut id="point1" expression="execution(void beans.DataBean1.aopTest())" /> <!-- 반환타입이 void, beans.DataBean1클래스, aopTest메서드, String매개변수 --> <aop:pointcut id="point1" expression="execution(void beans.DataBean1.aopTest(java.lang.String))" /> <!-- 반환타입이 void, beans.DataBean1클래스, aopTest메서드, String, 모든타입매개변수2개 --> <aop:pointcut id="point1" expression="execution(void beans.DataBean1.aopTest(String, *, *))" /> <!-- 반환타입이 void, beans.DataBean1클래스, aopTest메서드, 모든매개변수(개수포함) --> <aop:pointcut id="point1" expression="execution(void beans.DataBean1.aopTest(..))" /> <!-- 반환타입이 void, beans.DataBean1클래스, 모든메서드, 모든매개변수(개수포함) --> <aop:pointcut id="point1" expression="execution(void beans.DataBean1.*(..))" /> <!-- 반환타입이 void, beans안의 모든클래스, aopTest메서드, 모든매개변수(개수포함) --> <aop:pointcut id="point1" expression="execution(void beans.*.method1(..))" /> <!-- 반환타입이 void, 모든패지지, 모든매서드, 모든매개변수(개수포함) --> <aop:pointcut id="point1" expression="execution(void *.*(..))" />
3.어노테이션 하던가. 어노테이션이 많이쓰임 (@AspectJ)
위에서 배운거처럼 어차피 빈으로 써야할 클래스를 생성해야하니까 xml, config파일이 아닌 클래스에 직접 @Autowired 하자는 것과 비슷하게
어차피 advisor클래스를 만들어야하니까 xml, config에다가 설정하지말고 advisor클래스에다가 어노테이션을 붙이자는게 포인트임
- xml : aop:aspectj-autoproxy advisor클래스에 세팅되어있는 어노테이션을 보고 aspect를 세팅해라라는 의미
- javaConfig : @EnableAspectJAutoProxy : advisor클래스에 세팅되어있는 어노테이션을 보고 aspect를 세팅해라라는 의미
applicationContext.xml에 namespaces - aop, tx체크 및 아래 코드 추가
<!-- 자동으로 AspectJ라이브러리를 이용하여 Proxy객체를 생성함 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- @Before : 관심사 동작 이전 호출
- @After : 관심사 동작 이후 호출
- @Around : 관심사 동작 이전 이후 호출
- @BeforeThrowing : 예외없이 정상종료일때 호출
- @AfterThrowing : 예외 발생하여 종료일때 호출
@Before("execution(* org.joywins.service.MsgService*.*(..))")
public void aop1{}
← target 메서드를 실행하기전 aop1메서드를 먼저 실행하겠다는 뜻인데(before니까), execution은 pointCut을 지정하는 문법으로 org.joywins.service.MsgService로 시작하는 모든 클래스의 메소드를 나타낸다.
aop가 잘 적용됐으면, target메서드에 화살표가 있어야한다.
전달되는 파라미터를 확인하기위해 org.aspectj.lang.JoinPoint클래스를 활용
@Around("execution(* org.joywins.service.MsgService*.*(..))") // <- 타겟메서드 전체를 앞뒤로 감쌈. org.joywins.service.MsgService로 시작하는 모든 클래스가 target이다.
public Object timeLog(ProceedingJoinPoint pjp) throws Throwable { // <- 일반적인 Exception이 아니라 상위타입인 Throwable이 사용됨. 반드시 리턴타입 Object선언
logger.info("S.timeLog.=============================================");
long startTime = System.currentTimeMillis();
logger.info(Arrays.toString(pjp.getArgs()));
Object result = pjp.proceed(); // ...실제 메서드를 호출함.
long endTime = System.currentTimeMillis();
logger.info(pjp.getSignature().getName() + " : " + (endTime - startTime));
logger.info("E.timeLog.=============================================");
return result;
}
@Around는 메서드 실행에 직접 관여함.
- ...Around타입의 기능은 파라미터로 ProceedingJoinPoint 타입을 사용함.
- ...ProceedingJoinPoint는 JoinPoint의 모든 메서드를 가지면서도, 직접 target객체의 메서드를 실행할 수 있는 기능이 추가됨. (JoinPoint를 상속받으니까)
- ...ProceedingJoinPoint.proceed()는 특이하게도 Exception보다 상위의 Throwable을 처리.
- ...@Around를 이용하는 경우 반드시 메서드의 리턴타입은 Object로 선언해야 함.
- ...@Around는 메서드를 직접 호출하고, 결과를 반환해야만 정상적인 처리가 됨.
트랜잭션
스프링의 트랜잭션은 별다른 이유가없다면 aop설정을 응용하기때문에 필수적으로 알아두어야한다.
스프링에서 트랜잭션을 처리하는 방법은
- xml 설정 (별도의 transaction-context.xml을 이용하는 방식)
- @Transactional
트랜잭션을 처리하기위해서 기본적으로 트랜잭션을 처리하는 트랜잭션 매니저를 설정해주어야 한다.
<!-- DataSource설정이 존재하는곳에 트랜잭션 적용하는게 가장 간편. 하나의 DataSource를 사용하는경우, 스프링에서 제공하는 DataSourceTransactionManager를 이용하면됨.-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven /> <!-- @Transactional가 먹도록 해줌 -->
@Transactional 메소드 > 클래스 > 인터페이스 순으로 우선순위를 가진다.
어노테이션을 적용하면 connection을 열고 한번에 쿼리문을 실행하고 close한다 / 적용하지않았을때 sql문 마다 각각 connection을 엶.
스프링 시작 2가지방법
- 기존 스프링 : spring Legacy project
- 스프링부트 : spring Starter project (was내장)
스프링은 기본적으로 maven을 내장 관련라이브러리 알아서 다 받음
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/config/log4j.xml</param-value> </context-param>
이런파일을 못읽었다면,
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
를 넣어줘서 파일을 읽을수있게한다.
lombok.config 파일도 따로 만들어서 넣어줘야함
build path :
Libraries : 관련 라이브러리를 물수있도록 추가, lombok, serverl-api는 따로 넣어줘야할것.
Source : 빌드했을때, 포함시킬파일들. included가 **/*.java면 빌드시 java파일밖에 없으니 주의
Depolyment Assembly :
export나 빌드했을때, 관련 파일들이 어디에 위치할지 설정.
/lib : WEB/INF/lib 면 빌드전 /lib 파일이 빌드후 /WEB/INF/lib에 들어가게된다.
Project Natures :
이 프로젝트가 메이븐프로젝트인지 이런거 결정하는거같음..
이클립스폴더를 통째로 압축하면 제티등의 플러그인도 쓸수있게된다.
'IT > 스프링' 카테고리의 다른 글
mvc (0) | 2023.03.26 |
---|---|
Spring boot (0) | 2023.03.26 |
Spring (0) | 2023.03.26 |
JDBC & Mybatis 설정법 (0) | 2022.01.24 |
Spring boot를 이용해서 자바프로그램을 짜보자 (0) | 2022.01.24 |
댓글