Scala: Java AspectJ implementation around Python tips or decorators

I am using Java + AspectJ for my launch. I would love to switch to Scala, but I have a common design pattern that I'm not sure is the best way to implement in Scala.

A huge amount of our application uses AspectJ pointpoints, using annotations as a marker. It is very similar to the Python decorator and posted on the blog about it here .

I tried this technique in Scala but had problems with AspectJ + Scala . Even if I made it to work, it looks like unScala.

I have seen some projects use some spell-closing spells (I think they do).

@Transaction replacement example:

transaction { // code in here. } 

I have to say, although I prefer annotation more, as it seems more declarative. What is a Scala way of declaratively "decorating" code blocks?

+7
source share
3 answers

By the way, I speak on Scala Days 2011 in the same subject . The basic idea is the same as the example of Kim and Dean. However, when it comes to the full range of cross-cutting issues, similarities and differences become more nuanced.

At one end of the spectrum there are no truly cross-cutting issues, such as caching. When the host language does not support higher-order functions (such as Java), implementing the problem as an aspect becomes attractive. For example, using the AspectJ method and annotation you can say:

  @Cacheable(keyScript="#account.id") public double getNetWorth(Account account) { ... expensive computation } 

But with a higher order function in Scala, you can do:

  def getNetWorth(account: Account) : Double = { cacheable(keyScript=account.id) { ... expensive computation } } 

Scala's approach is much better because:

  • Caching is unlikely to be widely used. For example, it is unlikely that an entire method in a class or all public methods in all classes in a package can be cached. And even if there is such a situation, keyScript unlikely to be the same or easily expressed in general form.
  • The AspectJ approach uses annotation as a crutch to offer a decent implementation. With a higher order function in Scala, intent is expressed directly.
  • To calculate the AspectJ key, you must use an external language (for example, OGNL or Spring Expression Language). With Scala, you can simply use the host language.

In the middle, there are common problems related to transaction management and security. At first glance, they look like caching. However, in practice, we see that applying this functionality to all methods of a class (with a common subselect, for example, open access) or all methods of all classes marked with an annotation (for example, @Service ) is common. If so, then the AspectJ approach becomes excellent because it provides a way to apply functionality at a higher level than higher order functions. You no longer need to surround each method with transactional {} or secured {} when class-level annotation is fine. In terms of security, the AspectJ approach simplifies security auditing.

At the other end of the spectrum , cross-cutting issues such as tracing, profiling, monitoring, political execution, auditing, some forms of concurrency management (for example, Swing / SWT / Android UI thread dispatching), etc. are considered. This is very well suited for selecting a pointcut (with and often without annotations). It is very difficult to do the same in a consistent way, using only higher-order functions.

There are more semantic nuances, but the bottom line is that when you find annotation of each method to use a cross approach, a higher order function is likely to be a better approach. For others, using Scala with AspectJ is likely to provide a consistent and compact solution.

ps I have not tried AspectJ + Scala in Eclipse recently (since Scala in Eclipse only started working recently). But the external build using Maven worked fine after http://lampsvn.epfl.ch/trac/scala/ticket/4214 has been fixed.

+12
source

scala will be

 def transaction(f: =>Unit) = { println("start transaction") f println("end transaction") } transaction { println("inside transaction") } 

Will print

 start transaction inside transaction end transaction 
+8
source

There are other benefits to using the transaction and annotation method. You can add catch and finally clauses to ensure that resources are cleaned properly. To slightly increase Kim’s example:

 def transaction(f: =>Unit) = { println("start transaction") try { f println("end successful transaction") } catch { case ex => // rollback? println("end failed transaction") } finally { // cleanup? println("end cleanup") } } transaction { println("inside transaction") } 

You can also make transaction calls inside the method bodies, while you cannot annotate the block internal to the method. Of course, you could just make this inner block another method call with annotation.

I understand the appeal of annotations and XML configuration files in this regard since my Java days, but these days I prefer everything to be written as “normal” code because of uniformity and greater expressiveness. I only use annotations when I call the Java library that requires them. In addition, if you make your code as “functional" as possible, then everything will be declarative !;)

+5
source

All Articles