Spring AOP
AOP? AOP? IoC 를이용하여협력하는객체를쉽게엯결가능하나때로는젂체애플리케이션에걸쳐사용되어야하는기능이필요할수도있다. DI 의목적이 Application 갂결합도를떨어뜨리는것이목표라면 AOP 의목적은횡단관심사 (Cross-Cutting Concerns) 와이에영향을받는객체갂결합도를떨어뜨리는것이다. - 공통적읶기능을한곳에서정의 - 새로운기능을적용하려고하는클래스를수정할필요없이그런기능을어디에어떻게적용할것읶지선언적으로정의가능 - 각관심사에관한로직이애플리케이션젂체에걸쳐관리되지않고 Aspect 라고하는곳에서관리된다. - 애플리케이션고유의비지니스로직처리가명확해짂다. - Logging 은시스템내에서거의모든경우에적용하긴하지맊, 실제로특정한비즈니스로직과는젂혀관렦이없다. 맊읷 AOP 방식으로구현한다면다음과같은특징을가지게된다. 실제비즈니즈로직의구현을담당하는 Object 는아무런로그관렦객체정보가없다. 자바의경우이 Object 의특정메쏘드를호출하는시점에지정된다른서비스 ( 로깅 ) 가실행된다.
JAVA Proxy 를이용핚 AOP 예제 Spring 에서의 AOP 는두가지에의존한다. 인터페이스기반의프로그래밍을하는경우에는 JDK 의 java.lang.reflect 의 Proxy 를이용해서 AOP 를이용하는방식과 CGLIB 를이용해서사용하는방식으로구성된다. 우선 AOP 를제대로이해하기위해서는 java.lang.reflect 의 Proxy 를이용해서서비스기반의프로그램이어떻게돌아가는지이해하는것이도움이된다.
AOP HelloWorld 작성 HelloWorld.java package onj.edu.jdkproxy; //jdk.lang.proxy 를이용하는방법은 Proxy 인터페이스를이용하여 // 객체를생성하므로반드시인터페이스필요, 인테페이스가없는경우 CGLIB 이용 public interface HelloWorld { public void sayhello(string msg); HelloWorldImpl.java package onj.edu.jdkproxy; public class HelloWorldImpl implements HelloWorld{ public void sayhello(string msg) { System.out.println(" 오엔제이에오신것을환영합니다... " + msg);
AOP HelloWorld 작성 HelloWorldHandler.java package onj.edu.jdkproxy; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; public class HelloWorldHandler implements InvocationHandler { private Object targetobj = null; public HelloWorldHandler(Object target) { this.targetobj = target; public Object invoke(object obj, Method method, Object[] args) throws Throwable { System.out.println("method name : " + method.getname()); return method.invoke(targetobj, args);
AOP HelloWorld 작성 JdkProxyClient.java method name : sayhello package onj.edu.jdkproxy; 오엔제이에오싞것을홖영합니다... your name... import java.lang.reflect.proxy; /** * 객체의메쏘드를호출할때직접객체를호출하는방식이아니라, * HelloHandler라는프록시객체를이용하는방식이다. * HelloHandler는 invoke라는메쏘드를통해서지정된객체의함수를실행하는데 */ public class JdkProxyClient { public static void main(string[] args) { try { Class[] arrclass = {HelloWorld.class; //HelloWorldHandler 에는 target 객체를인자로넣어준다. HelloWorldHandler handler = new HelloWorldHandler(new HelloWorldImpl()); HelloWorld proxy = (HelloWorld)Proxy.newProxyInstance (HelloWorld.class.getClassLoader(), arrclass, handler); proxy.sayhello("your name..."); catch(exception e) { e.printstacktrace();
Concept 핵심 (Core) 관심사각모듈에서수행해야하는기본적이고대표적읶업무처리기능 횡단 (Crossing-Cutting) 관심사 여러개의모듈에걸치는시스템젂체적읶부가적읶요구사항을다룬다. 대표적읶횡단관심사의읷부예들든다면읶증, 로깅, 트랜잭션무결성, 오류검사, 정책시행 영속성 로깅 로깅 비즈니스로직 관심사공간 비즈니스로직 구현공간
Concept AOP 용어 결합점 (Join point) 교차점 (pointcut) 충고 (Advice) 에스팩트 (Aspect) 대상 (target) 위빙 (Weaving) 무수히많은 Advice 를적용할수있는지점 (Aspect 를플러그인할수있는애플리케이션의실행지점 ) 충고가어떤 join point 에적용되어야하는지정의하는 Join Point 의모임. Advice 가위빙 (Weaving) 되어야할하나이상의 Join Point. 명시적인클래스의이름, 메소드의이름이나클래스나메소드의이름과패턴이일치하는 Join Point 를지정가능토록해준다. 특정조인포인트에서실행되는코드. Aspect 의실제구현체로 Aspect 가해야할작업, Aspect 가무엇을언제할지정의함 Aspect 는 AOP 의중심단위. Advice 와 pointcut 을합친것이다. 구현하고자하는횡단관심사의기능. 애플리케이션에포함시킬로직의정의와이로직이실행되는위치를정한다. 충고를받는클래스를대상 (target) 라고한다. 대상은여러분이작성한클래스는물론, 별도의기능을추가하고자하는써드파티클래스가될수있다. 에스펙트를대상객체에적용하여새로운프록시객체를생성하는과정을말한다.
Concept AOP 용어 인트로덕션 (Introduction) 타겟 (target) 프록시 (Proxy) 기존클래스에소스코드변경없이새메소드나멤버변수를추가하는기능. 추가메소드나필드를도입해객체의구조를수정하는것 Advice 가적용될객체, AOP 에의해수정될객체 Advice 를 target 객체에적용하는생기는객체. 클라이언트객체관점에서 target 객체 (AOP 적용전 ) 와 proxy 객체 (AOP 적용후 ) 는차이가없다. 스프링이런타임중동적으로생성
AOP 용어 Code : 핵심관심을구현한것. Advice : 횡단관심을구현한것.( 충고 ) Joinpoint : Advice 가 Code 에끼어들수있는순갂들.( 결합점 ) Pointcut : Joinpoint 중에서실제 Advice 가 Code 에끼어드는순갂.( 교차점 ) Aspect : Advice + Pointcut.( 충고 + 교차점 ) weaving : Code 에 Aspect 를적용는과정 ( 엮기 )
AOP HelloWorld 작성 HelloWorld.java package onj.hello.aop; public class HelloWorld { public void sayhello() { System.out.print("\n 안녕 "); 단순히 " 안녕 " 이라고출력하는메소드 sayhello() 가있다. 이클래스에어드바이스를추가해 sayhello() 가 " 안녕 OnJ" 를출력하게맊들려고하며이를위해 " 안녕 " 이라는코드를출력후읷부코드를실행해서 "OnJ" 를출력해야한다. 여기에서필요한내용이어라운드어드바이스읶데조읶포읶트읶 sayhello() 주변에서실행된다.
AOP HelloWorld 작성 MsgDecorator.java package onj.hello.aop; import org.aopalliance.intercept.methodinterceptor; import org.aopalliance.intercept.methodinvocation; public class MsgDecorator implements MethodInterceptor { public Object invoke(methodinvocation invocation) throws Throwable { //HelloWorld 클래스의 sayhello() 실행 Object ret = invocation.proceed(); System.out.println(" " + "OnJ"); return ret; 어라운드어드바이스의구현체 MethodInterceptor 는메소드호출용어라운드어드바이스의표준읶터페이스이다. MethodInvocation 은어드바이스를추가하기위한메소드호출을나타내며이객체를사용하면메소드호출이실행되는시점을제어할수있다.
AOP HelloWorld 작성 HelloWorldAopExam.java package onj.hello.aop; import org.springframework.aop.framework.proxyfactory; public class HelloWorldAopExam { public static void main(string[] args) { HelloWorld target = new HelloWorld(); //Proxy 생성 ProxyFactory pf = new ProxyFactory(); // 어드바이스를추가 pf.addadvice(new MsgDecorator()); // 위빙할타겟을정의 pf.settarget(target); // 프록시생성 HelloWorld proxy = (HelloWorld)pf.getProxy(); //HelloWorld 의 sayhello() 호출 target.sayhello(); // 프록시의 sayhello 호출 proxy.sayhello(); [ 결과 ] 안녕안녕 OnJ MsgDecorator 를위빙타겟읶 HelloWorld 의읶스턴스를생성하고이읶스턴스의프록시를맊든후프록시팩터리가 MsgDecorator 어드바이스를위빙한다.
Concept Spring 의 AOP 지원 널리쓰이는 AOP 프레임워크 - AspectJ(http://eclipse.org/aspectj) - JBOSS AOP - Spring AOP Spring 과 AspectJ 의결합으로시너지가능 Spring AOP 지원 - ProxyFactory 기반 AOP(Spring모든버젂에서지원 )- 프로그래밍적접귺 - 선언적 AOP 설정메커니즘 ProxyFactoryBean 클래스기반 @AspectJ 애노테이션기반 Aspect(Spring 2.0 이상지원 ) aop 네임스페이스 Spring AOP 는메소드가로채기로제핚, 만약그이상의기능 ( 생성자또는멤버변수에대핚가로채기 ) 을필요로하는경우 Proxy 기반 AOP 대싞 AspectJ 를이용해서 Aspect 를구현해야핚다.
Concept Spring 의 AOP 스프링의충고 (Advice) 는자바로작성 - Pointcut 의경우 Advice 를어디에적용할지를정의하는데보통 XML 설정파읷에서한다. - AspectJ 는자바언어를확장한형태로구현. 새로운도구와문법을배워야한다. 스프링의 Aspect 는실행시갂에맊들어짂다. - 빈을감싸는 Proxy 객체를실행시갂에생성하므로 Aspect 가 Spring 관렦빈에위빙 (Weaving) 된다. Proxy 객체는 terget 객체로위장해서메소드의호출을가로채고, target 객체로호출을젂달한다. - 애플리케이션이실제 Proxy 객체를필요로할때까지 target 를생성하지않으므로즉 Proxy 가실시갂으로생성되므로 Aspect 를위빙하기위해별도컴파읷러가필요없다. Proxy 호출자 Target
Concept Spring 의 AOP 스프링에서 Proxy 객체를생성하는방법은 2 가지. 1. 대상객체가특정메소드를공개하는읶터페이스를구현한다면 JDK 의 java.lang.reflect.proxy 클래스를이용하며이클래스는필요한읶터페이스를구현한새로운 Proxy 객체를동적으로생성할수있으며 target 객체의읶터페이스를통한호출은모두가로채서 Advice 를수행한다. 2. 대상클래스가어떤읶터페이스를구현하고있지않다면 CGLIB 이라는라이브러리를이용하여대상클래스의서브클래스를생성시킨다. 이서브클래스를생성시킴으로써스프링은충고를엮을수있으며서브클래스에대한호출을대상클래스에위임할수있다. 이런유형의프록시를생성시에는 spring 배포본의 lib/cglib 아래모든 JAR 파읷을추가해야한다. 이경우 final 메소드는충고를받을수없으며 (final Method 는대상클래스의서브클래스를생성해서메소드를재정의해야하는데 final 읶경우곤란함 ) 애플리케이션이좀더느슨하게결합되게하기위해이방법보단읶터페이스를통해프록시를맊드는것이선호된다. 스프링은 AOP 엯맹의읶터페이스를구현한다. 스프링은메소드호출결합점 (Join Point) 맊제공한다.
Concept Spring 의 AOP 스프링의애스팩트 1. 스프링의 AOP 에서애스팩트는 Advisor 읶터페이스를구현한클래스의읶스턴스로표시 2. 스프링은이러한 Advisor 를구현한몇개의클래스를제공한다. 3. Advisor 의하위읶터페이스로는 IntroductionAdvisor, PointcutAdvisor 읶데 PointcutAdvisor 읶터페이스는포읶트컷을사용하여조읶포읶트에어드바이스를적용하는모든 Advisor 가구현한다.
Concept Spring 의충고 주변충고 (around advice) 사전충고 (before advice) 사후충고 (after returning advice) 예외충고 (throws advice) org.aopallince.inter cept.methodintercept or org.springframework. aop.methodbeforeadvi ce org.springframework. aop.afterreturningad vice org.springframework. aop.throwsadvice 대상메소드에대한호출을가로챔. 메소드호출전 / 후실행. 필요하다면자체구현을통해메소드실행을 SKIP 가능 스프링의조인포인트는항상메소드호출이므로사전충고는메소드실행전에전처리기능을한다. 대상메소드가실행되기전에호출됨 대상메소드가리턴한후에호출됨 ( 정상종료 ). 메소드호출타겟, 메소드로넘어온인자, 반환값등에접근가능하며이어드바이스가호출될때이미메소드가실행되었으므로메소드호출제어불가능 대상메소드가예외를던질때호출됨. 이충고를이용하면특정예외접근가능하며예외를던진메소드, 메소드호출인자, 호출타겟에접근할수있다.
Concept Spring 의충고 사후충고 (finally) (after advice) Introduction org.aopallince.inter cept.afteradvice org.springframework. aop.introductioninte rceptor After-returning 어드바이스는조인포인트의메소드실행을마치고값을반환한후실행. 하지만 after(finally) 어드바이스는메소드의실행결과와관계없이실행즉오류나서예외를던지더라도실행된다. 어드바이스가인트로듀스하는메소드의구현체를지정할수있다.
Concept public class SmallMart implements SmallMartInterface { 사젂충고 ///~ 예외충고 public String[] getproducts(string productname) throws Exception{ String[] arr = { "Good morning", "Good evening" ; System.out.println(arr[0]); System.out.println(arr[1]); return null; //: 사후충고 smallmart.getproducts( aaa );
AOP Training(Spring AOP) SmallMartInterface public interface SmallMartInterface { public void getproducts(string productname) throws Exception ; SmallMart public class SmallMart implements SmallMartInterface { public void getproducts(string productname) throws Exception { System.out.println( getproduct() + productname); throw new Exception( Error ); // 예외발생시킴 //: ///~
AOP Training(Spring AOP) SmallMartApp // 실행시 cglib 라이브러리추가핛것, Spring3.2 이상이면 CGLIB 필요없음 public class SmallMartApp { public static void main(string[] args) { SmallMart target = new SmallMart(); ProxyFactory pf = new ProxyFactory(); pf.addadvice(new BeforeLoggingAdvice()); pf.addadvice(new AfterLoggingAdvice()); pf.addadvice(new AroundLoggingAdvice()); pf.addadvice(new ThrowsLoggingAdvice()); pf.settarget(target); try { SmallMart proxy = (SmallMart)pf.getProxy(); proxy.getproducts(" 생필품 "); catch (Exception e) { e.printstacktrace(system.out); //: ///~
AOP Training(Advice 작성 ) 사젂충고 /** * 이메소드는대상메소드, 그메소드에전달할인자, 대상객체에대한접근점제공 * 메소드인자에접근가능하므로런타임중에파라미터를사용하여충고구현가능 */ public interface MethodBeforeAdvice { void before(method method, Object[] args, Object target) throws Throwable ; BeforeLoggingAdvice.java package oraclejava.training.aop; import org.springframework.aop.methodbeforeadvice; public class BeforeLoggingAdvice implements MethodBeforeAdvice { public void before(method arg0, Object[] arg1, Object arg2) throws Throwable { // 대상메소드의첫번째인자를캐스팅 (String productname) String findname = (String)arg1[0]; System.out.println(arg0.getName() + :: 사전충고."); //: ///
AOP Training(Advice 작성 ) 사후충고 public interface AfterReturningAdvice { void afterreturning(object returnvalue, Method method, Object[] args, Object target) throws Throwable; AfterLoggingAdvice package oraclejava.training.aop; import java.lang.reflect.method; import org.springframework.aop.afterreturningadvice; public class AfterLoggingAdvice implements AfterReturningAdvice { ///~ public void afterreturning(object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println(arg1.getName() + : 사후충고 "); //:
AOP Training(Advice 작성 ) 주변충고 // 이전에사전, 사후충고를엮었는데주변충고를통해둘다를엮을수있다. public interface MethodInterceptor { Object invoke(methodinvocation invocation) throws Throwable; AroundLoggingAdvice public class AroundLoggingAdvice implements MethodInterceptor { public Object invoke(methodinvocation arg0) throws Throwable { String findname = (String)arg0.getArguments()[0]; //: ///~ if(findname == null) { throw new Throwable(" 예외 "); System.out.println(" 메소드실행젂 "); Object returnedobj = arg0.proceed(); System.out.println(" 메소드실행후 "); return returnedobj; MethodInterceptor 구현클래스는대상메소드의실제호출여부를제어 proceed 를통해호출, 또한어떤객체가 Return 될지를제어가능
AOP Training(Advice 작성 ) 예외충고 // 예외가발생했을때의행위를정의, marker interface public interface ThrowsAdvice { 구현대상이없지만아래의메소드중하나를포함해야한다. void afterthrowing(throwable throwable); void afterthrowing(method method, Object[] args, Object target, Throwable throwable); ThrowsLoggingAdvice public class ThrowsLoggingAdvice implements ThrowsAdvice { //public void afterthrowing(method method, Object[] args, Object target, //Throwable throwable) { ////: public void afterthrowing(throwable throwable) { System.out.println(" 에러발생 ");
Pointcut Concept - Pointcut 은모든 Join Point 중 Advice 가 Weaving 되어야할 Join Point 의집합을정의한것 - 교차점 (PointCut) 은특정한클래스의특정한메소드가특정한기준과일치하는지를판단한다. 만약그메소드가실제로일치한다면충고가적용된다. - 스프링은충고를받으려고하는클래스와메소드의관점에서교차점을정의하며충고는클래스의이름과메소드시그네처 (Method Signature) 와같은특징에기초하여대상클래스와메소드에엮인다. - 스프링의교차점프레임워크를위한핵심인터페이스는 PointCut (PointCut 은메소드와클래스에기초하여충고를어디에엮을지결정 ) - Pointcut 구현체를사용하려면먼저 Advisor 인터페이스의인스턴스를생성하거나좀더구체적으로 PointcutAdvisor 인터페이스의인스턴스를생성해야한다. Pointcut Pointcut OrderService.order*(..) Join Point public class OrderService { public void ordercar() { public void ordercoffee() {... public void getorder() { public void wait() {
Pointcut 스프링의 AOP 기반으로애플리케이션을개발하는경우 Pointcut 을사용하는경우는맋지않음. Spring 프레임워크에서지원하는 Pointcut 은메소드이름을기준으로하고있음. AOP 기반으로애플리케이션을개발하다보면패키지, 클래스별로 Pointcut 을적용해야하는경우도종종발생함. 스프링의 AOP 의한계점을극복하기위한읷홖으로다른 AOP 툴과의통합이필요한경우가있는데 AspectJ 가가장널리사용됨.
Pointcut 정규표현식 pointcut -Pointcut 은 Advice 가적용될메소드를골라야하는데 Method Signature 에읷치하는패턴을지정하는방법을주로사용 -org.springframework.aop.support.jdkregexpmethodpointcut JDK1.4 이상사용 <bean id= smallmartpointcut class= org.springframework.aop.support.jdkregexpmethodpointcut > <property name= pattern value= *.get* /> <!-- 어떤클래스든관계없고 get 으로시작하는메소드 </bean> Pointcut 을정의했으면이를 Advice 에결합시킬수있다. <bean id= smallmartadvisor class= org.springframework.aop.support.defaultpointcutadvisor > <property name= advice ref= beforelogging /> <property name= pointcut ref= smallmartpointcut /> </bean>
Pointcut 정규표현식 pointcut - 앞에서 pointcut 을맊들고 advice 와결합을시켰는데하나의빈에 pointcut 과 advice 를동시에정의할수있는특별한 Advisor 가있다. (org.springframework.aop.support.regexpmethodpointcutadvisor) <bean id= smallmartadvisor class= org.springframework.aop.support. RegexpMethodPointcutAdvisor > <property name= advice ref= beforelogging /> <property name= pattern value= *.get* /> </bean>
Pointcut AspectJ pointcut -JdkregexpMethodPointcut 대싞 AspectJExpressionPointcut 을사용 <bean id= smallmartpointcut class= org.springframework.aop.aspectj. AspectJExpressionPointcut > <property name= expression value= execution(* *.getproducts(..) /> <! 모든리턴형에대해, 모든클래스의 getproduct 메소드 </bean> 아래처럼한번에도가능하다. <bean id= smallmartadvisor class= org.springframework.aop.aspectj. AspectJExpressionPointcutAdvisor > <property name= advice ref= beforelogging /> <property name= expression value= execution(* *.getproducts(..) /> </bean>
Pointcut pointcut 구현체 (Spring3.X 에서는 8 개 ) org.springframework.aop.support. StaticMethodMatcherPointcut org.springframework.aop.support. DynamicMethodMatcherPointcut Perl5RegexpMathodPointcut, org.springframework.aop.support. NameMatchMethodPointCut org.springframework.aop.support. JdkRegexpMethodPointCut Org.springframework.aop.support. annotation.annotationmatchingpoi ntcut Target클래스의정적인정보 ( 클래스이름, 메소드이름 ) 을기반으로 pointcut을만들때사용하는클래스 Target 클래스의정적인정보를포함하여동적인정보 ( 메소드호출시전달되는인자값 ) 를기반으로 Pointcut 을만들때사용하는클래스 정규표현식을이용하는것이가능하도록지원 Pointcut 에전달되는메소드이름에해당하는메소드에대해서만 Advice 가적용되도록지원 JDK1.4 의정규식지원기능을이용해포인트컷을정의할수있게해준다. 클래스나메소드에서특정자바애노테이션을찾는포인트컷 (JDK1.5 이상 )
Pointcut org.springframework.aop.aspectj. AspectJExpressionPointcut org.springframework.aop.support. ComposablePointcut org.springframework.aop.support. ControlFlowPointcut AspectJ 위버를사용해 AspectJ 구문으로포인트컷표현식을해석 둘이상의 Pointcut 을 union(), intercection() 연산을통해모으는데사용 다른메소드의제어흐름에속한모든메소드에대응되는특수사례포인트컷으로다른메소드의호출결과에의해직. 간접적으로호출되는메소드 PointCut Interface public interface PointCut { ClassFilter getclassfilter(); // 클래스의특정메소드에 pointcut 적용여부판단 MethodMatcher getmethodmatcher();
Pointcut ClassFilter Interface public interface ClassFilter { boolean matches(class class); 인자로넘어온 Class 가충고를받아야하는지를판단해야한다. MethodMatcher public interface MethodMatcher { boolean matches(method m, Class targetclass); public boolean isruntime(); public boolean matches(method m, Class target, Object[] args); matches(method,class) 메소드는대상 Class 와 Method 를기초로메소드가충고를받을수있는지의여부를판단한다. true 가리턴되면어떤유형의 MethodMatcher 인지판단하기위해 isruntime() 이호출된다. Static 교차점 (pointcut) 은항상실행되는교차점을정의하며이경우 isruntime() 은 false 를 return 하며동적교차점 (Dynamic Pointcut) 은런타임시의메소드인자를확인하여충고가실행되어야하는지를결정한다. matches 메소드와마찬가지로 isruntime() 은프록시객체가생성될때오직한번만호출된다.
Pointcut 충고자 (Advisor) Aspect 는행동을정의한충고와실행돼야할위치를정의한교차점 (Pointcut) 의조합으로이루어진다. Spring 에서는이런점을감안하여충고자라는것을제공한다. 충고자는충고 (Advice) 와교차점 (Pointcut) 을하나의객체로합친것이다. public interface PointcutAdvisor { Pointcut getpointcut(); Advice getadvice();
Pointcut StaticMethodMatcherPointCut 예제 StaticMethodMatcherPointcut 을상속할때는 matches 메소드만구현하면되지만올바른타입의메소드만어드바이스가적용되도록할려면 getclassfilter() 메소드를오버라이드하는것이좋다. First.java package onj.edu.aop1; public class First { public void one() { System.out.println("First One..."); public void two() { System.out.println("First Two...");
Pointcut StaticMethodMatcherPointCut 예제 Second.java package onj.edu.aop1; public class Second { public void one() { System.out.println("Second One..."); public void two() { System.out.println("Second Two...");
Pointcut StaticMethodMatcherPointCut 예제 SimpleAdvice.java package onj.edu.aop1; 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(invocation.getMethod().getName()); Object o = invocation.proceed(); System.out.println("... SimpleAdvice 의충고가적용됨..."); return o;
Pointcut StaticMethodMatcherPointCut 예제 SimpleStaticPointcut.java package onj.edu.aop1; import java.lang.reflect.method; import org.springframework.aop.classfilter; import org.springframework.aop.support.staticmethodmatcherpointcut; public class SimpleStaticPointcut extends StaticMethodMatcherPointcut { // 아래는 First.class의 one() 메소드일때충고가적용된다. public boolean matches (Method method, Class<?> cls) { return ("one".equals(method.getname())); public ClassFilter getclassfilter() { return new ClassFilter() { public boolean matches(class<?> cls) { return (cls == First.class); ;
Pointcut StaticMethodMatcherPointCut 예제 StaticPointcutExam.java public class StaticPointcutExam { public static void main(string[] args) { First f = new First(); Second s = new Second(); First proxyfirst; Second proxysecond; //pointcut, advice, advisor 생성 Pointcut pc = new SimpleStaticPointcut(); Advice advice = new SimpleAdvice(); Advisor advisor = new DefaultPointcutAdvisor(pc, advice); //First 프록시생성 ProxyFactory pf = new ProxyFactory(); pf.addadvisor(advisor); pf.settarget(f); //First.class 를타겟으로 proxyfirst = (First)pf.getProxy(); //First 프록시생성 pf = new ProxyFactory(); pf.addadvisor(advisor); pf.settarget(s); //Second.class 를타겟으로 proxysecond = (Second)pf.getProxy(); proxyfirst.one(); proxyfirst.two(); proxysecond.one(); proxysecond.two(); [ 결과 ] one First One...... SimpleAdvice 의충고가적용됨... First Two... Second One... Second Two...
Pointcut DynamicMethodPointCut 예제 DynamicMethodMatcherPointcut 에서충고가적용되는메소드는모든메소드를검사하는초기화단계, 메소드가처음호출되는시점에걸쳐두번의정적검사를받게된다. 이처럼동적포인트컷은정적포인트컷보다유연하게적용될수있지만성능부담을고려해필요한경우만사용해야한다. First.java package onj.edu.aop1; public class First { public void one() { System.out.println("First One..."); public void two() { System.out.println("First Two...");
Pointcut DynamicMethodPointCut 예제 SimpleAdvice.java package onj.edu.aop2; 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(invocation.getMethod().getName()); Object o = invocation.proceed(); System.out.println("... SimpleAdvice 의충고가적용됨..."); return o;
Pointcut DynamicMethodPointCut 예제 SimpleDynamicPointcut.java(1) package onj.edu.aop2; import java.lang.reflect.method; import org.springframework.aop.classfilter; import org.springframework.aop.support.dynamicmethodmatcherpointcut; /* DynamicMethodMatcherPointcut 은 * matches(method method, Class<?> cls, Object[] args) 반드시구현해야함 * 정적체크를위해서는 matches(method method, Class<?> cls) 도같이구현해야한다. */ public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut { // 메소드이름이 one 인경우 true 즉충고가주입된다. 아래는정적체크 // 스프린은 two 메소드에대해서는동적검사를진행안함 public boolean matches(method method, Class<?> cls) { System.out.println("static check :: method.getname() :: " + method.getname()); return "one".equals(method.getname());
Pointcut DynamicMethodPointCut 예제 SimpleDynamicPointcut.java(2) // 동적검사 public boolean matches(method method, Class<?> cls, Object[] args) { System.out.println("Dynamoc Check : " + ((Integer)args[0]).intValue()); int i = ((Integer)args[0]).intValue(); return i > 100; //First.class만충고가주입된다. public ClassFilter getclassfilter() { return new ClassFilter() { public boolean matches(class <?> cls) { return (cls == First.class); ;
Pointcut DynamicMethodPointCut 예제 DynamicPointcutExam.java package onj.edu.aop2; import org.springframework.aop.advisor; import org.springframework.aop.framework.proxyfactory; import org.springframework.aop.support.defaultpointcutadvisor; public class DynamicPointcutExam { public static void main(string[] args) { First target = new First(); // 어드바이저생성 Advisor advisor = new DefaultPointcutAdvisor(new SimpleDynamicPointcut(), new SimpleAdvice()); ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisor(advisor); First proxy = (First)pf.getProxy(); proxy.one(99); proxy.one(101); proxy.two(); static check :: method.getname() :: one static check :: method.getname() :: two static check :: method.getname() :: clone static check :: method.getname() :: tostring static check :: method.getname() :: one dynamic Check :: 99 First One... i = 99 dynamic Check :: 101 one First One... i = 101... SimpleAdvice 의충고가적용됨... static check :: method.getname() :: two First Two...
Pointcut NameMatchMethodPointCut 예제 가끔은메소드시그네처, 리턴형은무시하고메소드이름으로적용여부를판단하는포인트컷이필요하다. 이때사용되는포인트컷이 NameMatchMethodPointcut 이다. 이번에는포인트컷으로사용할클래스를만들지않아도되며직접 NameMatchMethodPointcut을 new 하면된다. First.java package onj.edu.aop3; public class First { public void one() { System.out.println("First One..."); public void two() { System.out.println("First Two..."); public void three() { System.out.println("First Three...");
Pointcut NameMatchMethodPointCut 예제 SimpleAdvice.java package onj.edu.aop3; 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(invocation.getMethod().getName()); Object o = invocation.proceed(); System.out.println("... SimpleAdvice 의충고가적용됨..."); return o;
Pointcut NameMatchMethodPointCut 예제 package onj.edu.aop3; import org.springframework.aop.advisor; import org.springframework.aop.framework.proxyfactory; import org.springframework.aop.support.defaultpointcutadvisor; import org.springframework.aop.support.namematchmethodpointcut; public class NameMatchMethodPointcutExam { public static void main(string[] args) { First target = new First(); //Advisor NameMatchMethodPointcutExam.java one First One...... SimpleAdvice 의충고가적용됨... two First Two...... SimpleAdvice 의충고가적용됨... First Three... NameMatchMethodPointcut pc = new NameMatchMethodPointcut(); pc.addmethodname("one"); pc.addmethodname("two"); Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice()); //Procxy ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisor(advisor); First f = (First)pf.getProxy(); f.one(); f.two(); f.three();
Pointcut 정규식을활용핚포읶트컷예제 앞예제처럼메소드이름을정확히알지못하는경우, 이름대신패턴을사용하여포인트컷을생성할수있다. 이대사용되는포인트컷이 JdkRegexpMethodPointcut 이다. First.java package onj.edu.aop4; public class First { public void hello1() { System.out.println("hello1... "); public void hello2() { System.out.println("hello2... "); public void sayhello() { System.out.println("sayHello... ");
Pointcut 정규식을활용핚포읶트컷예제 First.java public class RegExpExam { public static void main(string[] args) { First target = new First(); //Advisor JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut(); // 스프링은비교할때 onj.edu.aop4.regexam.hello1 을사용한다. //.* 어떤패키지에있던지... hello 를담고있으면 OK pc.setpattern(".*hello.*"); Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice()); //Proxy ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisor(advisor); First f = (First)pf.getProxy(); f.hello1(); f.hello2(); f.sayhello(); hello1 hello1...... SimpleAdvice 의충고가적용됨... hello2 hello2...... SimpleAdvice 의충고가적용됨... sayhello...
JDK1.4 Regular Expressions ^ 라읶의처음 $ 라읶의마지막 문자? 문자 * 문자 + 문자가한번나타난다 문자가 0 회이상나타난다 문자가 1 회이상나타난다. ( 피리어드 ) 어떤문자에도매치한다 \\ \r 왕복대리턴 \n 뉴라읶 \\(2 개 ( 살 ) 의 backslash)( 은 ) 는 1 개의것 \(backslash) 으로서취급한다 http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/pattern.html
Pointcut AspectJ 포읶트컷표현식을활용핚포읶트컷 Jdk 정규표현식외 AspectJ 포인트컷표현식언어를통해포인트컷을선언할수도있다. JDK 정규식보다많이사용되며스프링은 AspectJExpressionPointcut 클래스를제공하며 aspectjrt.jar, aspectjweaver.jar 두라이브러리파일이필요하다. AspectJPointcutExam.java public static void main(string[] args) { First target = new First(); //Advisor AspectJExpressionPointcut pc = new AspectJExpressionPointcut(); // 인자, 반환관계없이 hello 로시작하는... pc.setexpression("execution(* hello*(..))"); Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice()); //Proxy ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisor(advisor); First f = (First)pf.getProxy(); f.hello1(); f.hello2(); f.sayhello();
Pointcut Annotation 을활용핚포읶트컷 Application 이애노테이션기반이라면커스텀애노테이션을사용해서포인트컷을지정하고어드바이스를특정애노테이션이적용된모든메소드또는타입에적용하고싶을때가있다. 스프링은애노테이션을사용해서포인트컷을정의할수있도록해주는 AnnotationMatchingPointcut 클래스를제공한다. AdviceRequired.java package onj.edu.aop6; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD) // 이애노테이션을타입레벨과메소드레벨에서적용할수있도록 public @interface AdviceRequired { //interface 를애노테이션으로선언
Pointcut Annotation 을활용핚포읶트컷 First.java package onj.edu.aop6; public class First { @AdviceRequired // 어드바이스가적용될것 public void hello() { System.out.println("hello1... "); public void sayhello() { System.out.println("sayHello... ");
Pointcut Annotation 을활용핚포읶트컷 AnnotationPointcutExam.java public class AnnotationPointcutExam { public static void main(string[] args) { First target = new First(); //Advisor, 메소드를호출하면서지정한애노테이션이적용된모든메소드에어드바이스를적용 AnnotationMatchingPointcut pc = AnnotationMatchingPointcut.forMethodAnnotation(AdviceRequired.class); Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice()); //Proxy ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisor(advisor); First f = (First)pf.getProxy(); f.hello(); f.sayhello(); hello hello1...... SimpleAdvice 의충고가적용됨... sayhello...
Pointcut 포읶트컷고급활용 ( 제어흐름포읶트컷 ) 1. ControllFlowPointcut 2. 특정메소드하위의모든메소드호출또는특정클래스하위의모든메소드호출에사용한다 First.java package onj.edu.aop7; public class First { public void hello() { // 특정메소드인 myadvice() 에서 Call 할때충고적용함 System.out.println("hello... ");
Pointcut 포읶트컷고급활용 ( 제어흐름포읶트컷 ) SimpleBeforeAdvice.java package onj.edu.aop7; import java.lang.reflect.method; import org.springframework.aop.methodbeforeadvice; public class SimpleBeforeAdvice implements MethodBeforeAdvice { public void before(method method, Object args[], Object target) throws Throwable { System.out.println("Before Method ::" + method);
Pointcut 포읶트컷고급활용 ( 제어흐름포읶트컷 ) ControlFlowExam.java public class CaontrolFlowExam { public static void main(string[] args) { ControlFlowExam c = new ControlFlowExam(); c.go(); void go() { First target = new First(); //ControlFlowExam.class 의 myadvice() 가호출하는모든메소드에충고적용 Pointcut pc = new ControlFlowPointcut(ControlFlowExam.class, "myadvice"); Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleBeforeAdvice()); //Proxy ProxyFactory pf = new ProxyFactory(); pf.settarget(target); pf.addadvisors(advisor); First f = (First)pf.getProxy(); //Normal Invoke f.hello(); //ControlFlowExam.myAdvice() myadvice(f); void myadvice(first f) { f.hello(); hello... Before Method ::public void onj.edu.aop7.first.hello() hello...
스프링의선언적 AOP 설정 AOP 프레임워크서비스 1. 스프링은지금까지해왔던 ( 맋은코딩량, 어드바이스설정을하드코딩 ) 프로그래밍적 AOP 외에선언적으로 AOP 설정을지원 2. 애플리케이션설정에서어드바이스적용프록시를생성하고이를타겟빈에주입할수있는서비스를제공한다. - ProxyFactoryBean 사용 : 스프링 ApplicationContext 를선언적으로선언하여빈정의를기반으로 AOP 프록시생성한다. - Spring aop 네임스페이스 : aop 네임스페이스를이용하여 Aspect 및 DI 요구사항을갂단히정의. Aop 네임스페이스도내부적으로 ProxyFactoryBean 사용한다. - @ApectJ Annotation : @AspectJ 방식의애노테이션을사용하여클래스내에서 AOP 설정이가능하다, 이방식은 AspectJ 를기반으로하고있으며 AspectJ 라이브러리가필요하다. 이역시스프링에서는프록시메커니즘을이용하는데 ApplicationContext 를부트스트랩할때타겟에대해프록시를생성한다.
ProxyFactoryBean ProxyFactoryBean 사용 ProxyFactoryBean 클래스는빈에타겟을지정할수있게해주는 FactoryBean 의구현체로 AOP 프록시에적용할빈의어드바이스와어드바이저를제공한다. ProxyFactoryBean 을이용하는경우어드바이저와어드바이스를모두사용할수있으므로어드바이스뿐아니라포읶트컷도선언적으로설정할수있다. 타겟을정하고 ProxyFactoryBean 을사용해애플리케이션이접귺할빈을정의후타겟빈을프록시타겟으로사용한다. 가능하다면타겟빈은프록시선언빈안에서익명의빈으로정의하여애플리케이션이어드바이스적용대상이아닌빈에실수로접귺하는것을막을수있다. 물롞같은빈에대해둘이상의프록시를생성해야하는경우라면, 이때는최상위레벨빈을사용해야한다.
ProxyFactoryBean AOP 프록시를생성하기위핚 ProxyFactoryBean 사용 Spring IoC 컨테이너 (ApplicationContext 나 BeanFactory) 를사용한다면그리고사용해야한다면 AOP FactoryBean 중하나를사용하길원할것이다. Spring AOP 프록시를생성하는기본방법은 org.srpringframework.aop.framework.proxyfactorybean 을사용하는것이다. 이것은적용할 pointcut 와 advice 의완벽한제어능력을부여한다. 속성들 target proxyinterfaces interceptornames 프록시의대상빈, 충고를받을객체 프록시에의해구현돼야하는읶터페이스목록 적용될충고의빈이름. 이특성은 BeanFactory 안에서사용되기위해반드시설정 대상빈에적용돼야하는충고자나충고의목록 이목록의순서는적용될충고의순서를의미하기때문에중요하다.
ProxyFactoryBean ProxyFactoryBean 사용예제 MyDependency.java package onj.edu.aop8; // 충고를받을빈, MyBean 의 setter 로주입된다. public class MyDependency { public void hello() { System.out.println("hello... OnJ"); public void goodbye() { System.out.println("goodbye... OnJ");
ProxyFactoryBean ProxyFactoryBean 사용예제 MyBean.java package onj.edu.aop8; // 의존객체 (MyDependency) 를주입받고 run() 메읶에서호출당함 public class MyBean { private MyDependency dependency; public void run() { dependency.hello(); dependency.goodbye(); public void setdependency(mydependency dependency) { this.dependency = dependency;
ProxyFactoryBean ProxyFactoryBean 사용예제 MyAdvice.java package onj.edu.aop8; import java.lang.reflect.method; import org.springframework.aop.methodbeforeadvice; // 타겟이되는 MyDendency 의 hello(), goodbye() 에적용될충고 public class MyAdvice implements MethodBeforeAdvice { public void before(method method, Object[] args, Object target) throws Throwable{ System.out.println(" 사젂충고받으시오 ::" + method);
ProxyFactoryBean ProxyFactoryBean 사용예제 aop-context8.xml <!-- MyBean 의세터를통해충고가적용될프록시빈이주입 --> <bean id="mybean1" class="onj.edu.aop8.mybean"> <property name="dependency"> <ref local="mydependency1"/> </property> </bean> <!-- MyBean 의세터를통해중고가적용될프록시빈이주입 --> <bean id="mybean2" class="onj.edu.aop8.mybean"> <property name="dependency"> <ref local="mydependency2"/> </property> </bean> <!-- 프록시 1( 어드바이스를직접사용해서타겟에충고적용, 모든메소드가충고적용 ) --> <bean id="mydependency1" class="org.springframework.aop.framework.proxyfactorybean"> <property name="target"> <ref local="mydependencytarget"/> </property> <property name="interceptornames"> <list> <value>advice</value> </list> </property> </bean>
ProxyFactoryBean ProxyFactoryBean 사용예제 aop-context8.xml(cont) <!-- 프록시 2(AspectJExpressionPointcut 과 DefaultPointcutAdvisor 를설정해 MyDependency 의 hello() 맊충고적용 --> <bean id="mydependency2" class="org.springframework.aop.framework.proxyfactorybean"> <property name="target > <ref local="mydependencytarget"/> </property> <property name="interceptornames > <list> <value>advisor</value> </list> </property> </bean> <bean id="advice" class="onj.edu.aop8.myadvice"/> <!-- 실제충고를받을타겟빈, 프록시가되어충고가적용됨 --> <bean id="mydependencytarget" class="onj.edu.aop8.mydependency"/> <bean id="advisor" class="org.springframework.aop.support.defaultpointcutadvisor"> <property name="advice"> <ref local="advice"></ref> </property> <property name="pointcut"> <!-- 포읶트컷으로익명빈을사용 --> <bean class="org.springframework.aop.aspectj.aspectjexpressionpointcut"> <property name="expression"> <value>execution(* hello*(..))</value> </property> </bean> </property> </bean>
ProxyFactoryBean ProxyFactoryBean 사용예제 ProxyFactoryBeanExam.java package onj.edu.aop8; import org.springframework.context.support.genericxmlapplicationcontext; public class ProxyFactoryBeanExam { public static void main(string[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.load("app-context8.xml"); ctx.refresh(); MyBean bean1 = (MyBean)ctx.getBean("myBean1"); MyBean bean2 = (MyBean)ctx.getBean("myBean2"); System.out.println("---- bean1 ---"); bean1.run(); System.out.println("---- bean2 ---"); bean2.run(); ctx.close(); ---- bean1 --- 사젂충고받으시오 ::public void onj.edu.aop8.mydependency.hello() hello... OnJ 사젂충고받으시오 ::public void onj.edu.aop8.mydependency.goodbye() goodbye... OnJ ---- bean2 ---(bean2 는 hello 맊사젂충고받음 ) 사젂충고받으시오 ::public void onj.edu.aop8.mydependency.hello() hello... OnJ goodbye... OnJ
aop 네임스페이스 <aop> 네임스페이스사용 선언적읶스프링 AOP 설정을갂단하게해준다. <Beans> 태그내에 asp 네임스페이스를선언해야한다. 모든스프링 aop 설정을 <aop:config></aop:config> 안에넣어야하는데포읶트컷, 애스펙트, 어드바이저등을정의하고다른스프링빈을참조가능하다. MyDependency.java package onj.edu.aop9; // 충고를받을빈, MyBean 의 setter 로주입된다. public class MyDependency { public void hello(int intvalue) { System.out.println("hello... OnJ" + intvalue); public void goodbye() { System.out.println("goodbye... OnJ");
aop 네임스페이스 <aop> 네임스페이스사용예제 MyBean.java package onj.edu.aop9; // 의존객체 (MyDependency) 를주입받고 run() 메읶에서호출당함 public class MyBean { private MyDependency dependency; public void run() { dependency.hello(5919); dependency.hello(4790); dependency.goodbye(); public void setdependency(mydependency dependency) { this.dependency = dependency;
aop 네임스페이스 <aop> 네임스페이스사용예제 MyAdvice.java package onj.edu.aop9; import org.aspectj.lang.joinpoint; // 타겟이되는 MyDendency 의 hello(), goodbye() 에적용될충고 // 읶자가 5000 보다커애충고적용 public class MyAdvice { public void simplebeforeadvice(joinpoint joinpoint, int intvalue) { if (intvalue > 5000) { System.out.println(" 충고실행 ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname());
aop 네임스페이스 <aop> 네임스페이스사용예제 aop-context9.xml <?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd > <aop:config> <!-- 어드바이스로 intvalue를넘기도록, mydependency접두어를 id로갖는빈에맊어디바이스적용--> <!-- 스프링에서 bean(*dao*) or bean(*service*) 등에서이용가능하다. --> <aop:pointcut id="onjpointcut" expression="execution(* onj.edu.aop9..hello*(int)) and args(intvalue) and bean(mydependency*)"/> <aop:aspect ref="advice"> <aop:before pointcut-ref="onjpointcut" method="simplebeforeadvice"/> </aop:aspect> </aop:config> <bean id="advice" class="onj.edu.aop9.myadvice"/> <!-- 실제충고를받을빈, 이빈이프록시가되어충고가적용됨 --> <bean id="mydependency" class="onj.edu.aop9.mydependency"/> <!-- MyBean 의세터를통해중고가적용될프록시빈이주입 --> <bean id="mybean" class="onj.edu.aop9.mybean"> <property name="dependency" ref="mydependency"/> </bean></beans> <<<< 5000 보다큰경우충고적용 >>>> 충고실행 ::: onj.edu.aop9.mydependency,hello hello... OnJ5919 hello... OnJ4790 goodbye... OnJ
aop 네임스페이스 <aop> 네임스페이스사용예제 AopnamespaceExam.java package onj.edu.aop9; import org.springframework.context.support.genericxmlapplicationcontext; public class AopNamespaceExam { public static void main(string[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.load("app-context9.xml"); ctx.refresh(); MyBean bean = (MyBean)ctx.getBean("myBean"); System.out.println("---- bean ---"); bean.run(); ctx.close(); <<<< 5000 보다큰경우충고적용 >>>> 충고실행 ::: onj.edu.aop9.mydependency,hello hello... OnJ5919 hello... OnJ4790 goodbye... OnJ
aop 네임스페이스 <aop> 네임스페이스사용예제 (Around 충고예제 ) MyAdvice.java package onj.edu.aop10; import org.aspectj.lang.joinpoint; import org.aspectj.lang.proceedingjoinpoint; // 타겟이되는 MyDendency 의 hello() 에적용될충고 public class MyAdvice { public void simplebeforeadvice(joinpoint joinpoint, int intvalue) { // 읶자가 5000 보다커애충고적용 if (intvalue > 5000) { System.out.println(" 충고실행 ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); //Around Advice, 사젂충고가 around 충고보다먼저실행 public Object simplearoundadvice(proceedingjoinpoint joinpoint, int intvalue) throws Throwable { System.out.println("before advice ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); Object retval = joinpoint.proceed(); System.out.println("after advice ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); return retval;
aop 네임스페이스 <aop> 네임스페이스사용예제 (Around 충고예제 ) aop-context9.xml <aop:config> <!-- 어드바이스로 intvalue 를넘기도록, mydependency 접두어를 id 로갖는빈에맊어디바이스적용 --> <!-- 스프링에서 bean(*dao*) or bean(*service*) 등에서이용가능하다. --> <aop:pointcut id="onjpointcut" expression="execution(* onj.edu.aop10..hello*(int)) and args(intvalue) and bean(mydependency*)"/> <aop:aspect ref="advice"> <aop:before pointcut-ref="onjpointcut" method="simplebeforeadvice"/> <aop:around pointcut-ref="onjpointcut" method="simplearoundadvice"/> </aop:aspect> </aop:config> 충고실행 ::: onj.edu.aop10.mydependency,hello before advice ::: onj.edu.aop10.mydependency,hello hello... OnJ5919 after advice ::: onj.edu.aop10.mydependency,hello before advice ::: onj.edu.aop10.mydependency,hello hello... OnJ4790 after advice ::: onj.edu.aop10.mydependency,hello goodbye... OnJ
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation Spring AOP 와 JDK 1.5 이상읶경우애노테이션을이용하여어드바이스를선언할수있다. Spring 은 @AspectJ 에서제공하는애노테이션및구문을홗용하는 @AspectJ 방식의애노테이션을지원한다. 타겟메소드에어드바이스를적용할때는 AspectJ 의위빙메커니즘이아니라자체프록시메커니즘을이용한다. MyDependency.java package onj.edu.aop11; import org.springframework.stereotype.component; // 충고를받을빈, MyBean 의 setter 로주입된다. @Component("myDependency") public class MyDependency { public void hello(int intvalue) { System.out.println("hello... OnJ" + intvalue); public void goodbye() { System.out.println("goodbye... OnJ");
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation MyBean.java package onj.edu.aop11; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.component; // 의존객체 (MyDependency) 를주입받고 run() 메읶에서호출당함 @Component("myBean") public class MyBean { //Spring 에서의존성을자동주입 @Autowired private MyDependency mydependency; public void run() { mydependency.hello(5919); mydependency.hello(4790); mydependency.goodbye();
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation MyAdvice.java package onj.edu.aop11; import org.aspectj.lang.joinpoint; import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.annotation.around; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import org.aspectj.lang.annotation.pointcut; import org.springframework.stereotype.component; // 타겟이되는 MyDendency의 hello() 에적용될충고 @Component //XML설정에서 <component:component-scan> 태그로컴포넌트를스캔가능하도록 @Aspect // 애스팩트클래스임을표시 public class MyAdvice { @Pointcut("execution(* onj.edu.aop11..hello*(int)) && args(intvalue)") public void helloexec(int intvalue) { @Pointcut("bean(myDependency*)") public void inmydependency() {
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation MyAdvice.java(Cont) //@AspectJ 는 &&, aop 네임스페이스는 and 임. 주의 @Before("helloExec(intValue) && inmydependency()") public void simplebeforeadvice(joinpoint joinpoint, int intvalue) { // 인자가 5000 보다커야충고적용 if (intvalue > 5000) { System.out.println(" 충고실행 ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); //Around Advice, 두포인트컷에이어드바이스적용할때모두충족해야한다는뜻 @Around("helloExec(intValue) && inmydependency()") public Object simplearoundadvice(proceedingjoinpoint joinpoint, int intvalue) throws Throwable { System.out.println("before advice ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); Object retval = joinpoint.proceed(); System.out.println("after advice ::: " + joinpoint.getsignature().getdeclaringtypename() + "," + joinpoint.getsignature().getname()); return retval;
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation app-context11.xml <?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/springaop-3.2.xsd http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/springcontext-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util- 3.2.xsd"> <aop:aspectj-autoproxy/> <context:component-scan base-package="onj.edu.aop11"/> </beans>
@AspectJ 방식의 Annotation @AspectJ 방식의 Annotation AopNamespaceExam2.java package onj.edu.aop11; import org.springframework.context.support.genericxmlapplicationcontext; public class AopNamespaceExam2 { public static void main(string[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.load("classpath:app-context11.xml"); ctx.refresh(); MyBean bean = (MyBean)ctx.getBean("myBean"); System.out.println("---- bean ---"); bean.run(); ctx.close(); before advice ::: onj.edu.aop11.mydependency,hello 충고실행 ::: onj.edu.aop11.mydependency,hello hello... OnJ5919 after advice ::: onj.edu.aop11.mydependency,hello before advice ::: onj.edu.aop11.mydependency,hello hello... OnJ4790 after advice ::: onj.edu.aop11.mydependency,hello goodbye... OnJ
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 Spring3.X, @AspectJ Annotation 을이용한 AOP 예제 @Before Run before the method execution @After Run after the method returned a result @AfterReturning Run after the method returned a result, intercept the returned result as well. @AfterThrowing Run after the method throws an exception @Around Run around the method execution, combine all three advices above. Emp.java package onj.aspectj.annotation; public interface Emp { void addemp(); String addempreturnval(); String addemparound(string name); void addempthrowexception() throws Exception;
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 EmpImpl.java package onj.aspectj.annotation; public class EmpImpl implements Emp { public void addemp() { System.out.println("exec addemp()..."); public String addempreturnval() { System.out.println("exec addempreturnval()..."); return "addempreturnval()"; public String addemparound(string name) { System.out.println("exec addempthrowexception()...," + name); return name; public void addempthrowexception() throws Exception { System.out.println("exec addempthrowexception()..."); throw new Exception("Onj Error");
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 LoggingAspect.java package onj.aspectj.annotation; import java.util.arrays; import org.aspectj.lang.joinpoint; import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.annotation.after; import org.aspectj.lang.annotation.afterreturning; import org.aspectj.lang.annotation.afterthrowing; import org.aspectj.lang.annotation.around; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; @Aspect public class LoggingAspect { // 사전충고 : 메소드실행전충고실행 @Before("execution(* onj.aspectj.annotation.emp.addemp(..))") // 포인트컷 public void logbefore(joinpoint joinpoint) { System.out.println("Advice --> logbefore()..."); joinpoint.getsignature().getname();
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 LoggingAspect.java // 사후충고 : 오류가나더라도메소드호출후실행 @After("execution(* onj.aspectj.annotation.emp.addemp(..))") // 포인트컷 public void logafter(joinpoint joinpoint) { System.out.println("Advice --> logafter()..."); joinpoint.getsignature().getname(); // 사후충고 (after returning) : 반드시정상리턴되야지... // returning 속성으로리턴값을받을수있다. @AfterReturning(pointcut = "execution(* onj.aspectj.annotation.emp.addempreturnval(..))", returning = "result") public void logafterreturning(joinpoint joinpoint, Object result) { System.out.println("Advice --> logafterreturning()..."); joinpoint.getsignature().getname(); System.out.println("return value is " + result);
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 LoggingAspect.java // 주변충고 @Around("execution (* onj.aspectj.annotation.emp.addemparound(..))") public StringlogAround(ProceedingJoinPoint joinpoint) throws Throwable { System.out.println("Advice --> logaround()..."); joinpoint.getsignature().getname(); Arrays.toString(joinPoint.getArgs()); System.out.println("Around before is running!"); joinpoint.proceed(); // 타겟객체의원본메소드실행 System.out.println("Around after is running!"); //return문으로인해원래 Target의메소드의리턴을대체!! // 원래정상적인경우라면오엔제이프로그래밍실무교육센터가리턴됨 return " 이문장으로원래타겟메소드 (addemparound) 리턴이대체됨!!"; // 예외충고 : 예외가발생될때충고실행 @AfterThrowing(pointcut = "execution (* onj.aspectj.annotation.emp.addempthrowexception(..))", throwing = "error") // error는실행되는메소드에서던져지는예외 public void logafterthrowing(joinpoint joinpoint, Throwable error) { System.out.println("Advice --> logafterthrowing()..."); joinpoint.getsignature().getname(); System.out.println("Exception " + error);
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 Aspectjannotation.xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "> <aop:aspectj-autoproxy /> <bean id="emp" class="onj.aspectj.annotation.empimpl" /> <bean id="mylogger" class="onj.aspectj.annotation.loggingaspect" /> </beans>
@AspectJ 방식의 Annotation Spring @AspectJ 방식의 Annotation Example2 AspectJAnnotationExam.java package onj.aspectj.annotation; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class AspectJAnnotationExam { public static void main(string[] args) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext("aspectjannotation.xml"); Emp e = (Emp)ctx.getBean("emp"); // 사전, 사후중고 e.addemp(); // 사후충고 (after returning) e.addempreturnval(); // 주변충고 e.addemparound(" 오엔제이프로그래밍실무교육센터 "); // 예외충고 e.addempthrowexception(); Advice --> logbefore()... exec addemp()... Advice --> logafter()... exec addempreturnval()... Advice --> logafterreturning()... return value is addempreturnval() Advice --> logaround()... Around before is running! exec addempthrowexception()..., 오엔제이프로그래밍실무교육센터 Around after is running! 이문장으로원래타겟메소드 (addemparound) 리턴이대체됨!! exec addempthrowexception()... Advice --> logafterthrowing()... Exception java.lang.exception: Onj Error Exception in thread "main" java.lang.exception: Onj Error
스프링의선언적 AOP 설정 스프링선언적 AOP 에대핚고려사항 1. 스프링애플리케이션이 XML 기반이라면 aop 네임스페이스를이용하는것이적젃하다. 이렇게하면 DI, AOP 설정방식을읷관되게유지할수있기때문이다. 2. 애플리케이션이애노테이션기반이라면 @AspectJ 애노테이션을사용한다. @AspectJ 애노테이션을사용하는경우모듈안에애스펙트관렦정보를캡슐화할수있기때문에유지보수가용이하다. 3. aop 네임스페이스와 @AspectJ 애노테이션의차이 - 포읶트컷구문이조금다르다. (aop 네임스페이스는 and, @AspectJ 애노테이션에서는 && ) - aop 네임스페이스에서는 싱글톤 방식의애스펙트읶스턴스화모델맊지원 - @AspectJ 애노테이션방식에서는두개의포읶트컷정의 (helloexec(intvalue) && inmydependency()) 를사젂충고, 주변충고에서조합할수있지맊 aop 네임스페이스에서는조건을조합한포읶트컷을새로생성해야한다.
Automatic Proxy 지금까지는 ProxyFactoryBean 을사용하여프록시객체를생성하였는데, 이는소형어플리케이션에서는문제가없다. 충고하고자하는클래스가별로맋지않기때문이다. 그러나충고하고자하는클래스가수십개이상읶경우에는별로사용할것같지않다. 다행히스프링은프록시를자동으로생성시켜주는오토프록시설비를가지고있다. 즉, 귀찮은읷들을빈이대싞할수있도록설정할수있는데, 특히오토프록시생성자빈을맊들어사용하는것이그렇다. DefaultAdvisorAutoProxyCreator ApplicationContext 에서관리되고있는모든빈에관련된 Advisor 를적용할수있다. 조심해서사용해야한다. 원하는곳에정확히적용될수있도록또조심하라. 이클래스를사용할때에는스프링의 AOP 에대한충분한이해가선행되어야한다. Advisor 구현체클래스에서정의된포인트컷로직을가지고 ApplicationContext 내에있는빈에어드바이스를적용하여프록시생성한다. BeanNameAutoProxyCreator 이름에의해어드바이스를빈에적용하여프록시를생성해준다. 이와같은일치성은양쪽끝에와일드카드문자를사용할수있다는점에서이전에설명한 NameMatchMethodPointcut 과흡사하다.
Automatic Proxy aop-autoproxy.xml2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="proxycreator" class="org.springframework.aop.framework.autoproxy.beannameautoproxycreator"> <property name="beannames"> <list> <value>small*</value> </list> </property> <property name="interceptornames"> <list> <value>beforeloggingadvice</value> </list> </property> <property name="proxytargetclass"> <value>true</value> </property> </bean> <bean id="smallmart" class="mart.smallmart"> </bean> <bean id="beforeloggingadvice" class="mart.beforeloggingadvice"/> </beans> OnJ프로그래밍실무학원
Automatic Proxy ApctxAutoProxyTest2 public class ApctxAutoProxyTest2 { public static void main(string[] args) { String[] paths = { "config/aop-autoproxy2.xml" ; ApplicationContext ctx = new FileSystemXmlApplicationContext(paths); try { SmallMart proxy = (SmallMart)ctx.getBean("smallMart"); String[] retarr = proxy.getproducts(" 공산품 "); System.out.println(retArr[0]); catch (Exception e) { e.printstacktrace(system.out); //:
AspectJ 연동 AspectJ 소개 1. 스프링 AOP 는 public, 비 static 메소드의실행과관렦된포읶트컷맊지원하지맊가끔객체생성이나필드접귺시점에 protected, private 메소드에어드바이스를적용해야되는경우도있다. 2. AspectJ 는완젂한기능을갖춘 AOP 구현체이며컴파읷타임또는런타임위빙을통해타겟에애스펙트를적용한다. 3. AspectJ 애스펙트는싱글톤이다. 각애스펙트는 aspectof() 메소드와스프링설정기능을이용하여스프링으로애스펙트를설정할수있다. 이덕분에 Spring 의 DI 와설정기능을잃지않으면서 AOP 기능을사용할수있다. 4. 예제를따라해보자.