Spring reference I. Giới thiệu



tải về 261.07 Kb.
trang2/3
Chuyển đổi dữ liệu02.09.2016
Kích261.07 Kb.
#30866
1   2   3

b) Tạo After Advice:
Advice này được thực thi sau khi method invocation ở joinpoint thực thi và trả về giá trị. Do đó bạn không thể thay đổi các tham số được chuyển đến nó. Mặc dù bạn có thể đọc nó, nhưng bạn không thể thay đổi đường dẫn, hoặc ngăn chặn phương thức thực thi. Mặc dù bạn không có thể sửa đổi giá trị trả về trong after advice, nhưng bạn có thể ném vào một exception thay vì trả về giá trị.
Chúng ta sẽ xem một vài ví dụ sử dụng after advice sau. Ví dụ đầu tiên đơn giản in ra message - tên method invocation, sau khi gọi nó.
import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.aop.framework.ProxyFactory;
public class SimpleAfterAdvice implements AfterReturningAdvice {
public static void main(String[] args) {

ProxyFactory pf = new ProxyFactory();

pf.setTarget(new MessageWriter());

pf.addAdvice(new SimpleAfterAdvice());

MessageWriter proxy = (MessageWriter) pf.getProxy();

proxy.writeMessage();

}

public void afterReturning(Object returnValue, Method method,



Object[] args, Object target) throws Throwable {

System.out.println("");

System.out.println("After method: " + method.getName());

}
}
Ví dụ này thì không khác lớp SimpleBeforeAdvice mấy. Nhưng có một số ghi chú sau: phương thức afterReturning() của giao tiếp AfterReturningAdvice được chuyển vào các tham số gồm: giá trị trả về của method invocation, một tham chiếu đến method invocation, các tham số được chuyển cho phương thức và target của invocation. Chạy ví dụ này sẽ kết xuất như sau:


World

After method: writeMessage

2007/01/17 17:55:06 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/17 17:55:06 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available
Một cách sử dụng tốt của advice return là thực hiện một vài kiểm tra lỗi khi có thể giá trị của phương thức trả về không hợp lệ.
c) Tạo around advice:
Chức năng của around advice là sự kết hợp của before advice và after advice, cộng thêm một sự khác biệt là bạn có thể thay đổi giá trị trả về.
Không chỉ vậy, bạn còn có thể ngăn chặn phương thức thực thi. Điều này có nghĩa rằng, bạn có thể thay thế toàn bộ cài đặt của một phương thức với code mới. Around advice trong Spring được mô hình theo interceptor bằng giao tiếp MethodInterceptor.
Sau đây chúng ta sẽ sử dụng method interceptor để in ra thông tin liên quan method invocation. Cần chú ý phương thức invoke() của lớp MethodInterceptor không cung cấp cùng tập hợp các tham số như MethodBeforeAdviec và AfterReturningAdvice, phương thức này không được chuyển đến target của invocation, method invocation hoặc các tham số được sử dụng. Tuy nhiên, bạn có thể truy cập dữ liệu này bằng đối tượng MethodInvocatioin được chuyển cho phương thức invoke().
Ví dụ này chúng ta thấy cách advise một lớp để lấy thông tin về tốc độ runtime của các phương thức của nó. Đặc biệt, chúng ta muốn biết thời gian thực thi phương thức. Để làm được điều này, chúng ta có thể sử dụng lớp StopWatch trong Spring, và chúng ta cần MethodInterceptor, bởi vì chúng ta cần start StopWatch trước method invocation và stop nó sau đó.
Lớp WorkerBean sau đây là lớp mà chúng ta dự định sẽ mô tả sơ lược cách sử dụng lớp StopWatch và around advice:
public class WorkerBean {
public void doSomeWork(int noOfTimes) {

for (int i = 0; i < noOfTimes; i++) {

work();

}

}



private void work(){

System.out.print("");

}

}
Đây là một lớp đơn giản. Phương thức doSomeWork() chấp nhận một tham số - noOfTimes, và gọi phương thức work() một noOfTimes lần. Phương thức work chỉ đơn giản gọi sysout và chuyển vào một chuỗi rỗng.


Lớp ProfilingInterceptor sử dụng lớp StopWatch để lấy thời gian thực thi của method invocation.
import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.util.StopWatch;


public class ProfilingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {

StopWatch sw = new StopWatch();

sw.start(invocation.getMethod().getName());

Object returnValue = invocation.proceed();

sw.stop();
Object target = invocation.getThis();

Method method = invocation.getMethod();

System.out.println("Executed method: " + method.getName());

System.out.println("On object of type: " + target.getClass().getName());

System.out.println("Took: " + sw.getTotalTimeMillis() + " ms");

return returnValue;

}

}
Trong phương thức invoke(), chúng ta sẽ tạo một thể hiện của StopWatch và sau đó start ngay lập tức, cho phép method invocation thực thi với một lời gọi là MethodInvocation.proceed(). Ngay khi method invocation thực thi hoàn tất và trả về giá trị, chúng ta sẽ stop StopWatch và in ra số milisecond nhận được.


