강의 8 - 강원대학교

Download Report

Transcript 강의 8 - 강원대학교

1
고급자바프로그래밍
(ADVANCED JAVA PROGRAMMING)
강원대학교 컴퓨터학부
2012년 가을학기
담당교수 정충교
1/43
2
6장 AOP 1
2/43
3
6.1 트랙스액션 코드의 분리
interface UserService {
void add(User user);
void upgradeLevels();
}
3/43
4
UserServiceTx
public void upgradeLevels() {
TransactionStatus status = this.transactionManager
.getTransaction(new
DefaultTransactionDefinition());
try {
userService.upgradeLevels();
this.transactionManager.commit(status);
} catch (RuntimeException e) {
this.transactionManager.rollback(status);
throw e;
}
}
4/43
5
UserService
public void upgradeLevels() {
List<User> users = userDao.getAll();
for (User user : users) {
if (canUpgradeLevel(user)) {
upgradeLevel(user);
}
}
}
5/43
6
<bean id="userService" class="springbook.user.service.UserServiceTx">
<property name="transactionManager" ref="transactionManager" />
<property name="userService" ref="userServiceImpl" />
</bean>
<bean id="userServiceImpl" class="springbook.user.service.UserServiceImpl">
<property name="userDao" ref="userDao" />
<property name="mailSender" ref="mailSender" />
</bean>
6/43
7
고립된 단위 테스트
7/43
8
static class MockUserDao implements UserDao {
private List<User> users;
private List<User> updated = new ArrayList();
private MockUserDao(List<User> users) {
this.users = users;
}
public List<User> getUpdated() {
return this.updated;
}
public List<User> getAll() {
return this.users;
}
public void update(User user) {
updated.add(user);
}
public void add(User user) { throw new UnsupportedOperationException(); }
public void deleteAll() { throw new UnsupportedOperationException(); }
public User get(String id) { throw new UnsupportedOperationException(); }
public int getCount() { throw new UnsupportedOperationException(); }
}
8/43
9
다이내믹 프록시와 팩토리빈
(Dynamic Proxy, Factory Bean)
9/43
10
전략패턴
10/43
11
부가기능과 핵심기능의 분리
11/43
12
핵심기능 인터페이스 적용
12/43
13
프록시와 타겟
타겟의 대리인 역할
13/43
14
데코레이션 패턴
런타임에 동적으로 부가기능 삽입
14/43
15
프록시 패턴
프록시가 타겟에 대한 접근 방법을 제어할 때
• 타겟 오브젝트 생성 시점 지연
• 원격 오브젝트 이용
• 타겟에 대한 접근 권한 제한
15/43
16
간단한 프록시 예
interface Hello {
static class HelloTarget implements Hello {
String sayHello(String name);
public String sayHello(String name) {
String sayHi(String name);
return "Hello " + name;
String sayThankYou(String name);
}
}
public String sayHi(String name) {
return "Hi " + name;
}
public String sayThankYou(String name) {
return "Thank You " + name;
}
}
16/43
17
interface Hello {
class HelloUppercase implements Hello {
String sayHello(String name);
Hello hello;
String sayHi(String name);
public HelloUppercase(Hello hello) {
String sayThankYou(String name);
this.hello = hello;
}
}
public String sayHello(String name) {
return hello.sayHello(name).toUpperCase();
}
public String sayHi(String name) {
return hello.sayHi(name).toUpperCase();
}
public String sayThankYou(String name) {
return hello.sayThankYou(name).toUpperCase();
}
}
17/43
18
interface Hello {
String sayHello(String name);
String sayHi(String name);
String sayThankYou(String name);
}
@Test
public void simpleProxy() {
Hello hello = new HelloTarget();
assertThat(hello.sayHello("Toby"), is("Hello Toby"));
assertThat(hello.sayHi("Toby"), is("Hi Toby"));
assertThat(hello.sayThankYou("Toby"), is("Thank You Toby"));
Hello proxiedHello = new HelloUppercase(new HelloTarget());
assertThat(proxiedHello.sayHello("Toby"), is("HELLO TOBY"));
assertThat(proxiedHello.sayHi("Toby"), is("HI TOBY"));
assertThat(proxiedHello.sayThankYou("Toby"), is("THANK YOU TOBY"));
}
18/43
19
class HelloUppercase implements Hello {
Hello hello;
인터페이스의 모든 메소드를 구현해야 함
public HelloUppercase(Hello hello) {
똑같은 부가 기능이 모든 메소드에 중복됨
this.hello = hello;
}
public String sayHello(String name) {
return hello.sayHello(name).toUpperCase();
}
public String sayHi(String name) {
return hello.sayHi(name).toUpperCase();
}
public String sayThankYou(String name) {
return hello.sayThankYou(name).toUpperCase();
}
}
19/43
20
리플렉션 (Reflection)
• 자바의 모든 클래스는 자체 구성 정보를 담은 Class 타입
오브젝트를 하나씩 가지고 있다.
• 클래스이름.class
• getClass() – 클래스의 오브젝트를 가지고 있는 경우
• 클래스 이름, 슈퍼클래스, 인터페이스, 필드 이름과 타입,
메소드 이름과 파라미터와 리턴타입, 오브젝트의 필드 값,
메소드 호출
20/43
21
public class Reflection {
@Test
public void invokeMethod() throws Exception {
String name = "Spring";
// length
assertThat(name.length(), is(6));
Method lengthMethod = String.class.getMethod("length");
assertThat((Integer)lengthMethod.invoke(name), is(6));
// toUpperCase
assertThat(name.charAt(0), is('S'));
Method charAtMethod = String.class.getMethod("charAt",
int.class);
assertThat((Character)charAtMethod.invoke(name, 0), is('S'));
}
@Test
public void createObject() throws Exception {
Date now = (Date) Class.forName("java.util.Date").newInstance();
}
}
21/43
22
리플렉션을 이용한 다이내믹 프록시
• 다이내믹 프록시는 런타임에 프록시팩토리에 의해 생성
• 다이내믹 프록시 오브젝트는 타킷의 인터페이스 타입
• 클라이언트는 타겟 인터페이스를 통해 다이내믹프록시 사용
22/43
23
리플렉션을 이용한 다이내믹 프록시
23/43
24
class UppercaseHandler implements InvocationHandler {
Object target;
private UppercaseHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
Object ret = method.invoke(target, args);
if (ret instanceof String && method.getName().startsWith("say")) {
return ((String)ret).toUpperCase();
}
else {
return ret;
}
}
}
24/43
25
class UppercaseHandler implements InvocationHandler {
Object target;
private UppercaseHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object ret = method.invoke(target, args);
if (ret instanceof String && method.getName().startsWith("say")) {
return ((String)ret).toUpperCase();
}
else {
return ret;
}
}
}
Hello proxiedHello = (Hello)Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] { Hello.class},
new UppercaseHandler(new HelloTarget()));
25/43
26
class UppercaseHandler implements InvocationHandler {
Object target;
private UppercaseHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = method.invoke(target, args);
if (ret instanceof String && method.getName().startsWith("say")) {
return ((String)ret).toUpperCase();
}
else {
return ret;
}
}
}
Hello proxiedHello = (Hello)Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] { Hello.class},
new UppercaseHandler(new HelloTarget()));
26/43
27
다이내믹 프록시를 이용한 트랜랙색션 부가기능
인터페이스의 모든 메소드를 구현해야 함
똑같은 부가 기능이 모든 메소드에 중복됨
27/43
28
public class TransactionHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().startsWith(pattern)) {
return invokeInTransaction(method, args);
} else {
return method.invoke(target, args);
}
}
private Object invokeInTransaction(Method method, Object[] args) throws Throwable {
TransactionStatus status = this.transactionManager.
getTransaction(new faultTransactionDefinition());
try {
Object ret = method.invoke(target, args);
this.transactionManager.commit(status);
return ret;
} catch (InvocationTargetException e) {
this.transactionManager.rollback(status);
throw e.getTargetException();
}
}
}
28/43
29
@Test
public void upgradeAllOrNothing() throws Exception {
TransactionHandler txHandler = new TransactionHandler();
txHandler.setTarget(testUserService);
txHandler.setPattern("upgradeLevels");
UserService txUserService = (UserService)Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] {UserService.class},
txHandler);
…
}
29/43
30
다이내믹 프록시를 위한 팩토리빈
• 팩토리빈을 사용하면 다이내믹 프록시를 스프링빈으로 등
록할 수 있다.
org.springframework.beans.factory ;
Interface FactoryBean<T> {
T getObject() ;
Class<?> getObjectType() ;
boolean isSingleton() ;
}
• FactoryBean<T>를 스프링빈으로 등록해 놓으면 이것이 T
타입 오브젝트를 만들어 역시 스프링빈을 등록해 준다.
30/43
31
BeanFactory 사용 예
package springbook.learningtest.spring.factorybean;
public class Message {
String text;
private Message(String text) {
this.text = text;
}
public String getText() {
return text;
}
public static Message newMessage(String text) {
return new Message(text);
}
}
31/43
32
public class MessageFactoryBean implements FactoryBean<Message> {
String text;
public void setText(String text) {
this.text = text;
}
public Message getObject() throws Exception {
return Message.newMessage(this.text);
}
public Class<? extends Message> getObjectType() {
return Message.class;
}
public boolean isSingleton() {
return false;
}
}
32/43
33
<bean id="message" class="springbook.learningtest.spring.factorybean.MessageFactoryBean">
<property name="text" value="Factory Bean" />
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class FactoryBeanTest {
@Autowired
ApplicationContext context;
@Test
public void getMessageFromFactoryBean() {
Object message = context.getBean("message");
assertThat(message, is(Message.class));
assertThat(((Message)message).getText(), is("Factory Bean"));
}
@Test
public void getFactoryBean() throws Exception {
Object factory = context.getBean("&message");
assertThat(factory, is(MessageFactoryBean.class));
}
}
33/43
34
팩토리빈을 이용하여 트랜잭션 다이내믹 프록시를 스프링빈으
로 등록
왜 다이나믹 프록시를 스프링빈으로 만들려 하나?
34/43
35
public class TxProxyFactoryBean implements FactoryBean<Object> {
…
public Object getObject() throws Exception {
TransactionHandler txHandler = new TransactionHandler();
txHandler.setTarget(target);
txHandler.setTransactionManager(transactionManager);
txHandler.setPattern(pattern);
return Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] { serviceInterface },
txHandler);
}
…
}
35/43
36
<bean id="userService" class="springbook.user.service.TxProxyFactoryBean">
<property name="target" ref="userServiceImpl" />
<property name="transactionManager" ref="transactionManager" />
<property name="pattern" value="upgradeLevels" />
<property name="serviceInterface"value="springbook.user.service.UserService" />
</bean>
36/43
37
• 다이나믹 프록시를 사용하면 프록시 패턴 혹은 데커레이션
패턴이 갖는 아래 두 가지 문제 해결
인터페이스의 모든 메소드를 구현해야 함
똑같은 부가 기능이 모든 메소드에 중복됨
• 팩토리빈을 사용하면 다이나믹 프록시를 스프링빈으로 등
록시키고 다른 오브젝트에 DI할 수 있다.
37/43
38
한번 만든 TxProxyFactoryBean은 다른 서비스
에도 그대로 적용할 수 있다.
리스트 6-38, 6-39, 6-40
coreService라는 아이디를 가진 빈을 DI 받아 사용하는 클라이언트는 코드 변
경 없이도 프록시가 제공하는 트랜잭션 기능이 적용된 coreService를 이용할
수 있다.
38/43
39
팩토리빈의 한계
• 여러 개의 클래스에 공통의 부가기능 지원하기 위해서는
여러 개의 팩토리빈을 설정해야 함
• 하나의 타깃에 여러 가지 부가기능을 지원하기 위해서는
여러 개의 팩토리빈을 설정해야 함
39/43
40
6.4 스프링의 팩토리빈 (ProxyFactoryBean)
JDK 다이나믹 프록시
FactoryBean<T>
스프링 다이나믹 프록시
타깃과 연결되어
있지 않음
40/43
41
어드바이저 = 포인트컷 (메소드 선정) + 어드바이스(부가기능)
스프링 다이나믹 프록시
41/43
42
Advice와 pointcut은 공용
42/43
43
끝
43/43