1 / 44

Approfondimento Aspect Oriented Programming (AOP)

Approfondimento Aspect Oriented Programming (AOP). Paradigma di programmazione, che prevede l’inserimento di funzionalità comune (cross-cutting concerns) a funzionalità esistente

kiona
Download Presentation

Approfondimento Aspect Oriented Programming (AOP)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ApprofondimentoAspect Oriented Programming (AOP)

  2. Paradigma di programmazione, che prevede l’inserimento di funzionalità comune (cross-cutting concerns) a funzionalità esistente Lo scopo è quello di separare aspetti comuni (logging, security, gestione eccezioni, ecc.) da business logic nel senso più stretto Aspect Oriented Programming

  3. Aspect Oriented Programming (AOP) • Tutti i paradigmi, in particolare il paradigma a oggetti, prevedono l’incapsolamento di funzionalità in entità separate, creando astrazioni (metodi, classi). • Esistono però funzionalità che potrebbero non rientrare facilmente in un’organizzazione di questo tipo, perché più trasversali (cross-cutting) • Esempio: logging

  4. Cross-cutting concerns tipici • Sincronizzazione • Gestione della memoria • Persistenza • Security • Caching • Logging • Monitoring • Business rules • …

  5. Implementazioni • Le implementazini più diffuse per Java sono AspectJ e Hyper/J • AOP viene integrata in diversi framework, soprattutto attraverso l’implementazione di AspectJ • Spring • JBoss AOP • Guice

  6. Terminologia di base • Cross-cutting concernFunzionalità comune (trasversale) da condividere tra più funzioni. Esempio: funzionalità di logging, identica, all’interno di metodi o classi diverse, in cui, ad esempio, un log dev’essere eseguito in entrata e uno in uscita. • AdviceCodice aggiuntivo da applicare al modello di business esistente. Esempio: codice di logging, unico, da chiamare separatamente quando necessario.

  7. Terminologia di base (2) • PointcutPunto di esecuzione all’interno dell’applicazione. Punto in cui un cross-cutting concern dev’essere applicato. Esempio: punto raggiunto quando un certo metodo inizia (logging di entrata) o termina (logging di chiusura) • AspectModulo che combina la descrizione del pointcut (join-point) e il body dell’advice.Esempio: aggiungiamo un aspect all’applicazione, specificando modalità di logging e quando questa va eseguita

  8. Logging • Tipicamente una funzionalità da aspect come logging la si trova in più parti nel codice, rendendo difficoltosa la sua comprensione e la sua manutenzione • Una modifica al logging può significare modificare diversi moduli, classi e metodi

  9. Logging (2) • Codice in ogni modulo

  10. Logging (3) • Centralizzazione in un unico aspect

  11. Esempio • Funzionalità di pagamento con carta di credito public class BillingService implements IBillingService { … public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return ...; } }

  12. Esempio (2) • Abbiamo però anche bisogno di controlli di security e logging public Receipt chargeOrder(User user, Order order, CreditCard card) throws Exception { logger.info(“Inizio pagamento…”); if (!checkUserPermission(user) { logger.info(“Utente non autorizzato…”); throw new UnauthorizeException(); } ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); logger.info(“Fine pagamento…”); return ...; }

  13. Esempio (3) • Nel codice precedente, elementi di logging e di security sono da considerare cross-cutting concerns • Cosa succede se dobbiamo modificare elementi di security nell’applicazione? Siccome questi elementi sono sparsi in tutta l’applicazione, le modifiche saranno parecchie. • Meglio sarebbe poterli gestire in modo centrale

  14. Aspect • AOP spinge a risolvere il problema della presenza di singoli elementi in più parti del codice permettendo di esprimere questi cross-cutting concerns attraverso moduli chiamati aspects • Un aspect contiene advice (codice da eseguire) e pointcut (dichiarazione di quando gli advice vanno eseguiti) • Esempio: un aspect potrebbe contenere • codice di verifica della security • specifica che la verifica viene eseguita ogni volta prima della chiamata a charge()

  15. Aspect (2) • Possibile esempio in AspectJ “pseudocode” public aspect SecurityCheck { before() : within(Receipt IBillingService.chargeOrder( User user, Order order, CreditCard card)) && call(ChargeResult ICreditCardProcessor.charge( long, CreditCard)) { if (!checkUserPermission(user) { logger.info(“Utente non autorizzato…”); throw new UnauthorizeException(); } }

  16. Aspect (3) • AspectJ permette tutta una serie di modalità dichiarative per specificare i join points • I joint point sono l’elemento più critico, perché attraverso la loro dichiarazione, espressa anche attraverso regular expressions, si specifica il match • Espressioni complesse rendono il codice poco “prevedibile” e quindi anche poco mantenibile

  17. Esempi di pointcut • execution(* set*(*)) match con l’esecuzione di tutti i metodi il cui nome inizia con “set” e hanno un unico parametro di qualsiasi tipo • within(ch.supsi.*) limita lo scope del pointcut a qualsiasi cosa (classe, metodo) nel package “ch.supsi” • this(CreditCard)Questo pointcut risolve quando l’oggetto attuale in esecuzione (this) è un’istanza di CreditCard • execution(* set*(*)) && within(ch.supsi.*) && this(CreditCard)Combinazione dei tre criteri precedenti

  18. Implementazioni • Esistono fondamentalmente due tipi di implementazione per AOP • Class-wavingIntegra le implementazioni degli aspect direttamente nelle classi in cui devono essere eseguite. Il weaving può essere applicato sia a livello di compilazione, che loading o runtime. • ProxyLa chiamata al metodo di un oggetto viene intercettata. Vengono usati Java dynamic proxy o CGLIB (code generation lib)I framework che si basano sul proxy sono generalmente più semplici e si basano quasi esclusivamente sul meccanismo di method interceptor.

  19. Implementazione: proxy (1) • Viene generata una sottoclasse della classe da estendere, o dinamicamente, a runtime (Java dynamic proxy), oppure modificando il bytecode durante la generazione (CGLIB) Subject request() ... RealSubject Proxy request() ... request() ...

  20. Implementazione: proxy (2) • Il meccanismo di Java dynamic proxy può essere applicato solo se esiste un’interfaccia della classe da estendere • Con CGLIB, invece, il proxy eredita dalla stessa classe che estende RealSubject request() ... RealSubject Proxy request() ... request() ...

  21. AOP in framework • Framework come Spring, Guice o altri, integrano le funzionalità di AspectJ, creando se possibile un livello di astrazione • Spring: ne facilita l’utilizzo, pur lasciando aperta la possibilità di accedere a funzionalità avanzate e specifiche di AspectJ • Guice: ne facilita e ne delimita l’utilizzo, allo scopo di promuoverne l’impiego dove questo migliora il codice, ma evitarne l’abuso, con conseguenze nefaste nel codice

  22. AOP con Guice • Guice prevede AOP attraverso il meccanismo di “method interception” • In questo modo si può eseguire un advice ogni volta che uno specifico metodo viene invocato • In Guice si parla di • Matcher: elemento esegue il match => pointcut • MethodInterceptor: parte di codice da eseguire => advice

  23. AOP con Guice (2) • MatcherDichiarazione che permette di accettare o rifiutare un valore. In Guice servono due matcher: uno per specificare la classe e uno per specificare il metodo. • MethodInterceptorEseguito all’invocazione di un metodo che risolve il match.Riceve informazioni sulla chiamata: il metodo, i suoi argomenti, e l’istanza dell’oggetto invocante.

  24. Esempio • Riutilizzare l’esempio del pagamento con carta di credito già usato per DI • Eseguire durante l’esecuzione del pagamento un controllo attraverso AOP per verificare i giorni in cui può avvenire il pagamento • Implementato: specificare uno o più giorni in cui il servizio non può essere usato

  25. Esempio (2) • Codice attuale public class BillingService implements IBillingService { … public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return ...; } }

  26. Matcher • Definiamo un’annotation per specificare il Matcher. Servirà a segnalare il metodo interessato. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NotOnWeekends { }

  27. Matcher (2) • Utilizzo dell’annotation public class BillingService implements IBillingService { … @NotOnWeekends public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return ...; } }

  28. Interceptor public class WeekendBlocker implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Calendar today = new GregorianCalendar(); String todayDisplayName = today.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH); Order order = (Order) invocation.getArguments()[0]; if (todayDisplayName.equals(”Sunday")) { throw new IllegalStateException( invocation.getMethod().getName() + " for " + order.getAmount() + " not allowed on " + todayDisplayName); } return invocation.proceed(); } }

  29. Interceptor (2) • Chiamata al metodo vero e proprioinvocation.proceed(); • Accesso ai parametriinvocation.getArguments() • Accesso alle informazioni del metodoinvocation.getMethod()

  30. Modulo di bind • Guice utilizza un modulo per specificare i vari binding public class NotOnWeekendsModule extends AbstractModule { protected void configure() { bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), new WeekendBlocker()); bind(ITransactionLog.class). toProvider(DatabaseTransactionProvider.class); bind(ICreditCardProcessor.class). to(PaypalCreditCardProcessor.class); bind(IBillingService.class).to(BillingService.class); } }

  31. Modulo di bind (2) Match con ogniclasse Solo imetodi con questa annotation bindInterceptor( Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), new WeekendBlocker()); Oggettocontenenteilmetodo interceptor

  32. Main public class Main{ public static void main(String[] args) { Injector injector = Guice.createInjector(new NotOnWeekendsModule()); … IBillingService billingService = injector.getInstance(IBillingService.class); … } }

  33. Esempio 2: security • Controllo se un certo utente cerca di eseguire una determinata azione e se ha il diritto di farlo • Le azioni che un utente può eseguire vengono specificate seguendo un approccio che utilizza ruoli • Specifichiamo con annotation quali ruoli hanno diritto ad eseguire una determinata azione

  34. Esempio 2: security (2) • Un interceptor viene creato per verificare se l’utente che chiama l’azione ha il ruolo necessario (in un caso reale, ruolo e azione sono legati con permission, perciò andrebbero verificate anche queste) • User manager che mantiene le informazioni sull’utente registrato: public interface IUserManager { void setCurrentUser(User user); User getCurrentUser(); }

  35. User Manager • All’interno di un’applicazione multiutente lo user manager utilizzerebbe la sessione per gestire gli utenti registrati • In un’applicazione desktop, invece, basta un singleton • Lo user possiamo rappresentarlo come una semplice classe che gestisce il nome dell’utente (informazione minima) e un set di ruoli appartenenti all’utente

  36. User public class User { private String fName; private Set<Role> fRoles; public User(String name, Set<Role> roles) { fName = name; fRoles = roles; } public String getName() { return fName; } public Set<Role> getRoles() { return fRoles; } … }

  37. Ruoli • Per specificare ruoli in modo semplice e controllato, usiamo una enumeration public enum Role { CUSTOMER, EMPLOYEE }

  38. Annotazione delle azioni • Le annotation ci permettono di segnalare quali ruoli sono richiesti per eseguire una certa operazione • Usata per segnalare un metodo e accessibile a runtime • value() serve a specificare il ruolo necessario @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresRole { Role value(); }

  39. Azioni • Supponendo ora di avere una classe con delle azioni da eseguire, le annotation ci permettono di specificare con quale ruolo public class VideoRental { … @RequiresRole(Role.CUSTOMER) ... @RequiresRole(Role.EMPLOYEE) … }

  40. Azioni (2) public class VideoRental { @Inject IUserManager fUserManager; @RequiresRole(Role.CUSTOMER) public void rentMovie(long movieId) { System.out.println(String.format( "Movie %d rented by user %s.”, movieId, fUserManager.getCurrentUser())); } @RequiresRole(Role.EMPLOYEE) public void registerNewMovie(String name) { System.out.println(String.format( "New movie \"%s\" registered by user %s.", name, fUserManager.getCurrentUser())); } }

  41. Interceptor • A questo punto ci vuole un interceptor che faccia uso delle informazioni specificate dalle annotation per verificare i ruoli public class RoleValidationInterceptor implements MethodInterceptor { @Inject private IUserManager fUserMgr; public Object invoke(MethodInvocation invocation) throws Throwable { Role requiredRole = invocation.getMethod().getAnnotation(RequiresRole.class).value(); if (fUserMgr.getCurrentUser() == null || !fUserMgr.getCurrentUser().getRoles().contains(requiredRole)){ throw new IllegalStateException(”…”); } return invocation.proceed(); } }

  42. Module • Bisogna inoltre specificare quando viene usato l’interceptor public class ExampleModule extends AbstractModule { public void configure() { bind(IUserManager.class).to(UserManager.class).in(Scopes.SINGLETON); RoleValidationInterceptor roleValidationInterceptor = new RoleValidationInterceptor(); bindInterceptor(any(), annotatedWith(RequiresRole.class), roleValidationInterceptor); //necessario, per risolvere @Inject in rileValidationInterceptor requestInjection(roleValidationInterceptor); } }

  43. Module (2) • Prima di tutto associamo un’implementazione di IUserManager all’interfaccia. Definendola come singleton, specifichiamo che lo stesso oggetto usato in VideoRental venga usato anche in RoleValidationInterceptor • In seguito associamo l’interceptor a tutti i metodi annotati con RequiresRole • L’ultimo passaggio è necessario per iniettare nell’interceptor le sue dipendenze (nel nostro caso con UserManager)

  44. Creazione di un framework AOP

More Related