Cuối cùng chúng ta trả về Object được trả về bởi MethodInvocation.proceed() để người gọi nắm lấy giá trị trả về đúng.
Bây giờ chúng ta viết một lớp ProfilingExample advise một thể hiện WorkerBean với ProfilingInterceptor và sau đó mô tả sơ lược về phương thức doSomeWork():
import org.springframework.aop.framework.ProxyFactory;
public class ProfilingExample {

public static void main(String[] args) {

ProxyFactory pf = new ProxyFactory();

pf.setTarget(new WorkerBean());

pf.addAdvice(new ProfilingInterceptor());

WorkerBean proxy = (WorkerBean) pf.getProxy();

proxy.doSomeWork(100000);

}

}


Chạy ví dụ này sẽ kết xuất như sau:

2007/01/18 12:48:34 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/18 12:48:34 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

Executed method: doSomeWork

On object of type: WorkerBean

Took: 16 ms

d) Advisor và Pointcut trong Spring:
Tất cả các ví dụ mà bạn thấy đều sử dụng ProxyFactory.addAdvice() để cấu hình advice cho proxy. Phương thức này sẽ ủy thác cho addAdvisor()ở hậu trường, tạo một thể hiện của DefaultPointcutAdvisor và cấu hình nó với pointcut trỏ đến tất cả các phương thức. Theo cách này, advice sẽ nhận ra để ghép vào tất cả các phương thức trên đối tượng target.
Bạn có thể đơn giản đăng ký advice cho một phương thức, nhưng phương pháp này có một vài hạn chế. Đầu tiên là code cứng danh sách các phương thức vào trong advice sẽ làm giảm khả năng tái sử dụng của advice. Sử dụng pointcut, bạn có thể cấu hình các phương thức để advice gắn vào mà không cần cài đặt code bên trong advice => tăng giá trị tái sử dụng của advice. Mặt hạn chế thứ 2 và thứ 3 với việc code cứng danh sách các phương thức vào trong advice thì liên quan đến tốc độ thực thi. Để ngăn chặn phương thức được advise trong advice, bạn cần thực hiện việc ngăn chặn mỗi khi bất kỳ phương thức nào trên target được triệu gọi. Điều này ảnh hưởng đến tốc độ thực thi của ứng dụng. Khi bạn sử dụng pointcut, việc ngăn chặn được thực hiện một lần cho mỗi phương thức và các kết quả được lưu cho lần sử dụng sau.
Do đó chúng ta sẽ cần tránh code cứng phương thức đăng ký vào advice và sử dụng pointcut thay thế mỗi khi thêm advice vào phương thức trên đối tượng target.
Pointcut trong Spring được tạo bằng cách cài đặt giao tiếp Pointcut:
public interface Pointcut {

ClassFilter getClassFilter ();

MethodMatcher getMethodMatcher();

}
Giao diện Pointcut định nghĩa hai phương thức, getClassFilter() và getMethodMatcher() trả về thể hiện của ClassFilter và MethodMatcher. Khi tạo pointcut riêng, bạn phải cài đặt cả hai giao tiếp ClassFilter và MethodMatcher.


Sử dụng DefaultPointcutAdvisor:
Trước khi bạn có thể sử dụng bất cứ cài đặt Pointcut nào, đầu tiên bạn phải tạo một Advisor, hoặc PointcutAdvisor. Nhớ rằng Advisor thì mô phỏng lại aspect của Spring, gắn kết advice và pointcut để chặn method được advise và chúng nên được advise như thế nào. Spring cung cấp 4 cài đặt của PointcutAdvisor nhưng bây giờ chúng ta quan tâm chỉ DefaultPointcutAdvisor. DefaultPointcutAdvisor thì đơn giản chỉ là PointcutAdvisor để kết hợp Pointcut và Advice.
Tạo static pointcut bằng StaticMethodMatcherPointcut:
Chúng ta sẽ tạo một static pointcut sử dụng lớp StaticMethodMatcherPointcut. Lớp này yêu cầu bạn cài đặt chỉ một phương thức matches(Method, Class). Mặc dù chỉ một phương thức được yêu cầu nhưng bạn có thể override lên phương thức getClassFilter() để đảm bảo chỉ các phương thức có đúng loại (kiểu dữ liệu) mới nhận được advise.
Ví dụ sau chúng ta sẽ tạo hai lớp BeanOne và BeanTwo, đều chứa hai phương thức giống nhau:
public class BeanOne {
public void foo(){

System.out.println("foo");

}

public void bar(){



System.out.println("bar");

}

}


Với ví dụ này chúng ta có thể tạo proxy cho cả hai lớp sử dụng cùng DefaultPointcutAdvisor, nhưng advice chỉ cung cấp cho một phương thức foo() của lớp BeanOne. Để làm điều này, chúng ta tạo lớp SimpleStaticPointcut như sau:
import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;

import org.springframework.aop.support.StaticMethodMatcherPointcut;
public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method method, Class cls) {

return ("foo".equals(method.getName()));

}
public ClassFilter getClassFilter() {

return new ClassFilter() {

public boolean matches(Class cls) {

return (cls == BeanOne.class);

}

};
}



}
Chúng ta vừa cài đặt phương thức matches(Method, Class) được yêu cầu bởi lớp StaticMethodMatcherPointcut. Cài đặt này đơn giản trả về true nếu tên của phương thức là foo, ngược lại trả về false. Ghi chú rằng chúng ta cũng override lên phương thức getClassFilter(), trả về thể hiện của ClassFilter với phương thức matches() của nó chỉ trả về true nếu lớp đó là BeanOne. Với static pointcut, chúng ta bảo là chỉ cho lấy các phương thức của lớp BeanOne và chỉ duy nhất phương thức foo().
import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;


public class SimpleAdvice implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable {

System.out.println("Excuted Method: " + invocation.getMethod().getName());

Object relValue = invocation.proceed();

System.out.println(">> Done");

return relValue;

}
}
Bây giờ chúng ta viết một ứng dụng đơn giản để tạo một thể hiện của DefaultPointcutAdvisor sử dụng các lớp SimpleAdvice và SimpleStaticPointcut.


import org.aopalliance.aop.Advice;

import org.springframework.aop.Pointcut;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;


public class StaticPointcutExample {

public static void main(String[] args) {

BeanOne one = new BeanOne();

BeanTwo two = new BeanTwo();

Pointcut pc = new SimpleStaticPointcut();

Advice advice = new SimpleAdvice();

DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();

advisor.setAdvice(advice);

advisor.setPointcut(pc);

ProxyFactory pf = new ProxyFactory();

pf.setTarget(one);

pf.addAdvisor(advisor);

BeanOne proxyOne = (BeanOne) pf.getProxy();

pf = new ProxyFactory();

pf.setTarget(two);

pf.addAdvisor(advisor);

BeanTwo proxyTwo = (BeanTwo) pf.getProxy();

System.out.println("-----Foo-----");

proxyOne.foo();

proxyTwo.foo();

System.out.println("-----Bar-----");

proxyOne.bar();

proxyTwo.bar();

}

}


Chú ý là thể hiện của DefaultPointcutAdvisor được sử dụng để tạo 2 proxy: một thể hiện của BeanOne và một thể hiện của BeanTwo, cả hai phương thức foo() và bar() đều được triệu gọi trên 2 proxy.
Chạy ví dụ này sẽ kết xuất như sau:
2007/01/20 9:38:22 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/20 9:38:22 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

-----Foo-----

Excuted Method: foo

foo

>> Done

foo

-----Bar-----

bar

bar
Như bạn thấy, chỉ phương thức của SimpleAdvice được triệu gọi cho phương thức foo() của lớp BeanOne, chính xác như mong đợi :D.
Tạo Dynamic Pointcut bằng DyanmicMethodMatcherPointcut:
Tạo dynamic pointcut thì tương tự như static pointcut. Trong ví dụ này chúng ta sẽ tạo một dynamic pointcut cho lớp sau:
public class SampleBean {

public void foo(int x) {

System.out.println("Invoked foo() with: " + x);

}

public void bar(){



System.out.println("Invoked bar()");

}

}


Chúng ta chỉ muốn advise chỉ một phương thức foo() nhưng không giống ví dụ trước, chúng ta muốn advise phương thức này chỉ nếu tham số được chuyển đến nó là một số nguyên lớn hơn hoặc ít hơn 100.
Ngoài static pointcut, Spring còn cung cấp một lớp tiện ích để tạo dynamic pointcut - DynamicMethodMatcherPointcut. Lớp DynamicMethodMatcherPointcut chỉ có một phương thức trừu tượng, matches (Method, Class, Object[]). Bây giờ chúng ta sẽ tạo một lớp SimpleDynamicPointcut kế thừa từ nó:
import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;

import org.springframework.aop.support.DynamicMethodMatcherPointcut;
public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {
public boolean matches(Method method, Class cls, Object[] args) {

System.out.println("Dynamic check for: " + method.getName());

int x = ((Integer)args[0]).intValue();

return (x != 100);

}

public boolean matches(Method method, Class cls){



System.out.println("Static check for: " + method.getName());

return ("foo".equals(method.getName()));

}

public ClassFilter getClassFilter() {



return new ClassFilter(){

public boolean matches(Class cls) {

return (cls == SampleBean.class);

}

};



}

}
Như đoạn code ở trên, chúng ta override lên phương thức getClassFilter() trong ngữ cảnh tương tự như ví dụ trước. Mặc dù chỉ yêu cầu cài đặt dynamic check, nhưng ở đây chúng ta cài đặt cả static check vì chúng ta biết phương thức bar() sẽ không bao giờ được advise. Và chúng ta thể hiện điều này bằng cách sử dụng static check, Spring tạo ra nó để không phải thực thi dynamic check cho phương thức này. Nếu chúng ta bỏ qua static check, Spring sẽ thực hiện dynamic check mỗi khi phương tức bar() được triệu gọi thậm chí nó luôn luôn trả về false. Trong phương thức matches(Method, Class, Object[]), bạn có thể thấy rằng chúng trả về false nếu giá trị của tham số được đến phương thức foo() là 100, ngược lại chúng ta sẽ trả về true. Chú ý trong dynamic check, chúng ta biết rằng chúng ta đang giải quyết phương thức foo() và các phương thức khác sẽ được chuyển đến static check.


Chúng ta sẽ viết ví dụ sử dụng pointcut này như sau:
import org.springframework.aop.Advisor;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;
public class DynamicPointcutExample {
/**

* @param args

*/

public static void main(String[] args) {



SampleBean bean = new SampleBean();

Advisor advisor = new DefaultPointcutAdvisor(

new SimpleDynamicPointcut(), new SimpleAdvice());

ProxyFactory pf = new ProxyFactory();

pf.setTarget(bean);

pf.addAdvisor(advisor);

SampleBean proxy = (SampleBean) pf.getProxy();

System.out.println("-----Foo------");

proxy.foo(1);

proxy.foo(10);

proxy.foo(100);

System.out.println("-----Bar------");

proxy.bar();

proxy.bar();

}
}
Chúng ta sử dụng lớp advice trong ví dụ static pointcut. Tuy nhiên, trong ví dụ này, chỉ hai lời gọi đầu nên được advise. Dynamic check ngăn chặn lời gọi thứ ba của phương thức foo() khỏi advise và static check cũng ngăn phương thức bar() khỏi advice. Chạy ví dụ này sẽ kết xuất như sau:
2007/01/20 10:58:30 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/20 10:58:31 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

Static check for: foo

Static check for: bar

Static check for: hashCode

Static check for: clone

Static check for: toString

-----Foo------

Static check for: foo

Dynamic check for: foo

Excuted Method: foo

Invoked foo() with: 1

>> Done

Dynamic check for: foo

Excuted Method: foo

Invoked foo() with: 10

>> Done

Dynamic check for: foo

Invoked foo() with: 100

-----Bar------

Static check for: bar

Invoked bar()

Invoked bar()
Mặc dù dynamic pointcut thì uyển chuyển hơn static pointcut nhưng nó tốn nhiều phí runtime khi yêu cầu nó, vì thế bạn chỉ nên sử dụng dynamic pointcut khi thật sự cần thiết.
Sử dụng Name Matching:
Thường khi tạo một pointcut, bạn muốn phù hợp dựa vào tên của phương thức, bỏ qua loại trả về cùng các tham số. Trong trường hợp này, bạn có thể thực hiện bằng cách tạo một lớp con của StaticMethodMatcherPointcut và sử dụng NameMatchMethodPointcut. Khi sử dụng NameMatchMethodPointcut, nó sẽ bỏ qua signature của phương thức (tên, tham số, giá trị trả về) vì thế nếu bạn có hai phương thức foo() và foo(int) thì cả hai đều phù hợp với tên foo.
public class NameBean {
public void foo(){

System.out.println("foo");

}

public void foo(int x){



System.out.println("foo " + x);

}

public void bar() {



System.out.println("bar");

}

public void yup(){



System.out.println("yup");

}

}


Trong ví dụ này, chúng ta lấy cả ba phương thức: foo(), foo(int) và bar() bằng NameMatchMethodPointcut. Chúng ta sẽ làm điều bày bằng cách chuyển tên foo và bar cho nó như sau:
import org.springframework.aop.Advisor;

import org.springframework.aop.Pointcut;

import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;

import org.springframework.aop.support.NameMatchMethodPointcut;
public class NamePointcutExample {
/**

* @param args

*/

public static void main(String[] args) {



NameBean bean = new NameBean();

NameMatchMethodPointcut pc = new NameMatchMethodPointcut();

pc.addMethodName("foo");

pc.addMethodName("bar");

Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

// create proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(bean);

pf.addAdvisor(advisor);

NameBean proxy = (NameBean) pf.getProxy();

proxy.foo();

proxy.foo(100);

proxy.bar();

proxy.yup();

}
}
Không cần phải tạo một lớp cho pointcut, bạn có thể đơn giản tạo một thể hiện của NameMatchMethodPointcut. Chạy ví dụ này sẽ kết xuất như sau:
2007/01/20 11:23:24 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/20 11:23:24 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

