Черты Scala позволяют смешивать новое поведение в классе. Рассмотрим две особенности добавления полей аудита и версии к сущностям JPA:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package mvcsample.domainimport javax.persistence.Versionimport scala.reflect.BeanPropertyimport java.util.Datetrait Versionable { @Version @BeanProperty var version: Int = _}trait Auditable { @BeanProperty var createdAt: Date = _ @BeanProperty var updatedAt: Date = _} |
Теперь смешаем «Versionable» и «Auditable» с их полями и поведением в элементе Member:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Entity@Table(name = 'members')class Member(f: String, l: String) extends BaseDomain with Auditable with Versionable { def this() = this(null, null) @BeanProperty var first: String = f @BeanProperty var last: String = l @OneToMany(fetch = FetchType.EAGER, mappedBy = 'member') @BeanProperty var addresses: java.util.List[Address] = _}trait BaseDomain { @BeanProperty @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = 'id') @Id var id: Long = 0} |
Приведенный выше класс Member теперь будет работать с классом BaseDomain и с характеристиками Versionable и Auditable. Этот вид смешивания невозможен с простой Java, так как эквивалент признаков с полями и поведением будет абстрактным (или конкретным) классом, а Java позволяет наследовать только от 1 базового класса. Однако с AspectJ можно достичь эквивалента mixin. Рассмотрим следующие аспекты, определенные с использованием языка Aspectj:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package mvcsample.aspect;import javax.persistence.Column;import javax.persistence.Version;import mvcsample.annot.Versioned;public interface Versionable { static aspect VersionableAspect { declare parents: @Versioned mvcsample.domain.* implements Versionable; @Version @Column(name = 'version') private Integer Versionable.version; public Integer Versionable.getVersion() { return this.version; } public void Versionable.setVersion(Integer version) { this.version = version; } }}package mvcsample.aspect;import java.util.Date;import javax.persistence.Column;import mvcsample.annot.Audited;public interface Auditable { static aspect AuditableAspect { declare parents: @Audited mvcsample.domain.* implements Auditable ; @Column(name='created_at') private Date Auditable.createdAt; @Column(name='updated_at') private Date Auditable.updatedAt; public Date Auditable.getCreatedAt(){ return this.createdAt; } public void Auditable.setCreatedAt(Date createdAt) { this.createdAt = createdAt; } public Date Auditable.getUpdatedAt(){ return this.updatedAt; } public void Auditable.setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } }} |
‘объявить родителей: @Versioned mvcsample.domain. * реализует Versionable;’ Конструкция aspectj добавляет интерфейс «Versionable» в качестве родительского для любого класса в пакете «mvcsampple.domain», аннотируемого @Versioned, аналогично тому, как для «Auditable». Затем аспект заключается в добавлении полей в интерфейс Versionable, который, в свою очередь, заканчивает добавлением (смешиванием) полей в целевые классы сущностей, таким образом поля и методы, связанные с аудитом и версией, смешиваются в классы сущностей. С этими двумя определенными аспектами целевой класс сущности будет выглядеть следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Entity@Table(name="members")@Access(AccessType.FIELD)@Versioned@Auditedpublic class Member extends BaseDomain{ public Member(){} public Member(String first, String last){ this.first = first; this.last = last; } private String first; @Size(min=1) private String last; @OneToMany(fetch=FetchType.EAGER, mappedBy="member") private List<address>addresses = new ArrayList<>(); .....}</address> |
Поля и поведение, определенные в аспектах Versionable и Auditable, будут смешаны в эту сущность (в более общем случае — в любую сущность с аннотациями @Versioned и @Audited). Вероятно, не так чисто, как черты Scala, но работает хорошо
Ссылка: Mixin in Java с аспектами — для примера черт Scala от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry.