Черты Scala позволяют смешивать новое поведение в классе. Рассмотрим две особенности добавления полей аудита и версии к сущностям JPA:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package mvcsample.domain import javax.persistence.Version import scala.reflect.BeanProperty import java.util.Date trait 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 @Audited public 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.