Excuted Method: foo

foo

>> Done

Excuted Method: foo

foo 100

>> Done

Excuted Method: bar

bar

>> Done

yup
Tạo pointcut với Regular Expressions:
Trong các phần trước, chúng ta đã thảo luận làm thế nào để chọn ra danh sách các phương thức phù hợp. Nhưng nếu bạn không biết tên của các phương thức mà biết pattern của tên đó. Ví dụ bạn muốn chọn ra tất cả các phương thức mà tên của nó bắt đầu bằng get. Trong trường hợp này, bạn có thể sử dụng regular expression pointcut: JdkRegexpMethodPointcut hoặc Perl5RegexpMethodPointcut để chọn ra các phương thức có tên phù hợp dựa vào regular expression.
Lớp RegexpBean chứa 3 phương thức sau:
public class RegexpBean {

public void foo1(){

System.out.println("foo1");

}

public void foo2(){



System.out.println("foo2");

}

public void bar(){



System.out.println("bar");

}

}


Sử dụng regular expression pointcut, chúng ta có thể chọn ra tất cả các phương thức trong lớp này mà tên của nó bắt đầu là foo.
import org.springframework.aop.framework.ProxyFactory;

import org.springframework.aop.support.DefaultPointcutAdvisor;

import org.springframework.aop.support.JdkRegexpMethodPointcut;
public class RegexpPointcutExample {
/**

* @param args

*/

public static void main(String[] args) {



RegexpBean bean = new RegexpBean();

JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();

pc.setPattern(".*foo.*");

DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());

// create proxy

ProxyFactory pf = new ProxyFactory();

pf.setTarget(bean);

pf.addAdvisor(advisor);

RegexpBean proxy = (RegexpBean) pf.getProxy();

proxy.foo1();

proxy.foo2();

proxy.bar();

}
}
Chúng ta không cần tạo một lớp pointcut, mà thay vào đó chúng ta tạo một thể hiện của JdkRegexpMethodPointcut và xác định pattern. Một thứ thú vị ở đây là pattern, khi tên của phương thức phù hợp pattern thì Spring xem phương thức đó là phù hợp.

Chạy ví dụ này sẽ kết xuất như sau:


2007/01/20 12:01:43 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

2007/01/20 12:01:43 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

Excuted Method: foo1

foo1

>> Done

Excuted Method: foo2

foo2

>> Done

bar
2.3.2. Proxy:
Mục đích chính của proxy là ngăn chặn method invocation, và thực thi một chuỗi advice được cung cấp cho từng phương thức ở nơi cần thiết. Spring AOP framework quản lý và triệu gọi advice (không phụ thuộc vào proxy). Tuy nhiên, proxy chịu trách nhiệm ngăn chặn các lời gọi đến tất cả các phương thức và chuyển chúng khi cần thiết cho advice được cung cấp.
2.3.3. Framework Services for AOP:
Cho đến bây giờ, chúng ta phải viết nhiều code để advise các đối tượng và phát sinh proxy cho chúng. Mặc dù điều này không phải là vấn đề lớn, nhưng có nghĩa là tất cả cấu hình advice đều được code cứng trong ứng dụng của bạn. Để tránh điều này Spring cung cấp một vài framework service để bạn có thể tạo advised proxy trong file cấu hình của ứng dụng và xen proxy này vào một target bean giống như bất kỳ dependency khác.
Sử dụng phương pháp declarative để cấu hình AOP. Khi sử dụng kỹ thuật này, bạn sẽ giảm được nhiều lỗi xảy ra. Bạn cũng có thể nhận các thuận lợi từ sự kết hợp của DI và AOP.
a) Cấu hình AOP Declaratively:
Cấu hình này không yêu cầu bất kỳ cấu hình đặc biệt nào. Cấu hình AOP thì giống như cấu hình bean, chỉ khác ở chỗ thay vì đưa ra bean trực tiếp, bạn có thể sử dụng lớp ProxyFactoryBean - kế thừa FactoryBean để tạo proxy một cách tự động.
b) Giới thiệu về ProxyFactoryBean:
Lớp này là lớp trọng tâm của cấu hình declarative AOP. Nó kế thừa FactoryBean để cho phép bạn xác định target và nó cung cấp một tập các advice và advisor để bean được trộn vào trong một AOP proxy. Bởi vì bạn có thể sử dụng cả advisor và advice với ProxyFactoryBean, nên bạn có thể cấu hình không chỉ advice mà còn cả pointcut.
c) ProxyFactoryBean:
Sử dụng ProxyFactoryBean thì rất là đơn giản. Bạn định nghĩa một bean sẽ là target bean và sau đó sử dụng ProxyFactoryBean, bạn định nghĩa bean mà ứng dụng sẽ truy cập, sử dụng target bean như proxy target. Mỗi khi có thể, định nghĩa target bean như anonymous bean bên trong khai báo proxy bean. Điều này ngăn ứng dụng truy cập vào các bean không cần advise. Tuy nhiên trong một vài trường hợp như ví dụ mà chúng ta sẽ xem sau, bạn có thể muốn tạo nhiều proxy cho cùng một bean, vì thế bạn nên sử dụng bean mức trên cùng cho trường hợp này.
Sau đây là hai bean và một trong hai phụ thuộc vào cái khác:
public class MyDependency {
public void foo() {

System.out.println("foo");

}

public void bar(){



System.out.println("bar");

}

}



public class MyBean {

private MyDependency dep;

public void execute(){

dep.foo();

dep.bar();

}
public MyDependency getDep() {

return dep;

}
public void setDep(MyDependency dep) {

this.dep = dep;

}

}


Trong ví dụ này, chúng ta dự định tạo 2 proxy cho cùng một thể hiện MyDependency, cả hai với cùng advice:
import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;


public class MyAdvice implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target) throws Throwable {

System.out.println("Executed method: " + method.getName());

}
}
Proxy đầu tiên sẽ advise target sử dụng trực tiếp advice, do đó tất cả các phương thức sẽ được advise. Proxy thứ hai, chúng ta sẽ cấu hình JdkRegexpMethodPointcut và DefaultPointcutAdvisor vì thế chỉ phương thức foo() của lớp MyDependency được advise. Để test advice, chúng ta sẽ tạo 2 định nghĩa bean của MyBean, mỗi bean sẽ được xen với proxy khác nhau. Sau đó chúng ta sẽ triệu gọi phương thức execute() trên mỗi bean này và quan sát điều gì sẽ xảy ra khi các phương thức được advise trên dependency được triệu gọi.


DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">












































advice























advisor




























.*foo.*












Chúng ta không làm bất kỳ thứ gì đặc biệt, chúng ta đơn giản thiết lập các thuộc tính mà chúng ta thiết lập trong code sử dụng khả năng DI của Spring.

Lớp sau sẽ lấy 2 thể hiện của MyBean từ ApplicationContext và sau đó gọi phương thức execute() cho mỗi bean:


import org.springframework.context.ApplicationContext;

import org.springframework.context.support.FileSystemXmlApplicationContext;

public class ProxyFactoryBeanExample {
/**

* @param args

*/

public static void main(String[] args) {



ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");

MyBean bean1 = (MyBean) context.getBean("myBean1");

MyBean bean2 = (MyBean) context.getBean("myBean2");

bean1.execute();

bean2.execute();

}
}
Chạy ví dụ này sẽ kết xuất như sau:


2007/01/20 14:40:08 org.springframework.core.CollectionFactory

情報: JDK 1.4+ collections available

2007/01/20 14:40:08 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions

情報: Loading XML bean definitions from file [D:\projects\Trinh\ProSpring\beans.xml]

2007/01/20 14:40:08 org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory

情報: Bean factory for application context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=4875744]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [myBean1,myBean2,myDependencyTarget,myDependency1,myDependency2,advice,advisor]; root of BeanFactory hierarchy

2007/01/20 14:40:08 org.springframework.context.support.AbstractApplicationContext refresh

情報: 7 beans defined in application context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=4875744]

2007/01/20 14:40:08 org.springframework.context.support.AbstractApplicationContext initMessageSource

情報: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@337d0f]

2007/01/20 14:40:08 org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster

情報: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@124bbbf]

2007/01/20 14:40:08 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons

情報: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [myBean1,myBean2,myDependencyTarget,myDependency1,myDependency2,advice,advisor]; root of BeanFactory hierarchy]

2007/01/20 14:40:09 org.springframework.aop.framework.DefaultAopProxyFactory

情報: CGLIB2 available: proxyTargetClass feature enabled

Executed method: foo

foo

Executed method: bar

bar

Executed method: foo

foo

bar
Cả hai phương thức foo() và bar() trong proxy đầu tiên đều được advise, bởi vì không có pointcut được sử dụng trong cấu hình. Đối với proxy thứ hai, chỉ có một phương thức foo() được advise bởi vì pointcut được sử dụng trong file cấu hình.
d) Transaction Management:
Transaction là phần nồng cốt của ứng dụng enterpise. Một cách nhìn đơn giản của transaction là begin transaction - SQL update, delete...theo sau bởi commit/rollback. Trong phần này chúng ta sẽ xem làm thế nào để sử dụng declaratice transaction thay vì code transaction. Cuối cùng, chúng ta sẽ xem làm thế nào để test transactional.
Spring hỗ trợ declarative transaction, nghĩa là bạn không cần làm lộn xộn code của bạn với code quản lý transaction. Tất cả những gì phải làm là khai báo phương thức muốn đưa vào transaction và Spring sẽ điều khiển việc quản lý transaction.
Khám phá Spring Transaction Abstraction Layer:
Khi sử dụng Spring, bạn cần chọn lựa khi sử dụng transaction - sử dụng transactoin toàn cục hay cục bộ. Transaction cục bộ xác định một transaction resource (ví dụ như JDBC connection), trong khi transaction toàn cục thì được quản lý bởi container và có thể mở nhiều transaction resource.
Phân tích các thuộc tính transaction:
Transaction có 4 thuộc tính ACID - atomicity, consistency, isolation, và durability - và nó đưa lên transaction resource để duy trì các aspect của transaction. Bạn không thể điều khiển atomicity, consistency, và durability của transaction, nhưng bạn có thể điều khiển transaction propagation và timeout, để thiết lập transaction chỉ đọc và xác định isolation level.
Spring đóng gói tất cả các thiết lập này trong giao tiếp TransactionDefinication. Giao tiếp này được sử dụng trong giao tiếp cơ bản của transaction trong Spring - PlatfromTransactionManager - mà cài đặt của nó thực hiện việc quản lý transaction trên platform xác định như JDBC hoặc JTA. Phương thức cơ bản, PlatformTransactionManager.getTransaction(), trả về một giao tiếp TransactionStatus được sử dụng để điều khiển việc thực thi transaction, nhiều cách để thiết lập transaction result và kiểm tra transaction là read-only hoặc là transaction mới.
Khám phá giao tiếp TransactionDefinition:
Như đề cập ở trên, giao tiếp TransactionDefinition điều khiển các thuộc tính của một transaction. Chúng ta sẽ xem chi tiết về nó:
package org.springframework.transaction;
import java.sql.Connection;
public interface TransactionDefinition {

int getPropagationBehavior();

int getIsolationLevel();

int getTimeout();

boolean isReadOnly();

}
Một số phương thức đơn giản và rõ ràng của giao tiếp này là getTimeout(), trả về thời gian (số giây) mà transaction phải hoàn tất và isReadOnly() để chỉ giao dịch là chỉ đọc. Transaction manager có thể sử dụng giá trị này để đáng giá việc thực thi và kiểm tra giao dịch đang thực hiện có phải read-only hay không.


Có hai phương thức khác: getPropagationBehavior() và getIsolationLevel() cần được thảo luận chi tiết hơn. Chúng ta bắt đầu với getIsolationLevel(), điều khiển những thay đổi gì trong giao dịch hiện hành mà giao dịch khác có thể truy cập. Sau đây là danh sách các transaction isolation level mà bạn có thể sử dụng:


  • TransactionDefinition.ISOLATION_DEFAULT - sử dụng isolation level mặc định của datastore ở dưới.

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED - level thấp nhất của isolation, nó cho phép giao dịch xem dữ liệu được sửa bởi các giao dịch khác chưa ủy nhiệm (commit).

  • TransactionDefinition.ISOLATION_READ_COMMITTED - level mặc định trong một số database, nó đảm bảo các giao dịch không thể đọc dữ liệu chưa commit bởi giao dịch khác.

  • TransactionDefinition.ISOLATION_REPEATABLE_READ - nhiều giới hạn hơn ISOLATION_READ_COMMITTED, nó đảm bảo rằng mỗi khi bạn chọn dữ liệu, bạn có thể chọn ít nhất cùng một tập hợp như thế một lần nữa. Điều này có nghĩa là giao dịch khác chèn dữ liệu mới, bạn có thể chọn dữ liệu mới được chèn đó.

  • TransactionDefinition.ISOLATION_SERIALIZABLE - isolation level đáng tin cậy, serializable có nghĩa là giao dịch phải được thực thi hoàn tất trước khi thực thi giao dịch khác - tuần tự.

Chọn isolation level thì rất quan trọng để đảm bảo tính toàn vẹn của dữ liệu, nhưng các sự lựa chọn này có thể ảnh hưởng đến tốc độ. Isolation level cao nhất ISOLATION_SERIALIZABLE thì trả giá đắc nhất để bảo trì.


Phương thức getPropagationBehavior() xác định những gì sẽ xảy ra cho một giao dịch, phụ thuộc vào việc có một giao dịch đang hoạt động hay không.
Các giá trị của Propagation Behavior:


  • TransactionDefinition.PROPAGATION_REQUIRED - thực thi giao dịch hiện hành, tạo một giao dịch mới nếu như không có giao dịch tồn tại.

  • TransactionDefinition.PROPAGATION_SUPPORTS - thực thi giao dịch nếu như nó tồn tại. Nếu không có thì không thực thi.

  • TransactionDefinition.PROPAGATION_MANDATORY - thực thi giao dịch nếu như có một giao dịch tồn tại. Ném vào một ngoại lệ nếu như không có giao dịch kích hoạt.

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW - luôn khởi tạo một giao dịch mới. Nếu như tồn tại một giao dịch đang hoạt động, thì nó sẽ bị tạm dừng.

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED - không thực thi giao dịch và dừng bất kỳ giao dịch nào đang tồn tại.

  • TransactionDefinition.PROPAGATION_NEVER - luôn luôn không thực thi thậm chí có giao dịch tồn tại. Ném vào ngoại lệ nếu có một giao dịch tồn tại.


Sử dụng giao tiếp TransactionStatus:
Giao tiếp này cho phép transaction manager điều khiển việc thực thi của giao dịch.Code này có thể kiểm tra giao dịch có phải là giao dịch mới hay không, hoặc nó có phải là giao dịch chỉ đọc không và nó có thể khởi tạo một rollback.
Giao tiếp PlatformTransactionManager:
Giao tiếp này sử dụng 2 giao tiếp: TransactionDefinition và TransactionStatus để tạo và quản lý các giao dịch. DataSourceTransactionManager điều khiển giao dịch được thực hiện bên trong một DataSource, HibernateTransactionManager điều khiển giao dịch được thực hiện trong session của Hibernate, JdoTransactionManager quản lý các giao dịch của JDO.
Khám phá các ví dụ mẫu về cách quản lý giao dịch:
Có 3 cách cơ bản để cài đặt một thủ tục sử dụng transaction: bạn có thể sử dụng declarative transaction, và khai báo phương thức phương thức yêu cầu transaction, bạn có thể sử dụng metadata để giới thiệu phương thức yêu cầu transaction, hoặc bạn có thể viết code để điều khiển transaction. Spring hỗ trợ cả 3 phương pháp, chúng ta sẽ bắt đầu với một phương pháp uyển chuyển và đạt hiệu quả nhất - declarative transaction management.
Cấu hình Hibernate trong ứng dụng Spring:
Nếu bạn muốn sử dụng Hibernate trong ứng dụng, bạn phải cấu hình dataSource để định nghĩa kết nối (connection) đến CSDL.

"http://www.springframework.org/dtd/spring-beans.dtd">





destroy-method="close">


org.postgresql.Driver

jdbc:postgresql://localhost/prospring

janm

Gen0me64





Bean dataSource này định nghĩa một kết nối đến CSDL PostgreSQL đang chạy trên máy cục bộ với username và password được xác định. Bạn cũng cần tạo bean sessionFactory để tất cả cài đặt trong DAO sử dụng.

"http://www.springframework.org/dtd/spring-beans.dtd">







>


dataSource">

mappingResources">

            sample.hbm.xml

hibernate.dialect">

net.sf.hibernate.dialect.PostgresSQLDialect



Đầu tiên, bạn phải thiết lập một tham chiếu đến bean dataSource. Sau đó cần thiết lập mappingResources đến ít nhất một file ánh xạ của Hibernate. Vị trí của file này phải được xác định. Cuối cùng bạn cần thiết lập thuộc tính hibernate.dialect để cho phép Hibernate phát sinh các câu SQL. Mỗi dialect cũng xác định thêm một vài đặc trưng mà CSDL hỗ trợ.
Các file ánh xạ:
Các file này định nghĩa các lớp, thuộc tính của chúng và làm thế nào để các lớp này cùng các thuộc tính của chúng ánh xạ đến các table và các column trong CSDL, để Hibernate có thể persist các lớp đến database.

Ánh xạ đơn giản:
Chúng ta bắt đầu với script tạo bảng sau:
create table Test (

TestId serial not null,

Name varchar(50) not null,

RunDate timestamp not null,

constraint PK_TestId primary key (TestId)

);


insert into Test (Name, RunDate) values ('foobar', '2004-01-01');

Đối tượng domain sẽ có 3 thuộc tính tương ứng với 3 cột đó và sẽ override lên phương thức toString() để trả về một biểu diễn của đối tượng mà người ta có thể hiểu được.

import java.util.Date;

public class Test {

private int testId;

private String name;

private Date runDate;

public String toString() {

// implementation

}

// Getters and Setters



}
Bây giờ chúng ta hãy xem file ánh xạ sau - ánh xạ các thuộc tính của đối tượng Test đến các column của table Test.

"-//Hibernate/Hibernate Mapping DTD//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">


tải về 261.07 Kb.

Chia sẻ với bạn bè của bạn:
1   2   3




Cơ sở dữ liệu được bảo vệ bởi bản quyền ©hocday.com 2024
được sử dụng cho việc quản lý

    Quê hương